{"id":18020931,"url":"https://github.com/edubart/lpegrex","last_synced_at":"2025-07-07T07:32:56.690Z","repository":{"id":40326501,"uuid":"370495511","full_name":"edubart/lpegrex","owner":"edubart","description":"Parse programming languages syntax into an AST using PEGs with ease (LPeg Extension).","archived":false,"fork":false,"pushed_at":"2022-12-09T13:15:26.000Z","size":76,"stargazers_count":64,"open_issues_count":2,"forks_count":9,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-04-08T10:34:16.696Z","etag":null,"topics":["ast","lpeg","lpeglabel","lpegrex","lua","nelua","parser","peg"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edubart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-24T22:02:52.000Z","updated_at":"2025-03-23T12:30:22.000Z","dependencies_parsed_at":"2023-01-25T21:30:54.722Z","dependency_job_id":null,"html_url":"https://github.com/edubart/lpegrex","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/edubart/lpegrex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edubart%2Flpegrex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edubart%2Flpegrex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edubart%2Flpegrex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edubart%2Flpegrex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edubart","download_url":"https://codeload.github.com/edubart/lpegrex/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edubart%2Flpegrex/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264034573,"owners_count":23547236,"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":["ast","lpeg","lpeglabel","lpegrex","lua","nelua","parser","peg"],"created_at":"2024-10-30T06:08:19.079Z","updated_at":"2025-07-07T07:32:56.672Z","avatar_url":"https://github.com/edubart.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LPegRex\n\nLPegRex is a re-implementation of\n[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/)/\n[LPegLabel](https://github.com/sqmedeiros/lpeglabel)\n`re` module with some extensions to make\neasy to parse language grammars into an AST (abstract syntax tree)\nwhile maintaining readability.\n\nLPegRex stands for *LPeg Regular Expression eXtended*.\n\n## Goals\n\nThe goal of this library is to extend the LPeg\n[re module](http://www.inf.puc-rio.br/~roberto/lpeg/re.html)\nwith some minor additions to make easy parsing a whole\nprogramming language grammar to an abstract syntax tree\nusing a single, simple, compact and clear PEG grammar.\n\nFor instance is in the goal of the project to parse Lua 5.4 source\nfiles with complete syntax into an abstract syntax tree under 100 lines\nof clear PEG grammar rules while generating an output suitable to be used analyzed by a compiler.\n**This goal was accomplished, see the Lua example section below.**\n\nThe new extensions should not break any existing `re` syntax.\n\nThis project will be later incorporated\nin the [Nelua](https://github.com/edubart/nelua-lang)\nprogramming language compiler.\n**This goal was accomplished, and LPegRex is the new parsing engine\nfor the Nelua compiler.**\n\n## Additional Features\n\n* New predefined patterns for control characters (`%ca` `%cb` `%ct` `%cn` `%cv` `%cf` `%cr`).\n* New predefined patterns for utf8 (`%utf8` `%utf8seq` `%ascii`).\n* New predefined pattern for spaces independent of locale (`%sp`).\n* New syntax for capturing arbitrary values while matching empty strings (e.g. `$true`).\n* New syntax for optional captures (e.g `patt~?`).\n* New syntax for throwing labels errors on failure of expected matches (e.g. `@rule`).\n* New syntax for rules that capture AST Nodes (e.g. `NodeName \u003c== patt`).\n* New syntax for rules that capture tables (e.g. `MyList \u003c-| patt`).\n* New syntax for matching unique tokens with automatic skipping (e.g. `` `,` ``).\n* New syntax for matching unique keywords with automatic skipping (e.g. `` `for` ``).\n* Auto generate `KEYWORD` rule based on used keywords in the grammar.\n* Auto generate `TOKEN` rule based on used tokens in the grammar.\n* Use supplied `NAME_SUFFIX` rule for generating each keyword rule.\n* Use supplied `SKIP` rule for generating each keyword or token rule.\n* Capture nodes with initial and final positions.\n* Support using `-` character in rule names.\n* Pre define some useful auxiliary functions:\n    * `tonil` Substitute captures by `nil`.\n    * `totrue` Substitute captures by `true`.\n    * `tofalse` Substitute captures by `false`.\n    * `toemptytable` Substitute captures by `{}`.\n    * `tonumber` Substitute a string capture by its corresponding number.\n    * `tochar` Substitute a numeric code capture by its corresponding character byte.\n    * `toutf8char` Substitute a numeric code capture by its corresponding UTF-8 byte sequence.\n    * `foldleft` Fold tables to the left (use only with `~\u003e`).\n    * `foldright` Fold tables to the right (use only with `-\u003e`).\n    * `rfoldleft` Fold tables to the left in reverse order (use only with `-\u003e`).\n    * `rfoldright` Fold tables to the right in reverse order (use only with `~\u003e`)\n\n## Quick References\n\nFor reference on how to use `re` and its syntax,\nplease check [its manual](http://www.inf.puc-rio.br/~roberto/lpeg/re.html) first.\n\nHere is a quick reference of the new syntax additions:\n\n| Purpose | Example Syntax | Equivalent Re Syntax |\n|-|-|-|\n| Rule | `name \u003c-- patt` | `name \u003c- patt` |\n| Capture node rule | `Node \u003c== patt` | `Node \u003c- {\\| {:pos:{}:} {:tag:''-\u003e'Node':} patt {:endpos:{}:} \\|}` |\n| Capture tagged node rule | `name : Node \u003c== patt` | `name \u003c- {\\| {:pos:{}:} {:tag:''-\u003e'Node':} patt {:endpos:{}:} \\|}` |\n| Capture table rule | `name \u003c-\\| patt` | `name \u003c- {\\| patt \\|}` |\n| Match keyword | `` `keyword` `` | `'keyword' !NAME_SUFFIX SKIP` |\n| Match token | `` `.` `..` `` | `!('..' SKIP) '.' SKIP '..' SKIP` |\n| Capture token or keyword | `` {`,`} `` | `{','} SKIP` |\n| Optional capture | `` patt~? `` | `patt / ''-\u003etofalse` |\n| Match control character | `%cn` | `%nl` |\n| Arbitrary capture | `$'string'` | `''-\u003e'string'` |\n| Expected match | `@'string' @rule` | `'string'^Expected_string rule^Expected_rule` |\n\nAs you can notice the additional syntax is mostly sugar\nfor common capture patterns that are used when defining programming language grammars.\n\n## Folding auxiliary functions\n\nOften we need to reduce a list of captured AST nodes into a single captured AST node\n(e.g. when reducing a call chain),\nhere we call this operation folding.\nThe following table demonstrates the four ways to fold a list of nodes:\n\n| Purpose | Example Input | Corresponding Output | Syntax |\n|-|-|-|-|\n| Fold tables to the left | `{1}, {2}, {3}` | `{{{1}, 2}, 3}` | `patt ~\u003e foldleft` |\n| Fold tables to the right | `{1}, {2}, {3}` | `{1, {2, {3}}}}` | `patt -\u003e foldright` |\n| Fold tables to the left in reverse order | `{1}, {2}, {3}` | `{{{3}, 2}, 1}` | `patt -\u003e rfoldleft` |\n| Fold tables to the right in reverse order | `{1}, {2}, {3}` | `{3, {2, {1}}` | `patt ~\u003e rfoldright` |\n\nWhere the pattern `patt` captures a list of tables with a least one capture.\nNote that depending on the fold operation you must use its correct arrow (`-\u003e` or `~\u003e`).\n\n## Capture auxiliary syntax\n\nSometimes is useful to match empty strings and capture some arbitrary values,\nthe following tables show auxiliary syntax to help on that:\n\n| Syntax | Captured Lua Value |\n|-|-|\n| `$nil` | `nil` |\n| `$true` | `true` |\n| `$false` | `false` |\n| `$name` | `defs[name]` |\n| `${}` | `{}` |\n| `$16` | `16` |\n| `$'string'` | `\"string\"` |\n| `p~?` | `p` captures if it matches, otherwise `false` |\n\n## Capture auxiliary functions\n\nSometimes is useful to substitute a list of captures by a lua value,\nthe following tables show auxiliary functions to help on that:\n\n| Purpose | Syntax | Captured Value |\n|-|-|-|\n| Substitute captures by `nil` | `p -\u003e tonil` | `nil` |\n| Substitute captures by `false` | `p -\u003e tofalse` | `false` |\n| Substitute captures by `true` | `p -\u003e totrue` | `true` |\n| Substitute captures by `{}` | `p -\u003e toemptytable` | `{}` |\n| Substitute a capture by a number | `p -\u003e tonumber` | Corresponding number of the captured |\n| Substitute a capture by a character byte | `p -\u003e tochar` | Corresponding byte of the captured number |\n| Substitute a capture by UTF-8 byte sequence | `p -\u003e toutf8char` | Corresponding UTF-8 bytes of the captured number |\n\n## Captured node fields\n\nBy default when capturing a node with `\u003c==` syntax, LPegRex will set the following 3 fields:\n\n* `tag` Name of the node (its type)\n* `pos` Initial position of the node match\n* `endpos` Final position of the node match (usually includes following SKIP)\n\nThe user can customize and change these field names or disable them by\nsetting it's corresponding name in the `defs.__options` table when compiling the grammar,\nfor example:\n\n```lua\nlocal mypatt = rex.compile(mygrammar, {__options = {\n  tag = 'name', -- 'tag' field rename to 'name'\n  pos = 'init', -- 'pos' field renamed to 'init'\n  endpos = false, -- don't capture node final position\n}})\n```\n\nThe fields `pos` and `endpos` are useful to generate error messages with precise location\nwhen analyzing the AST and the `tag` field is used to distinguish the node type.\n\n## Captured node action\n\nIn case `defs.__options.tag` is a function, then it's called and the user will be responsible for\nsetting the tag field and return the node, this flexibility exists in case\nspecific actions are required to be executed on node creation, for example:\n\n```lua\nlocal mypatt = rex.compile(mygrammar, {__options = {\n  tag = function(tag, node)\n    print('new node', tag)\n    node.tag = tag\n    return node\n  end\n}})\n```\n\nNote that when this function is called the node children may be incomplete\nin case the node is being folded.\n\n## Matching keywords and tokens\n\nWhen using the back tick syntax (e.g. `` `something` ``),\nLPegRex will register its contents as a **keyword** in case it begins with a letter (or `_`),\nor as **token** in case it contains only punctuation characters (except `_`).\n\nBoth keywords and tokens always match the `SKIP` rule immediately to\nskip spaces, thus the rule `SKIP` must always be defined when using the back tick syntax.\n\nTokens matches are always unique in case of common characters, that is,\nin case both `.` and `..` tokens are defined, the rule `` `.` `` will match\n`.` but not `..`.\n\nIn case a **token** is found, the rule `TOKEN` will be automatically generated,\nthis rule will match any token plus `SKIP`.\n\nIn case a **keyword** is found,\nthe rule `NAME_SUFFIX` also need to be defined, it's used\nto differentiate keywords from identifier names.\n\nIn most cases the user will need define something like:\n\n```\nNAME_SUFFIX   \u003c- [_%w]+\nSKIP          \u003c- %s+\n```\n\nYou may want to edit the `SKIP` rule to consider comments if you grammar supports them.\nToken and keywords will not capture `SKIP` rule when using the syntax ``{`keyword`}``.\n\n## Capturing identifier names\n\nOften we need to create a rule that capture identifier names while ignoring grammar keywords, let call this rule `NAME`.\nTo assist doing this the `KEYWORD` rule is automatically generated based on all defined keywords in\nthe grammar, the user can then use it to define the `NAME` rule, in most cases something like:\n\n```\nNAME          \u003c-- !KEYWORD {NAME_PREFIX NAME_SUFFIX?} SKIP\nNAME_PREFIX   \u003c-- [_%a]\nNAME_SUFFIX   \u003c-- [_%w]+\nSKIP          \u003c- %s+\n```\n\n## Handling syntax errors\n\nAny rule name, keyword, token or string pattern can be preceded by the token `@`,\nmarking it as an expected match, in case the match is not fulfilled an error\nlabel will be thrown using the name `Expected_name`, where `name` is the\ntoken, keyword or rule name.\n\nOnce an error label is found, the user can generate pretty syntax error\nmessages using the function `lpegrex.calcline` to gather line information,\nfor example:\n\n```lua\nlocal patt = lpegrex.compile(PEG)\nlocal ast, errlabel, errpos = patt:match(source)\nif not ast then\n  local lineno, colno, line = lpegrex.calcline(source, errpos)\n  local colhelp = string.rep(' ', colno-1)..'^'\n  error('syntax error: '..filename..':'..lineno..':'..colno..': '..errlabel..\n        '\\n'..line..'\\n'..colhelp)\nend\n```\n\n## Usage Example\n\nHere is a small example parsing JSON into an AST in 12 lines of PEG rules:\n\n```lua\nlocal lpegrex = require 'lpegrex'\n\nlocal patt = lpegrex.compile([[\nJson          \u003c-- SKIP (Object / Array) (!.)^UnexpectedSyntax\nObject        \u003c== `{` (Member (`,` @Member)*)? @`}`\nArray         \u003c== `[` (Value (`,` @Value)*)? @`]`\nMember        \u003c== String `:` @Value\nValue         \u003c-- String / Number / Object / Array / Boolean / Null\nString        \u003c-- '\"' {~ ('\\' -\u003e '' @ESCAPE / !'\"' .)* ~} @'\"' SKIP\nNumber        \u003c-- {[+-]? (%d+ '.'? %d+? / '.' %d+) ([eE] [+-]? %d+)?} -\u003e tonumber SKIP\nBoolean       \u003c-- `false` -\u003e tofalse / `true` -\u003e totrue\nNull          \u003c-- `null` -\u003e tonil\nESCAPE        \u003c-- [\\/\"] / ('b' $8 / 't' $9 / 'n' $10 / 'f' $12 / 'r' $13 / 'u' {%x^4} $16) -\u003e tochar\nSKIP          \u003c-- %s*\nNAME_SUFFIX   \u003c-- [_%w]+\n]])\n\nlocal source = '[{\"string\":\"some\\\\ntext\", \"boolean\":true, \"number\":-1.5e+2, \"null\":null}]'\n\nlocal ast, errlabel, errpos = patt:match(source)\nif not ast then\n  local lineno, colno, line = lpegrex.calcline(source, errpos)\n  local colhelp = string.rep(' ', colno-1)..'^'\n  error('syntax error: '..lineno..':'..colno..': '..errlabel..\n        '\\n'..line..'\\n'..colhelp)\nend\n-- `ast` should be a table with the JSON\nprint('JSON parsed with success!')\n```\n\nThe above should parse into the following equivalent AST table:\n```lua\nlocal ast = { tag = \"Array\", pos = 1, endpos = 73,\n  { tag = \"Object\", pos = 2, endpos = 72,\n    { tag = \"Member\", pos = 3, endpos = 24,\n    \"string\",\"some\\ntext\" },\n    { tag = \"Member\", pos = 26, endpos = 40,\n    \"boolean\", true },\n    { tag = \"Member\", pos = 42, endpos = 58,\n      \"number\", -150.0 },\n    { tag = \"Member\", pos = 60, endpos = 71,\n      \"null\", nil }\n  }\n}\n```\n\nA JSON parser similar to this example can be found in\n[parsers/json.lua](https://github.com/edubart/lpegrex/blob/main/parsers/json.lua).\n\n## Debugging rule entry and exit\n\nWhen prototyping complex grammars you may want to debug the rules that\nthe parser is trying to match and the ones that were successfully matched.\nYou can enable LPegRex debug mode for this\nby setting `lpegrex.debug = true` globally.\nWhen debug is enabled all compiled grammars will be compiled in debug mode.\n\nWhen debugging is enabled every attempt to match a rule will print\n`ENTER \u003crulename\u003e (\u003clineno\u003e:\u003ccolno\u003e)` to `io.stderr`,\nand every rule successfully matched will print\n`LEAVE \u003crulename\u003e (\u003clineno\u003e:\u003ccolno\u003e)` to `io.stderr`.\nNotice that rules failing to match will not print `LEAVE`.\n\nThe following is an example of parsing `{\"string\":` JSON chunk\nusing the JSON parser shown above with debugging enabled:\n\n```\nENTER Json (1:1)\nENTER SKIP (1:1)\nLEAVE SKIP (1:1)\nENTER Object (1:1)\nENTER { (1:1)\nENTER Array (1:1)\nENTER [ (1:1)\nENTER SKIP (1:2)\nLEAVE SKIP (1:2)\nLEAVE [ (1:2)\nENTER Value (1:2)\nENTER String (1:2)\nENTER Number (1:2)\nENTER Object (1:2)\nENTER { (1:2)\nENTER SKIP (1:3)\nLEAVE SKIP (1:3)\nLEAVE { (1:3)\nENTER Member (1:3)\nENTER String (1:3)\nENTER SKIP (1:11)\nLEAVE SKIP (1:11)\nLEAVE String (1:11)\nENTER : (1:11)\nENTER SKIP (1:12)\nLEAVE SKIP (1:12)\nLEAVE : (1:12)\n```\n\nNotice `String` ENTER at `1:3` and LEAVE at `1:11`,\nthis means that we have matched the rule `String` in that range.\nNotice `Number` ENTER at `1:2` while no LEAVE is shown for `Number`,\nthis means that we attempted to match `Number`\nbut it failed since no LEAVE was shown afterwards.\n\n## Installing\n\nTo use LPegRex you need [LPegLabel](https://github.com/sqmedeiros/lpeglabel)\nto be properly installed.\nIf you have it already installed you can just copy the\n[lpegrex.lua](https://github.com/edubart/lpegrex/blob/main/lpegrex.lua) file.\n\nIf you can also install it using the\n[LuaRocks](https://luarocks.org/) package manager,\nwith the following command:\n\n```shell\nluarocks install lpegrex\n```\n\nThe library should work with Lua 5.x versions (and also LuaJIT).\n\n## Complete Lua Example\n\nA Lua 5.4 parser is defined in\n[parsers/lua.lua](https://github.com/edubart/lpegrex/blob/main/parsers/lua.lua),\nit servers as a good example on how to define a full language grammar\nin a single PEG that generates an AST suitable to be analyzed by a compiler,\nwhile also handling source syntax errors.\n\nA Lua AST printer using it is available in\n[examples/lua.lua](https://github.com/edubart/lpegrex/blob/main/examples/lua-ast.lua)\nYou can run it to parse any Lua file and print its AST.\n\nFor example by doing `lua examples/lua-ast.lua inputs/fact.lua` you should\nget the following AST output:\n\n```\nBlock\n| FuncDecl\n| | Id\n| | | \"fact\"\n| | -\n| | | Id\n| | | | \"n\"\n| | Block\n| | | If\n| | | | BinaryOp\n| | | | | Id\n| | | | | | \"n\"\n| | | | | \"eq\"\n| | | | | Number\n| | | | | | 0\n| | | | Block\n| | | | | Return\n| | | | | | -\n| | | | | | | Number\n| | | | | | | | 1\n| | | | Block\n| | | | | Return\n| | | | | | -\n| | | | | | | BinaryOp\n| | | | | | | | Id\n| | | | | | | | | \"n\"\n| | | | | | | | \"mul\"\n| | | | | | | | Call\n| | | | | | | | | -\n| | | | | | | | | | BinaryOp\n| | | | | | | | | | | Id\n| | | | | | | | | | | | \"n\"\n| | | | | | | | | | | \"sub\"\n| | | | | | | | | | | Number\n| | | | | | | | | | | | 1\n| | | | | | | | | Id\n| | | | | | | | | | \"fact\"\n| Call\n| | -\n| | | Call\n| | | | -\n| | | | | Number\n| | | | | | 10\n| | | | Id\n| | | | | \"fact\"\n| | Id\n| | | \"print\"\n```\n\n## Complete C11 example\n\nA complete C11 parser has been implemented and is available in\n[parsers/c11.lua](https://github.com/edubart/lpegrex/blob/main/parsers/c11.lua),\nit's experimental but it was verified to parse hundreds of prepossessed C file sources.\n\nA C11 AST printer using it is available in\n[examples/c11-ast.lua](https://github.com/edubart/lpegrex/blob/main/examples/c11-ast.lua).\n\nNote that the C file must be preprocessed, you can generate a preprocessed C file\nwith GCC/Clang or running `gcc -E file.c \u003e file_preprocessed.c`.\n\nFor example by doing `lua examples/c11-ast.lua inputs/fact.c` you should\nget the following AST output:\n\n```\ntranslation-unit\n| declaration\n| | type-declaration\n| | | declaration-specifiers\n| | | | storage-class-specifier\n| | | | | \"extern\"\n| | | | type-specifier\n| | | | | \"int\"\n| | | init-declarator-list\n| | | | init-declarator\n| | | | | declarator\n| | | | | | declarator-parameters\n| | | | | | | identifier\n| | | | | | | | \"printf\"\n| | | | | | | parameter-type-list\n| | | | | | | | parameter-declaration\n| | | | | | | | | declaration-specifiers\n| | | | | | | | | | type-qualifier\n| | | | | | | | | | | \"const\"\n| | | | | | | | | | type-specifier\n| | | | | | | | | | | \"char\"\n| | | | | | | | | declarator\n| | | | | | | | | | pointer\n| | | | | | | | | | | identifier\n| | | | | | | | | | | | \"format\"\n| | | | | | | | parameter-varargs\n| function-definition\n| | declaration-specifiers\n| | | storage-class-specifier\n| | | | \"static\"\n| | | type-specifier\n| | | | \"int\"\n| | declarator\n| | | declarator-parameters\n| | | | identifier\n| | | | | \"fact\"\n| | | | parameter-type-list\n| | | | | parameter-declaration\n| | | | | | declaration-specifiers\n| | | | | | | type-specifier\n| | | | | | | | \"int\"\n| | | | | | declarator\n| | | | | | | identifier\n| | | | | | | | \"n\"\n| | declaration-list\n| | compound-statement\n| | | if-statement\n| | | | expression\n| | | | | binary-op\n| | | | | | identifier\n| | | | | | | \"n\"\n| | | | | | \"==\"\n| | | | | | integer-constant\n| | | | | | | \"0\"\n| | | | return-statement\n| | | | | expression\n| | | | | | integer-constant\n| | | | | | | \"1\"\n| | | | return-statement\n| | | | | expression\n| | | | | | binary-op\n| | | | | | | identifier\n| | | | | | | | \"n\"\n| | | | | | | \"*\"\n| | | | | | | argument-expression\n| | | | | | | | argument-expression-list\n| | | | | | | | | binary-op\n| | | | | | | | | | identifier\n| | | | | | | | | | | \"n\"\n| | | | | | | | | | \"-\"\n| | | | | | | | | | integer-constant\n| | | | | | | | | | | \"1\"\n| | | | | | | | identifier\n| | | | | | | | | \"fact\"\n| function-definition\n| | declaration-specifiers\n| | | type-specifier\n| | | | \"int\"\n| | declarator\n| | | declarator-parameters\n| | | | identifier\n| | | | | \"main\"\n| | declaration-list\n| | compound-statement\n| | | expression-statement\n| | | | expression\n| | | | | argument-expression\n| | | | | | argument-expression-list\n| | | | | | | string-literal\n| | | | | | | | \"%d\\\\n\"\n| | | | | | | argument-expression\n| | | | | | | | argument-expression-list\n| | | | | | | | | integer-constant\n| | | | | | | | | | \"10\"\n| | | | | | | | identifier\n| | | | | | | | | \"fact\"\n| | | | | | identifier\n| | | | | | | \"printf\"\n| | | return-statement\n| | | | expression\n| | | | | integer-constant\n| | | | | | \"0\"\n```\n\n## Successful use case\n\nLPegRex is successfully used as the parsing engine in the Nelua programming\nlanguage compiler, you can see the complete syntax defined in a single\nPEG grammar [in this file](https://github.com/edubart/nelua-lang/blob/master/nelua/syntaxdefs.lua).\n\n## Try it online\n\nYou can test and prototype grammars with LPegRex live in the browser using the cool [lua-wasm-playground](https://mingodad.github.io/lua-wasm-playground/) tool created by [@mingodad](https://github.com/mingodad/lua-wasm-playground). There are C11 and Lua parsers as examples there.\n\n## Tests\n\nMost LPeg/LPegLabel tests where migrated into `tests/lpegrex-test.lua`\nand new tests for the addition extensions were added.\n\nTo run the tests just run `lua tests/test.lua`.\n\n## License\n\nMIT, see LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedubart%2Flpegrex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedubart%2Flpegrex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedubart%2Flpegrex/lists"}