{"id":13494724,"url":"https://github.com/DanielXMoore/Hera","last_synced_at":"2025-03-28T14:31:40.491Z","repository":{"id":38843235,"uuid":"403119763","full_name":"DanielXMoore/Hera","owner":"DanielXMoore","description":"Elegant parsing expression grammars","archived":false,"fork":false,"pushed_at":"2024-05-22T14:55:01.000Z","size":738,"stargazers_count":47,"open_issues_count":6,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-05-22T21:26:46.649Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DanielXMoore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2021-09-04T17:41:28.000Z","updated_at":"2024-06-19T11:28:17.181Z","dependencies_parsed_at":"2023-02-09T23:31:06.021Z","dependency_job_id":"985a3f35-e48a-46ba-84a8-c7282e4ccbe2","html_url":"https://github.com/DanielXMoore/Hera","commit_stats":{"total_commits":178,"total_committers":3,"mean_commits":"59.333333333333336","dds":"0.061797752808988804","last_synced_commit":"fc491bedf76bb9b514f57832646c9a416586a791"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielXMoore%2FHera","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielXMoore%2FHera/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielXMoore%2FHera/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielXMoore%2FHera/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DanielXMoore","download_url":"https://codeload.github.com/DanielXMoore/Hera/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245284713,"owners_count":20590307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T19:01:27.558Z","updated_at":"2025-03-28T14:31:40.485Z","avatar_url":"https://github.com/DanielXMoore.png","language":"TypeScript","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"Hera\n===\n\n\u003e I sing of golden-throned Hera whom Rhea bare. Queen of the Immortals is she, surpassing all in beauty: she is the sister and wife of loud-thundering Zeus,--the glorious one whom all the blessed throughout high Olympos reverence and honour even as Zeus who delights in thunder.\n\u003e\n\u003e — Homeric Hymn 12 to Hera (trans. Evelyn-White) (Greek epic C7th to 4th B.C.)\n\n[![Build](https://github.com/DanielXMoore/hera/actions/workflows/build.yml/badge.svg)](https://github.com/DanielXMoore/hera/actions/workflows/build.yml)\n[![Coverage Status](https://coveralls.io/repos/github/DanielXMoore/Hera/badge.svg?branch=main)](https://coveralls.io/github/DanielXMoore/Hera?branch=main)\n\nThe mother of all parsers.\n\n- [Parsing Expression Grammars:\nA Recognition-Based Syntactic Foundation](https://bford.info/pub/lang/peg.pdf)\n- [Wikipedia](https://en.wikipedia.org/wiki/Parsing_expression_grammar)\n\nQuickstart\n---\n\n```bash\nnpm install @danielx/hera\n```\n\nExample grammar that counts the number of 'a's followed by the number of 'b's\nthen returns the difference.\n\n```hera\n# parser.hera\nProgram\n  A:a B:b -\u003e\n    return a.length - b.length\nA\n  \"a\"*\nB\n  \"b\"*\n```\n\nImport the parser into your JS/TS files:\n\n```javascript\n// index.mjs\nimport { parse } from \"./parser.hera\"\n\nconsole.log(parse(\"aaaa\"), parse(\"bbb\"), parse(\"aabb\")) // 4 -3 0\n\nparse(\"c\") // throws error\n```\n\nRun on the command line:\n\n```bash\nnode --require '@danielx/hera/register' index.mjs\n```\n\nUse the Hera esbuild plugin to bundle:\n\n```javascript\nimport esbuild from \"esbuild\"\nimport heraPlugin from \"@danielx/hera/esbuild-plugin\"\n\nawait esbuild.build({\n  entryPoints: ['index.mjs'],\n  bundle: true,\n  outfile: 'out.js',\n  plugins: [heraPlugin()],\n})\n```\n\nOverview\n---\n\nHera uses Parsing Expression grammars to create parsers for programatic\nlanguages.\n\nHera grammars are indentation based, with each rule consisting of a name,\nindented choices that the name could expand to, and\nan optional further-indented code block (handler) for each choice:\n\n```\nRuleName\n  Choice1\n  Choice2 -\u003e\n    ...code...\n```\n\nParsing makes heavy use of the built-in regular expression capabilities of\nJavaScript. Terminals are either literal strings or regular expressions.\nRules are composed of choices or sequences of other rules and terminals.\n\nThe first rule listed in the grammar is the starting point. Each choice for the\nrule is checked in order, returning on the first match.\n\nDefinitions\n---\n\n**Rule**: A named production. The name is written on one unindented line by itself, and the choices (possible expansions) are written on separate lines with common indentation.  For example:\n\n```\nRuleName\n  Choice1\n  Choice2\n```\n\nChoices are attempted in order, and the first one to succeed wins.\nNote that this property is recursive, so may involve backtracking.\nEach choice can be any expression, as defined below, together with an\noptional handler.\n\n**Expression**: An expression can be a sequence, choice expression, or repetition of terminals, rule names, or expressions (recursive sequences, choice expressions, or repetitions).  When mixing sequences, choice expressions, and repetitions, use parentheses to separate them.  For example, `Part ( \",\" Part )*` is a sequence of a rule name and a repetition of a terminal and a rule name, representing one or more `Part`s separated by commas.\n\n**Choice expression** (`/`): A short inline way to specify a choice between two or more subchoices. For example, `This / That / Other` matches `This` or `That` or `Other`, whichever succeeds first. This is equivalent to `AnonymousRule` where\n\n```\nAnonymousRule\n  This\n  That\n  Other\n```\n\n**Sequence** (` `): One thing after another, separated by spaces. For example, `\"(\" Expr \")\"` matches the character `\"(\"` followed by a match of `Expr` followed by the character `\")\"`. Sequences with more than one part return an array of the parts.\n\n**Terminal** (`\"...\"`, `/.../`):\nA string literal (surrounded by double quotes)\nor a regular expression (normally surrounded by forward slashes).\nSimple regular expressions consisting of just `.` or character classes\nlike `[A-Z][a-z]*` do not need surrounding slashes.\nIn any case, the entire terminal must be matched at the exact position.\n(For regular expressions, this is as if the expressions started with `^`\nand it was applied to the rest of the string.)\nTerminals return a string when they match.\nIf the entire choice of a rule is a regular expression, then\nthe groups of the regular expression are available as `$1`, `$2`, ...\nand the matching string is available as `$0`.\n\n**Repetition** (`*`, `+`): `...*` means \"zero or more expansions of `...`\", and `...+` means one or more repetitions of `Choice`. Repetitions return an array of the matches.\n\n**Lookahead predicates** (`\u0026`, `!`): `\u0026...` and `!...` assert the existence or non-existence, respectively, of a match of `...`, without advancing the position or consuming any input. For example, `\u0026/\\s/` is like the look-ahead regular expression `/(?=\\s)/`.\n\n**Stringify** (`$`): `$...` matches `...` but returns just the string of the input that matched, instead of the computed return value from the matching process (from handlers and the arrays from sequences and repetitions).\n\n**Handler**: A mapping from the matched choice to a language primitive.\nHandlers are attached to rule choices by adding `-\u003e` after the choice.\nOptionally, `-\u003e` can be preceded by a return type annotation of the form `::type`.\nThe most general handler is JavaScript/TypeScript code indented\nbeneath the choice, which `return`s the desired value for the matched choice.\nThis code can also refer to the default value (strings for terminals,\narrays for sequences or repetitions) via `$0`,\nor to the `n`th matching item in the topmost sequence via `$n`.\nEach item in the topmost sequence can also be named via a `:name` suffix\n(for example, `Block:name`), and then the code can also refer to it as `name`.\nIf the expansion is a single regular expression,\n`$n` instead refers to the `n`th group in the regex.\nThe `$n` notation can also be put on the same line as the `-\u003e` as a shorthand\nfor `return $n` on a separate line; this also works for simple expressions\nlike JavaScript literals.\nJavaScript code can return the special value `$skip` to indicate a failed match.\n\n**Comment** (`#...`): Outside of handlers, lines starting with `#` (after possible indentation) are treated as comments. Inside handlers, use JavaScript `//` or `/*...*/` comments.\n\nCode Blocks\n---\n\nYou can use three backticks to create a code block that is inserted directly into the compiled file. These are useful for\ncreating utility functions or adding exports.\n\n````\n```\nfunction toInt(n) {\n  parseInt(n)\n}\n```\n\nNumber\n  [0-9]+ -\u003e\n    return toInt($0)\n````\n\nDemos\n---\n\nIf these demos are not interactive then view this page at\n\u003chttps://danielx.net/hera/docs/README.html\u003e\n\n---\n\nURL Parser \u003chttps://tools.ietf.org/html/rfc3986\u003e\n\n\u003e     #! hera url\n\n---\n\nMath example.\n\n\u003e     #! hera math\n\n---\n\nHera is self generating:\n\n\u003e     #! hera hera\n\n---\n\nToken location example\n\n\u003e     #! hera\n\u003e     Grammar\n\u003e       Punctuation? A+ Punctuation? -\u003e\n\u003e         return [].concat($1, $2, $3)\n\u003e\n\u003e     A\n\u003e       (\"a\" / \"A\") -\u003e\n\u003e         return {type: \"A\", loc: $loc, value: $1}\n\u003e\n\u003e     Punctuation\n\u003e       \"!\" / \".\" / \"?\" -\u003e\n\u003e         return {type: \"Punctuation\", loc: $loc, value: $1}\n\u003e\n\n---\n\nRegex Groups\n\n\u003e     #! hera\n\u003e     Phone\n\u003e       /1-(\\d{3})-(\\d{3})-(\\d{4})/ -\u003e [1, 2, 3]\n\n---\n\n\u003e     #! hera\n\u003e     Grammar\n\u003e       NamedMapping NamedMapping\n\u003e\n\u003e     NamedMapping\n\u003e       Punctuation -\u003e [\"P\", 0]\n\u003e\n\u003e     Punctuation\n\u003e       \".\"\n\nChangelog\n---\n\n- 0.7.3\n  - Using compiled parser instead of VM for performance boost.\n  - Publishing TypeScript types with package.\n  - cjs loader\n    ```javascript\n    require(\"@danielx/hera/register\")\n    const {parse} = require(\"./parser.hera\")\n    ```\n  - esbuild plugin `require(\"@danielx/hera/esbuild-plugin\")`\n  - `hera` CLI tool with experimental TypeScript output support `hera --types \u003c cool-app.hera \u003e parser.ts`\n  - Added support for number literals in structural handlers.\n  - Changed structural handling to use $1, etc. instead of 1 for positional variables.\n  - Added prefix `$` text operator.\n  - Added `.` any character matcher.\n  - Fixed structural mapping bug where in `[\"R\", $1]` the `$1` would take the\n  first element of the result rather than the whole result on non-sequence\n  handlers.\n- 0.7.2\n  - VSCode Extension \u003chttps://marketplace.visualstudio.com/items?itemName=DanielX.hera\u003e\n  - Bare character class\n\nExperiments\n---\n\nCompiling parsers to TypeScript.\n\nGlossary\n---\n\n`EOS` - End of statement\n\n`EOF` - End of file/input\n\nV2 Ideas\n---\n\nEasier way to output a string from a portion of a matching sequence. Maybe add\na caret/select prefix operator.\n\nOptimize option, sequence, and repetition of regexes (combine together) to\nreduce calls to invoke.\n\nSplat in mapping and other convience mappings.\n\nNamed arguments to handlers.\n\nReduce backtracking on common subsequence:\n\n    RuleBody\n      Indent Sequence EOS (Indent ^Sequence EOS)+ -\u003e [\"/\", [2, 4...]]\n      Indent Sequence EOS -\u003e 2\n\nThe above rule should be able to be made efficient (won't need to backtrack\nall the way to the beginning) since it has a common subsequence it should\nbe able to re-use the work already done.\n\nOne alternative is to make it one rule with an optional section and add\nlogic into the handler, but that seems crude.\n\n---\n\n\u003e     #! setup\n\u003e     require(\"./interactive\")(register)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDanielXMoore%2FHera","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDanielXMoore%2FHera","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDanielXMoore%2FHera/lists"}