{"id":20316111,"url":"https://github.com/lupyuen/nuttx-purescript-parser","last_synced_at":"2026-03-06T07:03:39.013Z","repository":{"id":223271872,"uuid":"759777924","full_name":"lupyuen/nuttx-purescript-parser","owner":"lupyuen","description":"Parsing Apache NuttX RTOS Logs with PureScript","archived":false,"fork":false,"pushed_at":"2024-03-03T01:22:18.000Z","size":171,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-04T09:13:38.534Z","etag":null,"topics":["bl808","nuttx","ox64","purescript","riscv","riscv64"],"latest_commit_sha":null,"homepage":"https://lupyuen.github.io/articles/purescript","language":"PureScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lupyuen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["lupyuen"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["paypal.me/lupyuen"]}},"created_at":"2024-02-19T10:13:03.000Z","updated_at":"2024-03-03T01:39:44.000Z","dependencies_parsed_at":"2024-03-02T02:24:55.443Z","dependency_job_id":"471a1712-b021-4b03-8150-08dd139a2e1d","html_url":"https://github.com/lupyuen/nuttx-purescript-parser","commit_stats":null,"previous_names":["lupyuen/nuttx-parser"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lupyuen/nuttx-purescript-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fnuttx-purescript-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fnuttx-purescript-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fnuttx-purescript-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fnuttx-purescript-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lupyuen","download_url":"https://codeload.github.com/lupyuen/nuttx-purescript-parser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fnuttx-purescript-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30164904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T04:43:31.446Z","status":"ssl_error","status_checked_at":"2026-03-06T04:40:30.133Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bl808","nuttx","ox64","purescript","riscv","riscv64"],"created_at":"2024-11-14T18:24:24.015Z","updated_at":"2026-03-06T07:03:38.913Z","avatar_url":"https://github.com/lupyuen.png","language":"PureScript","funding_links":["https://github.com/sponsors/lupyuen","paypal.me/lupyuen"],"categories":[],"sub_categories":[],"readme":"![Parsing Apache NuttX RTOS Logs with PureScript](https://lupyuen.github.io/images/purescript-parse2.png)\n\n[(Try the Online Demo)](https://lupyuen.github.io/nuttx-tinyemu/purescript)\n\n[(Watch the Demo on YouTube)](https://youtu.be/9oBhy3P7pYc)\n\n# Parsing Apache NuttX RTOS Logs with PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nIn the Web Browser, we can get Real-Time Logs from NuttX Devices (Web Serial API) NuttX Emulator (Term.js)...\n\nWhat if we could Analyse the NuttX Logs in Real-Time? And show the results in the Web Browser?\n\nLike for [Stack Dumps](https://gist.github.com/lupyuen/a715e4e77c011d610d0b418e97f8bf5d#file-nuttx-tcc-app-log-L168-L224), [ELF Loader Log](https://gist.github.com/lupyuen/a715e4e77c011d610d0b418e97f8bf5d#file-nuttx-tcc-app-log-L1-L167), [Memory Manager Log](https://docs.google.com/spreadsheets/d/1g0-O2qdgjwNfSIxfayNzpUN8mmMyWFmRf2dMyQ9a8JI/edit#gid=0) (malloc / free)?\n\nLet's do it with PureScript, since Functional Languages are better for Parsing Text.\n\nAnd we'll support Online Scripting of our PureScript for Log Parsing, similar to [try.purescript.org](https://try.purescript.org/)\n\n(Also automate the [Stack Dump Analysis](https://nuttx.apache.org/docs/latest/guides/cortexmhardfaults.html#))\n\n(Here's a [NuttX Emulator that crashes](https://lupyuen.github.io/nuttx-tinyemu/purescript/). Guess why?)\n\n_Why not code all this in JavaScript instead of PureScript?_\n\n(1) NuttX Logs might appear differently over time. Good to have a quick way to patch our parser as the NuttX Logs change.\n\n(2) We need to implement high-level Declarative Rules that will interpret the parsed NuttX Logs. We might adjust the Rules over time.\n\n(FYI Parsing CSV in JavaScript [looks like this](https://github.com/Chevrotain/chevrotain/blob/master/examples/grammars/csv/csv.js))\n\n_Why PureScript instead of Haskell?_\n\nRight now our NuttX Logs are accessible in a Web Browser through JavaScript: NuttX Emulator (over WebAssembly) and NuttX Device (over Web Serial API).\n\nPureScript is probably easier to run in a Web Browser for processing the JavaScript Logs.\n\n[(Zephyr Stack Dumps are also complicated)](https://github.com/zephyrproject-rtos/zephyr/issues/4416)\n\n# Parse NuttX Stack Dump with PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nLet's parse the [NuttX Stack Dump](https://gist.github.com/lupyuen/a715e4e77c011d610d0b418e97f8bf5d#file-nuttx-tcc-app-log-L168-L224)...\n\n```text\n[    6.242000] riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a\n[    6.242000] riscv_exception: PANIC!!! Exception = 000000000000000c\n[    6.242000] _assert: Current Version: NuttX  12.4.0 f8b0b06b978 Jan 29 2024 01:16:20 risc-v\n[    6.242000] _assert: Assertion failed panic: at file: common/riscv_exception.c:85 task: /system/bin/init process: /system/bin/init 0xc000001a\n[    6.242000] up_dump_register: EPC: 000000008000ad8a\n[    6.242000] up_dump_register: A0: 0000000000000000 A1: 00000000c0202010 A2: 0000000000000001 A3: 00000000c0202010\n[    6.242000] up_dump_register: A4: 00000000c0000000 A5: 0000000000000000 A6: 0000000000000000 A7: 0000000000000000\n[    6.242000] up_dump_register: T0: 0000000000000000 T1: 0000000000000000 T2: 0000000000000000 T3: 0000000000000000\n[    6.242000] up_dump_register: T4: 0000000000000000 T5: 0000000000000000 T6: 0000000000000000\n[    6.242000] up_dump_register: S0: 0000000000000000 S1: 0000000000000000 S2: 0000000000000000 S3: 0000000000000000\n[    6.242000] up_dump_register: S4: 0000000000000000 S5: 0000000000000000 S6: 0000000000000000 S7: 0000000000000000\n[    6.242000] up_dump_register: S8: 0000000000000000 S9: 0000000000000000 S10: 0000000000000000 S11: 0000000000000000\n[    6.242000] up_dump_register: SP: 00000000c0202800 FP: 0000000000000000 TP: 0000000000000000 RA: 000000008000ad8a\n[    6.242000] dump_stack: User Stack:\n[    6.242000] dump_stack:   base: 0xc0202040\n[    6.242000] dump_stack:   size: 00003008\n[    6.242000] dump_stack:     sp: 0xc0202800\n[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000\n[    6.242000] stack_dump: 0xc0202800: 00000000 00000000 0007e7f0 00000000 c0200208 00000000 c02001e8 00000000\n```\n\nLet's try this single line...\n\n```text\n[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000\n```\n\nHere's our parser in PureScript: [src/Main.purs](src/Main.purs)\n\n```purescript\n-- Parse the NuttX Stack Dump.\n-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.\nprintResults :: Effect Unit\nprintResults = do\n  doRunParser \"parseStackDump\" parseStackDump\n    \"[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000\"\n\n-- Parse a line of NuttX Stack Dump.\n-- Result: { addr: \"c02027e0\", timestamp: \"6.242000\", v1: \"c0202010\", v2: \"00000000\", v3: \"00000001\", v4: \"00000000\", v5: \"00000000\", v6: \"00000000\", v7: \"8000ad8a\", v8: \"00000000\" }\n-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.\nparseStackDump ∷ Parser { addr ∷ String , timestamp ∷ String , v1 ∷ String , v2 ∷ String , v3 ∷ String , v4 ∷ String , v5 ∷ String , v6 ∷ String , v7 ∷ String , v8 ∷ String }\nparseStackDump = do\n\n  -- To parse the line: `[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000`\n  -- Skip `[    `\n  -- `void` means ignore the Text Captured\n  -- `$ something something` is shortcut for `( something something )`\n  -- `\u003c*` is the Delimiter between Patterns\n  void $\n    string \"[\"    -- Match the string `[`\n    \u003c* skipSpaces -- Skip the following spaces\n\n  -- `timestamp` becomes `6.242000`\n  -- `\u003c*` says when we should stop the Text Capture\n  timestamp \u003c-\n    regex \"[.0-9]+\" \n    \u003c* string \"]\" \n    \u003c* skipSpaces\n\n  -- Skip `stack_dump: `\n  -- `void` means ignore the Text Captured\n  -- `$ something something` is shortcut for `( something something )`\n  -- `\u003c*` is the Delimiter between Patterns\n  void $ string \"stack_dump:\" \u003c* skipSpaces\n\n  -- `addr` becomes `c02027e0`\n  void $ string \"0x\"\n  addr \u003c- regex \"[0-9a-f]+\" \u003c* string \":\" \u003c* skipSpaces\n\n  -- `v1` becomes `c0202010`\n  -- `v2` becomes `00000000` and so on\n  v1 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v2 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v3 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v4 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v5 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v6 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v7 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n  v8 \u003c- regex \"[0-9a-f]+\" \u003c* skipSpaces\n\n  -- Return the parsed content\n  -- `pure` because we're in a `do` block that allows Effects\n  pure\n    { timestamp\n    , addr\n    , v1\n    , v2\n    , v3\n    , v4\n    , v5\n    , v6\n    , v7\n    , v8\n    }\n```\n\nResult of parsing...\n\n```bash\n## We're parsing: [    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000\n$ spago run\n[info] Build succeeded.\n(runParser) Parsing content with 'parseStackDump'\nResult: {\n  addr: \"c02027e0\", timestamp: \"6.242000\",\n  v1: \"c0202010\", v2: \"00000000\", v3: \"00000001\", v4: \"00000000\",\n  v5: \"00000000\", v6: \"00000000\", v7: \"8000ad8a\", v8: \"00000000\" }\n```\n\nNot that hard! We could have refactored the code to make it shorter. But we'll keep it long because it's easier to read.\n\nWorks OK in JavaScript too: [index.html](index.html)\n\n```javascript\n// Run parseStackDump\nconst stackDump = `[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000`;\nconst result = StringParser_Parser\n  .runParser\n  (parseStackDump)\n  (stackDump)\n  ;\nconsole.log({result});\n```\n\nShows...\n\n```json\n{\n    \"result\": {\n        \"value0\": {\n            \"timestamp\": \"6.242000\",\n            \"addr\": \"c02027e0\",\n            \"v1\": \"c0202010\",\n            \"v2\": \"00000000\",\n            \"v3\": \"00000001\",\n            \"v4\": \"00000000\",\n            \"v5\": \"00000000\",\n            \"v6\": \"00000000\",\n            \"v7\": \"8000ad8a\",\n            \"v8\": \"00000000\"\n        }\n    }\n}\n```\n\n_What if the parsing fails?_\n\nWe'll see `result.error`...\n\n```json\n{\n    \"result\": {\n        \"value0\": {\n            \"pos\": 0,\n            \"error\": \"Expected '['.\"\n        }\n    }\n}\n```\n\nSo we can run `parseStackDump` on every line of NuttX Log. And skip the lines with `result.error`\n\nTODO: Spot interesting addresses like 8000ad8a, c0202010\n\n# Parse NuttX Exception with PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nLet's parse the [NuttX Exception](https://gist.github.com/lupyuen/a715e4e77c011d610d0b418e97f8bf5d#file-nuttx-tcc-app-log-L168-L224)...\n\n```text\n[    6.242000] riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a\n```\n\nHere's how we parse the NuttX Exception in PureScript: [src/Main.purs](src/Main.purs)\n\n```purescript\n-- Parse the NuttX Exception.\n-- Given this NuttX Exception: `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`\n-- Result: { epc: \"000000008000ad8a\", exception: \"Instruction page fault\", mcause: 12, mtval: \"000000008000ad8a\" }\n-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.\nparseException ∷ Parser { exception ∷ String, mcause :: Int, epc :: String, mtval :: String }\nparseException = do\n\n  -- To parse the line: `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`\n  -- Skip `riscv_exception: EXCEPTION: `\n  -- `void` means ignore the Text Captured\n  -- `$ something something` is shortcut for `( something something )`\n  -- `\u003c*` is the Delimiter between Patterns\n  void $\n    string \"riscv_exception:\" -- Match the string `riscv_exception:`\n    \u003c* skipSpaces             -- Skip the following spaces\n    \u003c* string \"EXCEPTION:\"    -- Match the string `EXCEPTION:`\n    \u003c* skipSpaces             -- Skip the following spaces\n\n  -- `exception` becomes `Instruction page fault`\n  -- `\u003c*` says when we should stop the Text Capture\n  exception \u003c- regex \"[^.]+\" \n    \u003c* string \".\" \n    \u003c* skipSpaces \n\n  -- Skip `MCAUSE: `\n  -- `void` means ignore the Text Captured\n  -- `$ something something` is shortcut for `( something something )`\n  -- `\u003c*` is the Delimiter between Patterns\n  void $ string \"MCAUSE:\" \u003c* skipSpaces\n\n  -- `mcauseStr` becomes `000000000000000c`\n  -- We'll convert to integer later\n  mcauseStr \u003c- regex \"[0-9a-f]+\" \u003c* string \",\" \u003c* skipSpaces\n\n  -- Skip `EPC: `\n  -- `epc` becomes `000000008000ad8a`\n  void $ string \"EPC:\" \u003c* skipSpaces\n  epc \u003c- regex \"[0-9a-f]+\" \u003c* string \",\" \u003c* skipSpaces\n\n  -- Skip `MTVAL: `\n  -- `mtval` becomes `000000008000ad8a`\n  void $ string \"MTVAL:\" \u003c* skipSpaces\n  mtval \u003c- regex \"[0-9a-f]+\"\n\n  -- Return the parsed content\n  -- `pure` because we're in a `do` block that allows (Side) Effects\n  pure \n    {\n      exception\n    , mcause: -1 `fromMaybe`               -- If `mcauseStr` is not a valid hex, return -1\n        fromStringAs hexadecimal mcauseStr -- Else return the hex value of `mcauseStr`\n    , epc\n    , mtval\n    }\n```\n\nWe can run it in Web Browser JavaScript: [index.html](index.html)\n\n```javascript\n  // Run parseException\n  const exception = `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`\n  const result1 = StringParser_Parser\n    .runParser\n    (parseException)\n    (exception)\n    ;\n  console.log({result1});\n```\n\nWhich shows...\n\n```json\n{\n    \"result1\": {\n        \"value0\": {\n            \"exception\": \"Instruction page fault\",\n            \"mcause\": 12,\n            \"epc\": \"000000008000ad8a\",\n            \"mtval\": \"000000008000ad8a\"\n        }\n    }\n}\n```\n\n# Explain NuttX Exception with PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nWe explain in friendly words what the NuttX Exception means...\n\n\"NuttX crashed because it tried to read or write an Invalid Address. The Invalid Address is 8000ad8a. The code that caused this is at 8000ad8a. Check the NuttX Disassembly for the Source Code of the crashing line.\"\n\nHere's how we explain the NuttX Exception in PureScript: [src/Main.purs](src/Main.purs)\n\n```purescript\n-- Given this NuttX Exception: `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`\n-- Explain in friendly words: \"NuttX stopped because it tried to read or write an Invalid Address. The Invalid Address is 8000ad8a. The code that caused this is at 8000ad8a. Check the NuttX Disassembly for the Source Code of the crashing line.\"\n-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.\nexplainException ∷ Int → String → String → String\n\n-- Explain the NuttX Exception with mcause 12\nexplainException 12 epc mtval =\n  \"Instruction Page Fault at \" \u003c\u003e epc \u003c\u003e \", \" \u003c\u003e mtval\n\n-- Explain the Other NuttX Exceptions, that are not matched with the above\nexplainException mcause epc mtval =\n  \"Unknown Exception: mcause=\" \u003c\u003e show mcause \u003c\u003e \", epc=\" \u003c\u003e epc \u003c\u003e \", mtval=\" \u003c\u003e mtval\n```\n\nWe can run it in Web Browser JavaScript: [index.html](index.html)\n\n```javascript\n  // Run explainException\n  const result2 = explainException(12)('000000008000ad8a')('000000008000ad8a')\n  console.log({result2});\n```\n\nWhich shows...\n\n```json\n{\n    \"result2\": \"Instruction Page Fault at 000000008000ad8a, 000000008000ad8a\"\n}\n```\n\n# Rewrite JavaScript generated by PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nIn the JavaScript generated by PureScript, we point the PureScript Imports to compile.purescript.org, so we don't need to deploy the imports: [run.sh](run.sh)\n\n```bash\n## Change:\n##   import { main, doBoth, doRunParser, parseCSV, exampleContent2, parseException, parseStackDump, explainException } from './output/Main/index.js';\n## To:\n##   import { main, doBoth, doRunParser, parseCSV, exampleContent2, parseException, parseStackDump, explainException } from './index.js';\n## Change:\n##   import * as StringParser_Parser from \"./output/StringParser.Parser/index.js\";\n## To:\n##   import * as StringParser_Parser from \"https://compile.purescript.org/output/StringParser.Parser/index.js\";\ncat index.html \\\n  | sed 's/output\\/Main\\///' \\\n  | sed 's/.\\/output\\//https:\\/\\/compile.purescript.org\\/output\\//' \\\n  \u003edocs/index.html\n\n## Change:\n##   import * as Control_Alt from \"../Control.Alt/index.js\";\n## To:\n##   import * as Control_Alt from \"https://compile.purescript.org/output/Control.Alt/index.js\";\ncat output/Main/index.js \\\n  | sed 's/from \\\"../from \\\"https:\\/\\/compile.purescript.org\\/output/' \\\n  \u003edocs/index.js\n```\n\nTry it here: https://lupyuen.github.io/nuttx-purescript-parser/\n\n# Call PureScript from NuttX Emulator\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nAfter rewriting the JavaScript Imports, we may now call PureScript from NuttX Emulator: [index.html](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/index.html#L31-L98)\n\n```html\n\u003cscript type=module\u003e\n  // Import Main Module\n  import { main, doBoth, doRunParser, parseCSV, exampleContent2, parseException, parseStackDump, explainException } from 'https://lupyuen.github.io/nuttx-purescript-parser/index.js';\n  import * as StringParser_Parser from \"https://compile.purescript.org/output/StringParser.Parser/index.js\";\n\n  // Run parseException\n  console.log('Running parseException...');\n  const exception = `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`\n  const result1 = StringParser_Parser\n    .runParser\n    (parseException)\n    (exception)\n    ;\n  console.log({result1});\n\n  // Run explainException\n  const result2 = explainException(12)('000000008000ad8a')('000000008000ad8a')\n  console.log({result2});\n\n  // Run parseStackDump\n  console.log('Running parseStackDump...');\n  const stackDump = `[    6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000`;\n  const result3 = StringParser_Parser\n    .runParser\n    (parseStackDump)\n    (stackDump)\n    ;\n  console.log({result3});\n\u003c/script\u003e\n```\n\nTry it here: https://lupyuen.github.io/nuttx-tinyemu/purescript\n\n# Parse NuttX Logs in NuttX Emulator\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\nThis is how we parse every line of Terminal Output from NuttX Emulator: [term.js](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/term.js#L1483-L1527)\n\n```javascript\n// When TinyEMU prints to the Terminal Output...\nTerm.prototype.write = function(str) {\n    // Parse the output with PureScript\n    parseLog(str);\n    ...\n}\n\n// Parse NuttX Logs with PureScript.\n// Assume `str` is a single character for Terminal Output. We accumulate the characters and parse the line.\n// PureScript Parser is inited in `index.html`\nfunction parseLog(str) {\n\n    // Accumulate the characters into a line\n    if (!window.StringParser_Parser) { return; }\n    termbuf += str;\n    if (termbuf.indexOf(\"\\r\") \u003c 0) { return; }\n\n    // Ignore all Newlines and Carriage Returns\n    termbuf = termbuf\n        .split(\"\\r\").join(\"\")\n        .split(\"\\n\").join(\"\");\n    // console.log({termbuf});\n\n    // Parse the Exception\n    const exception = StringParser_Parser\n        .runParser(parseException)(termbuf)\n        .value0;\n\n    // Explain the Exception\n    if (exception.error === undefined) {\n        console.log({exception});\n        const explain = explainException(exception.mcause)(exception.epc)(exception.mtval);\n        console.log({explain});\n    }\n\n    // Run parseStackDump\n    const stackDump = StringParser_Parser\n        .runParser(parseStackDump)(termbuf)\n        .value0;\n    if (stackDump.error === undefined) { console.log({stackDump}); }\n\n    // Reset the Line Buffer\n    termbuf = \"\";\n}\n\n// Buffer the last line of the Terminal Output\nlet termbuf = \"\";\n```\n\nAnd it works correctly yay!\n\n```text\n\"exception\": {\n    \"exception\": \"Load page fault\",\n    \"mcause\": 13,\n    \"epc\": \"000000008000a0e4\",\n    \"mtval\": \"0000000880203b88\"\n}\n\"explain\": \"Load Page Fault at 000000008000a0e4, 0000000880203b88\"\n```\n\nTry it here: https://lupyuen.github.io/nuttx-tinyemu/purescript\n\n[(Watch the Demo on YouTube)](https://youtu.be/9oBhy3P7pYc)\n\n![Parsing Apache NuttX RTOS Logs with PureScript](https://lupyuen.github.io/images/purescript-parse2.png)\n\n# Show NuttX Disassembly by Address\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\n_Given an Exception Address like 8000ad8a, can we show the NuttX Disassembly?_\n\nWe need to chunk nuttx.S (or qjs.S) by address: nuttx-8000ad90.S, nuttx-8000ae00.S, nuttx-8000b000.S, nuttx-80010000.S. And link to the NuttX Repo Source Code.\n\nLet's chunk [qjs.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs.S), the NuttX App Disassembly for QuickJS JavaScript Engine...\n\n- Code Addresses are at 0x8000_0000 to 0x8006_4a28\n\n- Spanning 277K lines of code!\n\nWe created a [NuttX Disassembly Chunker](https://github.com/lupyuen/nuttx-disassembly-chunker/blob/main/src/main.rs) that will...\n\n- Split a huge NuttX Disassembly: [qjs.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs.S)\n\n- Into smaller Disassembly Chunk Files: [qjs-chunk/qjs-80001000.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs-chunk/qjs-80001000.S)\n\n- So that Disassembly Address 0x8000_0000 will be located in [qjs-80001000.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs-chunk/qjs-80001000.S)\n\n- And Disassembly Address 0x8000_1000 will be located in [qjs-80002000.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs-chunk/qjs-80002000.S), ...\n\nThis is how we chunk a NuttX Disassembly from [qjs.S](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs.S) to [qjs-chunk](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/qjs-chunk)...\n\n```bash\n## Chunk NuttX Disassembly $HOME/qjs.S into\n## $HOME/qjs-chunk/qjs-80001000.S\n## $HOME/qjs-chunk/qjs-80002000.S\n## ...\n\nchunkpath=$HOME\nchunkbase=qjs\nmkdir -p $chunkpath/$chunkbase-chunk\nrm -f $chunkpath/$chunkbase-chunk/*\ncargo run -- $chunkpath $chunkbase\n```\n\nAnd this is how we display the Disassembly Chunk by Address: [disassemble.js](https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/purescript/disassemble.js)\n\n```javascript\n// Show the NuttX Disassembly for the Requested Address\n// http://localhost:8000/nuttx-tinyemu/docs/purescript/disassemble.html?addr=80007028\n\n// Show 20 lines before and after the Requested Address\nconst before_count = 20;\nconst after_count  = 20;\n\n// Convert `nuttx/arch/risc-v/src/common/crt0.c:166`\n// To `\u003ca href=\"https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/crt0.c#L166\"\u003e...`\n// Convert `quickjs-nuttx/quickjs-libc.c:1954`\n// To `\u003ca href=\"https://github.com/lupyuen/quickjs-nuttx/blob/master/quickjs-libc.c#L1954\"\u003e...`\nconst search1  = \"nuttx/\";\nconst replace1 = \"https://github.com/apache/nuttx/blob/master/\";\nconst search2  = \"quickjs-nuttx/\";\nconst replace2 = \"https://github.com/lupyuen/quickjs-nuttx/blob/master/\";\n\n// Convert the Source File to Source URL\nfunction processLine(line) {\n  line = line.split(\"\\t\").join(\"\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\");\n  if (line.indexOf(\":\") \u003c 0) { return line; }\n  let url = line.split(\":\", 2).join(\"#L\");\n\n  // Search and replace Source File to Source URL\n  if (line.indexOf(search1) == 0) {\n    url = url.split(search1, 2).join(replace1);\n  } else if (line.indexOf(search2) == 0) {\n    url = url.split(search2, 2).join(replace2);\n  } else {\n    return line;\n  }\n  return `\u003ca href=\"${url}\" target=\"_blank\"\u003e${line}\u003c/a\u003e`;\n}\n\n// Fetch our Disassembly File, line by line\n// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#processing_a_text_file_line_by_line\nasync function* makeTextFileLineIterator(fileURL) {\n  const utf8Decoder = new TextDecoder(\"utf-8\");\n  const response = await fetch(fileURL);\n  const reader = response.body.getReader();\n  let { value: chunk, done: readerDone } = await reader.read();\n  chunk = chunk ? utf8Decoder.decode(chunk) : \"\";\n\n  const newline = /\\r?\\n/gm;\n  let startIndex = 0;\n  let result;\n\n  while (true) {\n    const result = newline.exec(chunk);\n    if (!result) {\n      if (readerDone) break;\n      const remainder = chunk.substr(startIndex);\n      ({ value: chunk, done: readerDone } = await reader.read());\n      chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : \"\");\n      startIndex = newline.lastIndex = 0;\n      continue;\n    }\n    yield chunk.substring(startIndex, result.index);\n    startIndex = newline.lastIndex;\n  }\n\n  if (startIndex \u003c chunk.length) {\n    // Last line didn't end in a newline char\n    yield chunk.substr(startIndex);\n  }\n}\n\n// Fetch and display our Disassembly File, line by line\nasync function run() {\n\n  // Set the Title. `addr` is `80007028`\n  const addr = new URL(document.URL).searchParams.get(\"addr\");\n  const title = document.getElementById(\"title\");\n  title.innerHTML += \n    addr.substring(0, 4).toUpperCase()\n    + \"_\"\n    + addr.substring(4).toUpperCase();\n\n  // URL of our Disassembly File, chunked for easier display.\n  // TODO: Given an Exception Address like 8000ad8a. we should try multiple files by address:\n  // qjs-8000ad90.S, qjs-8000ae00.S, qjs-8000b000.S, qjs-80010000.S\n  const url = \"qjs-chunk/qjs-80008000.S\";\n\n  // Remember the lines before and after the Requested Address\n  const before_lines = [];\n  const after_lines = [];\n  let linenum = 0;\n\n  // Process our Disassembly File, line by line\n  const iter = makeTextFileLineIterator(url);\n  for await (const line1 of iter) {\n    if (after_lines.length == 0) { linenum++; }\n\n    // Look for the Requested Address\n    if (line1.indexOf(`    ${addr}:`) == 0) {\n      const line2 = processLine(line1);\n      after_lines.push(line2);\n      continue;\n    }\n\n    // Save the lines before the Requested Address\n    const line2 = processLine(line1);\n    if (after_lines.length == 0) {\n      before_lines.push(line2);\n      if (before_lines.length \u003e before_count) { before_lines.shift(); }  \n    } else {\n      // Save the lines after the Requested Address\n      after_lines.push(line2);\n      if (after_lines.length \u003e after_count) { break; }\n    }\n  }\n\n  // Requested Line is `after_lines[0]`.\n  // Show the Before and After Lines.\n  const line = after_lines[0];\n  after_lines.shift();\n  console.log({before_lines});\n  console.log({line});\n  console.log({after_lines});\n\n  const disassembly = document.getElementById(\"disassembly\");\n  const file = `https://github.com/lupyuen/nuttx-tinyemu/tree/main/docs/purescript/${url}#L${linenum}`;\n  disassembly.innerHTML = [\n    `\u003cp\u003e\u003ca href=${file}\u003e(See the Disassembly File)\u003c/a\u003e\u003c/p\u003e`,\n    before_lines.join(\"\u003cbr\u003e\"),\n    `\u003cspan id=\"highlight\"\u003e\u003cbr\u003e${line}\u003cbr\u003e\u003c/span\u003e`,\n    after_lines.join(\"\u003cbr\u003e\"),\n    `\u003cp\u003e\u003ca href=${file}\u003e(See the Disassembly File)\u003c/a\u003e\u003c/p\u003e`,\n  ].join(\"\u003cbr\u003e\");\n}\n\nrun();\n```\n\nTry it here: https://lupyuen.github.io/nuttx-tinyemu/purescript/disassemble.html?addr=8000702a\n\n![NuttX Disassembly](https://lupyuen.github.io/images/purescript-disassembly.png)\n\n# Identify a NuttX Address\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\n_Given a NuttX Address like 80007028: How will we know whether it's in NuttX Kernel or NuttX Apps? And whether it's Code, Data, BSS or Heap?_\n\nThis is how we identify a NuttX Address in PureScript: [src/Main.purs](src/Main.purs)\n\n```purescript\n-- Given an Address, identify the Origin (NuttX Kernel or App) and Type (Code / Data / BSS / Heap)\nidentifyAddress ∷ String → Maybe { origin ∷ String , type ∷ AddressType }\n\n-- Address 502xxxxx comes from NuttX Kernel Code\n-- Address 800xxxxx comes from NuttX App Code (QuickJS)\n-- `|` works like `if ... else if`\n-- \"a `matches` b\" is same as \"(matches a b)\"\n-- `Just` returns an OK Value. `Nothing` returns No Value.\nidentifyAddress addr\n  | \"502.....\" `matches` addr = Just { origin: \"nuttx\", type: Code }\n  | \"800.....\" `matches` addr = Just { origin: \"qjs\",   type: Code }\n  | otherwise = Nothing\n\n-- Address can point to Code, Data, BSS or Heap\ndata AddressType = Code | Data | BSS | Heap\n\n-- How to display an Address Type\ninstance Show AddressType where\n  show Code = \"Code\"\n  show Data = \"Data\"\n  show BSS  = \"BSS\"\n  show Heap = \"Heap\"\n\n-- Return True if the Address matches the Regex Pattern.\n-- Pattern is assumed to match the Entire Address.\nmatches ∷ String → String → Boolean\n\n-- Match the Begin `^` and End `$` of the Address\n-- `\u003c\u003e` will concat 2 strings\n-- \"a `unsafeRegex` b\" is same as \"(unsafeRegex a b)\"\nmatches pattern addr = \n  let \n    patternWrap = \"^\" \u003c\u003e pattern \u003c\u003e \"$\"\n  in\n    isJust $                            -- Is there a Match...\n      patternWrap `unsafeRegex` noFlags -- For our Regex Pattern (no special flags)\n        `match` addr                    -- Against the Address?\n\n-- Test our code. Parse the NuttX Exception and NuttX Stack Dump. Explain the NuttX Exception.\n-- `Effect` says that it will do Side Effects (printing to console)\n-- `Unit` means that no value will be returned\n-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.\nprintResults :: Effect Unit\nprintResults = do\n\n  -- NuttX Kernel: 0x5020_0000 to 0x5021_98ac\n  -- NuttX App (qjs): 0x8000_0000 to 0x8006_4a28\n  logShow $ identifyAddress \"502198ac\" -- (Just { origin: \"nuttx\", type: Code })\n  logShow $ identifyAddress \"8000a0e4\" -- (Just { origin: \"qjs\", type: Code })\n  logShow $ identifyAddress \"0000000800203b88\" -- Nothing\n```\n\n_Tsk tsk so much Hard Coding..._\n\nOur Rules are still evolving, we're not sure how the NuttX Log Parser will be used in future.\n\nThat's why we need a PureScript Editor that will allow the Rules to be tweaked easily for other platforms...\n\n# PureScript Editor for NuttX\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\n_How to build a PureScript Editor that will allow the Rules to be tweaked easily for other platforms?_\n\nTo run our PureScript Editor for NuttX...\n\n```bash\ngit clone https://github.com/lupyuen/nuttx-trypurescript\ncd nuttx-trypurescript\ncd client\n\n## Build and Test Locally:\nnpm install\n## Produces `output` folder\n## And `public/js/index.js`\nnpm run serve:production\n## Test at http://127.0.0.1:8080\n\n## Deploy to GitHub Pages:\nrm -r ../docs\ncp -r public ../docs\nsimple-http-server .. \u0026\n## Test at http://0.0.0.0:8000/docs/index.html\n\n## If we need `client.js` bundle:\n## npm run build:production\n```\n\nTry it here: https://lupyuen.github.io/nuttx-trypurescript\n\nCopy [src/Main.purs](src/Main.purs) to the PureScript Editor.\n\n```purescript\nmain :: Effect Unit\nmain = printResults\n```\n\nTo this...\n\n```purescript\nimport TryPureScript (render, withConsole)\n\nmain :: Effect Unit\nmain = render =\u003c\u003c withConsole do\n  printResults\n```\n\nOur NuttX Parser Output appears...\n\n```text\nInstruction Page Fault at epc, mtval\nUnknown Exception: mcause=0, epc=epc, mtval=mtval\n(runParser) Parsing content with 'parseException'\nResult: { epc: \"000000008000ad8a\", exception: \"Instruction page fault\", mcause: 12, mtval: \"000000008000ad8a\" }\n-----\n(runParser) Parsing content with 'parseStackDump'\nResult: { addr: \"c02027e0\", timestamp: \"6.242000\", v1: \"c0202010\", v2: \"00000000\", v3: \"00000001\", v4: \"00000000\", v5: \"00000000\", v6: \"00000000\", v7: \"8000ad8a\", v8: \"00000000\" }\n-----\n```\n\nThe Generated Web Browser JavaScript looks like this...\n\n```html\n\u003cscript type=\"module\"\u003e\nimport * as Control_Alt from \"https://compile.purescript.org/output/Control.Alt/index.js\";\nimport * as Control_Applicative from \"https://compile.purescript.org/output/Control.Applicative/index.js\";\nimport * as Control_Apply from \"https://compile.purescript.org/output/Control.Apply/index.js\";\n...\nvar bind = /* #__PURE__ */ Control_Bind.bind(StringParser_Parser.bindParser);\nvar alt = /* #__PURE__ */ Control_Alt.alt(StringParser_Parser.altParser);\nvar voidRight = /* #__PURE__ */ Data_Functor.voidRight(StringParser_Parser.functorParser);\n...\n```\n\nTODO: Where is Main Function?\n\nTODO: Copy Generated JavaScript to NuttX Emulator\n\n# BigInt in PureScript\n\nRead the article...\n\n-   [\"Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)\"](https://lupyuen.github.io/articles/purescript)\n\n_Why are we passing addresses in Text instead of Numbers? Like `8000ad8a`_\n\nThat's because 0x8000ad8a is too big for PureScript Int, a signed 32-bit integer. PureScript Int is meant to interoperate with JavaScript Integer, which is also 32-bit.\n\n_What about PureScript BigInt?_\n\n```bash\nspago install bigints\nnpm install big-integer\n```\n\nIf we use [PureScript BigInt](https://pursuit.purescript.org/packages/purescript-bigints/7.0.1/docs/Data.BigInt#t:BigInt), then we need NPM big-integer.\n\nBut NPM big-integer won't run inside a Web Browser with Plain Old JavaScript. That's why we're passing addresses as Strings instead of Numbers.\n\n# Run parseCSV in Node.js\n\nLet's run [parseCSV](https://github.com/purescript-contrib/purescript-string-parsers/blob/main/test/Examples.purs) in Node.js. Normally we run PureScript like this...\n\n```bash\nspago run\n```\n\nThis is how we run it in Node.js...\n\n```bash\n$ spago build\n$ node .spago/run.js\n\n### Example Content 1 ###\n(runParser) Parsing content with 'fail'\n{ error: \"example failure message\", pos: 0 }\n-----\n(unParser) Parsing content with 'fail'\nPosition: 0\nError: \"example failure message\"\n-----\n(runParser) Parsing content with 'numberOfAs'\nResult was: 6\n-----\n(unParser) Parsing content with 'numberOfAs'\nResult was: 6\nSuffix was: { position: 59, substring: \"\" }\n-----\n(runParser) Parsing content with 'removePunctuation'\nResult was: \"How many as are in this sentence you ask Not that many\"\n-----\n(unParser) Parsing content with 'removePunctuation'\nResult was: \"How many as are in this sentence you ask Not that many\"\nSuffix was: { position: 59, substring: \"\" }\n-----\n(runParser) Parsing content with 'replaceVowelsWithUnderscore'\nResult was: \"H_w m_ny '_'s _r_ _n th_s s_nt_nc_, y__ _sk? N_t th_t m_ny.\"\n-----\n(unParser) Parsing content with 'replaceVowelsWithUnderscore'\nResult was: \"H_w m_ny '_'s _r_ _n th_s s_nt_nc_, y__ _sk? N_t th_t m_ny.\"\nSuffix was: { position: 59, substring: \"\" }\n-----\n(runParser) Parsing content with 'tokenizeContentBySpaceChars'\nResult was: (NonEmptyList (NonEmpty \"How\" (\"many\" : \"'a's\" : \"are\" : \"in\" : \"this\" : \"sentence,\" : \"you\" : \"ask?\" : \"Not\" : \"that\" : \"many.\" : Nil)))\n-----\n(unParser) Parsing content with 'tokenizeContentBySpaceChars'\nResult was: (NonEmptyList (NonEmpty \"How\" (\"many\" : \"'a's\" : \"are\" : \"in\" : \"this\" : \"sentence,\" : \"you\" : \"ask?\" : \"Not\" : \"that\" : \"many.\" : Nil)))\nSuffix was: { position: 59, substring: \"\" }\n-----\n(runParser) Parsing content with 'extractWords'\nResult was: (NonEmptyList (NonEmpty \"How\" (\"many\" : \"a\" : \"s\" : \"are\" : \"in\" : \"this\" : \"sentence\" : \"you\" : \"ask\" : \"Not\" : \"that\" : \"many\" : Nil)))\n-----\n(unParser) Parsing content with 'extractWords'\nResult was: (NonEmptyList (NonEmpty \"How\" (\"many\" : \"a\" : \"s\" : \"are\" : \"in\" : \"this\" : \"sentence\" : \"you\" : \"ask\" : \"Not\" : \"that\" : \"many\" : Nil)))\nSuffix was: { position: 59, substring: \"\" }\n-----\n(runParser) Parsing content with 'badExtractWords'\n{ error: \"Could not find a character that separated the content...\", pos: 43 }\n-----\n(unParser) Parsing content with 'badExtractWords'\nPosition: 43\nError: \"Could not find a character that separated the content...\"\n-----\n(runParser) Parsing content with 'quotedLetterExists'\nResult was: true\n-----\n(unParser) Parsing content with 'quotedLetterExists'\nResult was: true\nSuffix was: { position: 59, substring: \"\" }\n-----\n\n### Example Content 2 ###\n(runParser) Parsing content with 'parseCSV'\nResult was: { age: \"24\", firstName: \"Mark\", idNumber: \"523\", lastName: \"Kenderson\", modifiedEmail: \"mynameismark@mark.mark.com\", originalEmail: \"my.name.is.mark@mark.mark.com\" }\n-----\n(unParser) Parsing content with 'parseCSV'\nResult was: { age: \"24\", firstName: \"Mark\", idNumber: \"523\", lastName: \"Kenderson\", modifiedEmail: \"mynameismark@mark.mark.com\", originalEmail: \"my.name.is.mark@mark.mark.com\" }\nSuffix was: { position: 110, substring: \"\" }\n-----\n```\n\n# Run parseCSV in Web Browser\n\nHere's how we run [parseCSV](https://github.com/purescript-contrib/purescript-string-parsers/blob/main/test/Examples.purs) in the Web Browser: [index.html](index.html)\n\n```javascript\n  // Import Main Module\n  import { main, doBoth, doRunParser, parseCSV, exampleContent2 } from './output/Main/index.js';\n  import * as StringParser_Parser from \"./output/StringParser.Parser/index.js\";\n\n  // Run parseCSV\n  const result = StringParser_Parser\n    .runParser\n    (parseCSV)\n    (exampleContent2)\n    ;\n  console.log({result});\n```\n\nOutput:\n\n```json\n{\n    \"result\": {\n        \"value0\": {\n            \"idNumber\": \"523\",\n            \"firstName\": \"Mark\",\n            \"lastName\": \"Kenderson\",\n            \"age\": \"24\",\n            \"originalEmail\": \"my.name.is.mark@mark.mark.com\",\n            \"modifiedEmail\": \"mynameismark@mark.mark.com\"\n        }\n    }\n}\n```\n\nWe expose the PureScript Functions in the Web Browser: [index.html](index.html)\n\n```javascript\n// Import Main Module\nimport { main, doBoth, doRunParser, parseCSV, exampleContent2 } from './output/Main/index.js';\nimport * as StringParser_Parser from \"./output/StringParser.Parser/index.js\";\n\n// For Testing: Export the PureScript Functions\nwindow.main = main;\nwindow.doBoth = doBoth;\nwindow.doRunParser = doRunParser;\nwindow.parseCSV = parseCSV;\nwindow.exampleContent2 = exampleContent2;\nwindow.StringParser_Parser = StringParser_Parser;\n```\n\nSo we can run experiments in the JavaScript Console...\n\n```javascript\n// Run parseCSV in JavaScript Console\nwindow.StringParser_Parser\n  .runParser\n  (window.parseCSV)\n  (window.exampleContent2)\n```\n\n# Run parseCSV in try.purescript.org\n\nTo run [parseCSV](https://github.com/purescript-contrib/purescript-string-parsers/blob/main/test/Examples.purs) at [try.purescript.org](https://try.purescript.org/), change...\n\n```purescript\nmain :: Effect Unit\nmain = printResults\n```\n\nTo this...\n\n```purescript\nimport TryPureScript (render, withConsole)\n\nmain :: Effect Unit\nmain = render =\u003c\u003c withConsole do\n  printResults\n```\n\n# Compile PureScript to JavaScript in Web Browser\n\nHere's how we compile PureScript to JavaScript inside our Web Browser...\n\nhttps://github.com/lupyuen/nuttx-tinyemu/blob/fc22c9fba2d6fbc4faf8c1fb02f4761952cb66cd/docs/blockly/jslinux.js#L755-L804\n\n```javascript\n// Compile PureScript to JavaScript\n// Maybe we'll run a PureScript to analyse the Real-Time Logs from a NuttX Device?\n// https://lupyuen.github.io/nuttx-tinyemu/blockly/\nasync function compile_purescript() {\n\n    // Public Server API that compiles PureScript to JavaScript\n    // https://github.com/purescript/trypurescript#server-api\n    const url = \"https://compile.purescript.org/compile\";\n    const contentType = \"text/plain;charset=UTF-8\";\n\n    // PureScript to be compiled to JavaScript\n    const body =\n`\nmodule Main where\n\nimport Prelude\n\nimport Effect (Effect)\nimport Effect.Console (log)\nimport Data.Array ((..))\nimport Data.Foldable (for_)\nimport TryPureScript (render, withConsole)\n\nmain :: Effect Unit\nmain = render =\u003c\u003c withConsole do\n  for_ (10 .. 1) \\\\n -\u003e log (show n \u003c\u003e \"...\")\n  log \"Lift off!\"\n`;\n\n    // Call Public Server API to compile our PureScript to JavaScript\n    // Default options are marked with *\n    // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\n    const response = await fetch(url, {\n        method: \"POST\", // *GET, POST, PUT, DELETE, etc.\n        mode: \"cors\", // no-cors, *cors, same-origin\n        cache: \"no-cache\", // *default, no-cache, reload, force-cache, only-if-cached\n        credentials: \"same-origin\", // include, *same-origin, omit\n        headers: { \"Content-Type\": contentType },\n        redirect: \"follow\", // manual, *follow, error\n        referrerPolicy: \"no-referrer\", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url\n        body: body,\n    });\n\n    // Print the response\n    // { \"js\": \"import * as Control_Bind from \\\"../Control.Bind/index.js\\\";\\nimport * as Data_Array from \\\"../Data.Array/index.js\\\";\\nimport * as Data_Foldable from \\\"../Data.Foldable/index.js\\\";\\nimport * as Data_Show from \\\"../Data.Show/index.js\\\";\\nimport * as Effect from \\\"../Effect/index.js\\\";\\nimport * as Effect_Console from \\\"../Effect.Console/index.js\\\";\\nimport * as TryPureScript from \\\"../TryPureScript/index.js\\\";\\nvar show = /* #__PURE__ */ Data_Show.show(Data_Show.showInt);\\nvar main = /* #__PURE__ */ Control_Bind.bindFlipped(Effect.bindEffect)(TryPureScript.render)(/* #__PURE__ */ TryPureScript.withConsole(function __do() {\\n    Data_Foldable.for_(Effect.applicativeEffect)(Data_Foldable.foldableArray)(Data_Array.range(10)(1))(function (n) {\\n        return Effect_Console.log(show(n) + \\\"...\\\");\\n    })();\\n    return Effect_Console.log(\\\"Lift off!\\\")();\\n}));\\nexport {\\n    main\\n};\",\n    //   \"warnings\": [] }\n    console.log(await response.json());\n}\n```\n\nThe JSON Response looks like this...\n\n```text\n{\n  \"js\": \"\n    import * as Control_Bind from \"../Control.Bind/index.js\";\n    import * as Data_Array from \"../Data.Array/index.js\";\n    import * as Data_Foldable from \"../Data.Foldable/index.js\";\n    import * as Data_Show from \"../Data.Show/index.js\";\n    import * as Effect from \"../Effect/index.js\";\n    import * as Effect_Console from \"../Effect.Console/index.js\";\n    import * as TryPureScript from \"../TryPureScript/index.js\";\n    var show = /* #__PURE__ */ Data_Show.show(Data_Show.showInt);\n    var main = /* #__PURE__ */ Control_Bind.bindFlipped(Effect.bindEffect)(TryPureScript.render)(/* #__PURE__ */ TryPureScript.withConsole(function __do() {\n          Data_Foldable.for_(Effect.applicativeEffect)(Data_Foldable.foldableArray)(Data_Array.range(10)(1))(function (n) {\n              return Effect_Console.log(show(n) + \"...\");\n        })();\n        return Effect_Console.log(\"Lift off!\")();\n    }));\n    export {\n          main\n    };\n  \",\n  \"warnings\": []\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fnuttx-purescript-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flupyuen%2Fnuttx-purescript-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fnuttx-purescript-parser/lists"}