{"id":25050689,"url":"https://github.com/0x2a-42/lelwel","last_synced_at":"2025-05-16T11:03:53.393Z","repository":{"id":62441985,"uuid":"381504905","full_name":"0x2a-42/lelwel","owner":"0x2a-42","description":"Resilient LL(1) parser generator for Rust","archived":false,"fork":false,"pushed_at":"2025-04-26T17:52:31.000Z","size":689,"stargazers_count":139,"open_issues_count":7,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-26T18:32:21.050Z","etag":null,"topics":["grammar","parser","parser-generator","parsing","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/0x2a-42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","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,"zenodo":null}},"created_at":"2021-06-29T21:48:31.000Z","updated_at":"2025-04-26T17:52:34.000Z","dependencies_parsed_at":"2023-12-13T01:28:03.077Z","dependency_job_id":"9d734598-5888-4696-85d5-88fcbc70f1bd","html_url":"https://github.com/0x2a-42/lelwel","commit_stats":{"total_commits":99,"total_committers":1,"mean_commits":99.0,"dds":0.0,"last_synced_commit":"1a68f19c6c3ae9ccb3fdf7922b0552daa99540b3"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2a-42%2Flelwel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2a-42%2Flelwel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2a-42%2Flelwel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2a-42%2Flelwel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0x2a-42","download_url":"https://codeload.github.com/0x2a-42/lelwel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254518384,"owners_count":22084374,"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":["grammar","parser","parser-generator","parsing","rust"],"created_at":"2025-02-06T09:17:40.804Z","updated_at":"2025-05-16T11:03:53.386Z","avatar_url":"https://github.com/0x2a-42.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lelwel\n[![Crates.io](https://img.shields.io/crates/v/lelwel)](https://crates.io/crates/lelwel)\n[![MIT/Apache 2.0](https://img.shields.io/crates/l/lelwel)](./LICENSE-MIT)\n[![Crates.io](https://img.shields.io/crates/d/lelwel)](https://crates.io/crates/lelwel)\n[![Rust](https://img.shields.io/github/actions/workflow/status/0x2a-42/lelwel/rust.yml)](https://github.com/0x2a-42/lelwel/actions)\n[![Playground](https://img.shields.io/badge/playground-8A2BE2)](https://0x2a-42.github.io/playground.html)\n\n## Table of Contents\n* [Introduction](#introduction)\n* [Error Resilience](#error-resilience)\n* [Grammar Examples](#grammar-examples)\n* [Quickstart](#quickstart)\n* [Grammar Specification](#grammar-specification)\n* [License](#license)\n\n## Introduction\n\n[Lelwel](https://en.wikipedia.org/wiki/Lelwel_hartebeest) (**L**anguage for **E**xtended **L**L(1) parsing **W**ith **E**rror resilience and **L**ossless syntax trees) generates recursive descent parsers for Rust using [LL(1) grammars](https://en.wikipedia.org/wiki/LL_grammar) with extensions for direct left recursion, operator precedence, semantic predicates (which also enable arbitrary lookahead), semantic actions (which allow to deal with semantic context sensitivity, e.g. type / variable name ambiguity in C), and a restricted ordered choice (which allows for backtracking).\n\nThe parser creates a homogeneous, lossless, concrete syntax tree (CST) that can be used to construct an abstract syntax tree (AST).\nSpecial node rename, elision, marker, and creation operators allow fine-grained control over how the CST is built for certain parses.\n\nThe error recovery and tree construction is inspired by Alex Kladov's (matklad) [Resilient LL Parsing Tutorial](https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html).\nLelwel uses a (to my knowledge) novel heuristic to automatically calculate the recovery sets, by using the follow sets of the dominators in the directed graph induced by the grammar.\n\nLelwel is written as a library.\nIt is used by the CLI tool `llw`, the language server `lelwel-ls`, and can be included as a build dependency in order to be called from a `build.rs` file.\nThere is a plugin for [Neovim](https://github.com/0x2a-42/nvim-lelwel) that uses the language server.\n\nBy default the generated parser uses [Logos](https://github.com/maciejhirsz/logos) for lexing and [Codespan](https://github.com/brendanzab/codespan) for diagnostics, however this is not mandatory.\n\n#### Why Yet Another Parser Generator?\n* **Error Resilience:** The generated parser may provide similar [error resilience](https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html#Why-Resilience-is-Needed) as handwritten parsers.\n* **Lossless Syntax Tree:** Language tooling such as language servers or formatters require all the information about the source code including whitespaces and comments.\n* **Language Server:** Get instant feedback when your grammar contains conflicts or errors.\n* **Easy to Debug:** The generated parser is easy to understand and can be debugged with standard tools, as the code is not generated by a procedural macro.\n\n#### Why LL(1) and not a more general CFL or PEG parser?\n* **Error Resilience:** It seems to be the case that LL parsers are better suited than LR parsers for generating meaningful syntax trees from incomplete source code.\n* **Runtime Complexity:** More general parsers such as GLR/GLL or ALL(*) can have a runtime complexity of $O(n^3)$ or $O(n^4)$ respectively for certain grammars. With LL(1) parsers you are guaranteed to have linear runtime complexity as long as your semantic actions and predicates have a constant runtime complexity.\n* **Ambiguity:** The decision problem of whether an arbitrary context free grammar is ambiguous is undecidable. Warnings of a general parser generator therefore may contain false positives. In the worst case ambiguities may be found at runtime.\nThe PEG formalism just defines ambiguity away, which may cause the parser to parse a different language than you think.\n\n## Error Resilience\nThe following example shows the difference between [Lelwel](https://0x2a-42.github.io/playground.html) and [Tree-sitter](https://tree-sitter.github.io/tree-sitter/7-playground.html), a GLR parser generator with sophisticated error recovery, when parsing certain incomplete C source code.\n\n```c\nvoid f() {\n  g(1,\n  int x = 2 +\n}\n```\n\u003cdetails\u003e\n\u003csummary\u003eLelwel syntax tree\u003c/summary\u003e\n\n```\ntranslation_unit [0..33]\n    function_definition [0..33]\n        declaration_specifiers [0..4]\n            type_specifier [0..4]\n                Void \"void\" [0..4]\n        Whitespace \" \" [4..5]\n        declarator [5..8]\n            function_declarator [5..8]\n                ident_declarator [5..6]\n                    Identifier \"f\" [5..6]\n                LPar \"(\" [6..7]\n                RPar \")\" [7..8]\n        Whitespace \" \" [8..9]\n        compound_statement [9..33]\n            LBrace \"{\" [9..10]\n            Whitespace \"\\n  \" [10..13]\n            expression_statement [13..17]\n                call_expr [13..17]\n                    ident_expr [13..14]\n                        Identifier \"g\" [13..14]\n                    LPar \"(\" [14..15]\n                    argument_expression_list [15..17]\n                        int_expr [15..16]\n                            IntConst \"1\" [15..16]\n                        Comma \",\" [16..17]\n            Whitespace \"\\n  \" [17..20]\n            declaration [20..31]\n                declaration_specifiers [20..23]\n                    type_specifier [20..23]\n                        Int \"int\" [20..23]\n                Whitespace \" \" [23..24]\n                init_declarator_list [24..31]\n                    init_declarator [24..31]\n                        declarator [24..25]\n                            ident_declarator [24..25]\n                                Identifier \"x\" [24..25]\n                        Whitespace \" \" [25..26]\n                        Assign \"=\" [26..27]\n                        Whitespace \" \" [27..28]\n                        initializer [28..31]\n                            bin_expr [28..31]\n                                int_expr [28..29]\n                                    IntConst \"2\" [28..29]\n                                Whitespace \" \" [29..30]\n                                Plus \"+\" [30..31]\n            Whitespace \"\\n\" [31..32]\n            RBrace \"}\" [32..33]\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTree-sitter syntax tree\u003c/summary\u003e\n\n```\ntranslation_unit [0, 0] - [4, 0]\n  function_definition [0, 0] - [3, 1]\n    type: primitive_type [0, 0] - [0, 4]\n    declarator: function_declarator [0, 5] - [0, 8]\n      declarator: identifier [0, 5] - [0, 6]\n      parameters: parameter_list [0, 6] - [0, 8]\n        ( [0, 6] - [0, 7]\n        ) [0, 7] - [0, 8]\n    body: compound_statement [0, 9] - [3, 1]\n      { [0, 9] - [0, 10]\n      ERROR [1, 2] - [2, 11]\n        identifier [1, 2] - [1, 3]\n        ( [1, 3] - [1, 4]\n        number_literal [1, 4] - [1, 5]\n        , [1, 5] - [1, 6]\n        assignment_expression [2, 2] - [2, 11]\n          left: identifier [2, 2] - [2, 5]\n          ERROR [2, 6] - [2, 7]\n            identifier [2, 6] - [2, 7]\n          operator: = [2, 8] - [2, 9]\n          right: number_literal [2, 10] - [2, 11]\n      ERROR [2, 12] - [2, 13]\n        + [2, 12] - [2, 13]\n      } [3, 0] - [3, 1]\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]\n\u003e Tree-sitter is an excellent tool for its intended purpose, which is mainly syntax highlighting.\n\u003e This comparison only demonstrates the possible syntax tree quality of an error resilient parser.\n\n## Grammar Examples\nThe [parser for lelwel grammar files](src/frontend/lelwel.llw) (\\*.llw) is itself generated by Lelwel.\nThere are also examples for [C without a preprocessor](examples/c/src/c.llw) (actually resolves ambiguity with semantic context information, unlike examples for ANTLR4 and Tree-sitter), [Lua](examples/lua/src/lua.llw), [WGSL](examples/wgsl/src/wgsl.llw), [Python 2.7](examples/python2/src/python.llw), [arithmetic expressions](examples/calc/src/calc.llw), [JSON](examples/json/src/json.llw), [TOML](examples/toml/src/toml.llw), and [Oberon-0](examples/oberon0/src/oberon0.llw).\n\nYou can try out examples in the [Lelwel Playground](https://0x2a-42.github.io/playground.html).\n\nThe [following example](examples/l) shows a grammar for the toy language \"L\" introduced by the [Resilient LL Parsing Tutorial](https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html#Introducing-L).\n\n```antlr\ntoken Fn='fn' Let='let' Return='return' True='true' False='false';\ntoken Arrow='-\u003e' LPar='(' RPar=')' Comma=',' Colon=':' LBrace='{' RBrace='}'\n      Semi=';' Asn='=' Plus='+' Minus='-' Star='*' Slash='/';\ntoken Name='\u003cname\u003e' Int='\u003cint\u003e';\ntoken Whitespace;\n\nskip Whitespace;\n\nstart file;\n\nfile: fn*;\nfn: 'fn' Name param_list ['-\u003e' type_expr] block;\nparam_list: '(' [param (?1 ',' param)* [',']] ')';\nparam: Name ':' type_expr;\ntype_expr: Name;\nblock: '{' stmt* '}';\nstmt^:\n  stmt_expr\n| stmt_let\n| stmt_return\n;\nstmt_expr: expr ';';\nstmt_let: 'let' Name '=' expr ';';\nstmt_return: 'return' [expr] ';';\nexpr:\n  expr ('*' | '/') expr @expr_binary\n| expr ('+' | '-') expr @expr_binary\n| expr arg_list @expr_call\n| Name @expr_name\n| '(' expr ')' @expr_paren\n| expr_literal ^\n;\nexpr_literal: Int | 'true' | 'false';\narg_list: '(' [expr (?1 ',' expr)* [',']] ')';\n```\n\n## Quickstart\n1. Write a grammar file and place it in the `src` directory of your crate.\n   Optionally you can install the CLI or language server to validate your grammar file: `cargo install --features=cli,lsp lelwel`.\n1. Add the following to your `Cargo.toml` and  `build.rs` files.\n   ```toml\n   [dependencies]\n   logos = \"0.15\"\n   codespan-reporting = \"0.12\"\n\n   [build-dependencies]\n   lelwel = \"0.8\"\n   ```\n   ```rust\n   fn main() {\n      lelwel::build(\"src/your_grammar.llw\");\n   }\n   ```\n1. Start a build. This will create a `lexer.rs` and a `parser.rs` file next to your grammar file.\n   The `lexer.rs` and `parser.rs` files are supposed to be manually edited to implement the lexer and the parser callbacks. The actual parser `generated.rs` is included in `parser.rs` and written to the Cargo `OUT_DIR`.\n   If you change the grammar after the `lexer.rs` and `parser.rs` files have been generated, it may be required to manually update the `Token` enum or the `ParserCallbacks` implementation.\n1. Use the `lexer` and `parser` modules with the following minimal `main.rs` file for printing the CST and diagnostics.\n   ```rust\n   mod lexer;\n   mod parser;\n\n   use codespan_reporting::files::SimpleFile;\n   use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};\n   use codespan_reporting::term::{self, Config};\n   use parser::*;\n\n   fn main() -\u003e std::io::Result\u003c()\u003e {\n       let args: Vec\u003cString\u003e = std::env::args().collect();\n       if args.len() != 2 {\n           std::process::exit(1);\n       }\n\n       let source = std::fs::read_to_string(\u0026args[1])?;\n       let mut diags = vec![];\n       let cst = Parser::parse(\u0026source, \u0026mut diags);\n       println!(\"{cst}\");\n\n       let file = SimpleFile::new(\u0026args[1], \u0026source);\n       let writer = StandardStream::stderr(ColorChoice::Auto);\n       let config = Config::default();\n       for diag in diags.iter() {\n           term::emit(\u0026mut writer.lock(), \u0026config, \u0026file, diag).unwrap();\n       }\n       Ok(())\n   }\n   ```\n\n## Grammar Specification\n\nLelwel grammars are based on the formalism of [context free grammars (CFG)](https://en.wikipedia.org/wiki/Context-free_grammar) and more specifically [LL(1) grammars](https://en.wikipedia.org/wiki/LL_grammar).\nThere are certain extensions to the classical grammar syntax such as constructs similar to those from EBNF.\n\nA grammar file consists of top level definitions which are independent of their order.\n\n### Comments\n\nC-style and C++-style comments can be used.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e // this is a single-line comment\n\u003e /*\n\u003e this is a\n\u003e multi-line comment\n\u003e */\n\u003e ```\n\nDocumentation comments can be used before top level definitions.\n\u003e **Example**\n\u003e ```antlr\n\u003e /// this is a doc-comment\n\u003e token A B C;\n\u003e ```\n\n\u003e [!TIP]\n\u003e Documentation comments are shown by the language server on hover.\n\n### Token List\nA token list definition introduces a list of tokens (terminals) to the grammar.\nIt starts with the `token` keyword, ends with a `;` and contains a list of token names and corresponding token symbols.\n\nA token name must start with a capital letter.\nThe token symbol is optional and delimited by single quotation marks.\nIn a regex a token can be referenced by its name or symbol.\n\n\u003e [!TIP]\n\u003e The token symbol is used in error messages and the generator of the `lexer.rs` file.\n\u003e If the token symbol string starts with `\u003c` and ends with `\u003e`, the token is interpreted as a class of tokens for which the symbol is only a description.\n\u003e This influences how error messages and lexer rules are generated by default in `lexer.rs`.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e token MyKeyword='my_keyword' Int='\u003cinteger literal\u003e' True='true' False='false';\n\u003e ```\n\n### Skip\nA `skip` definition allows to specify a list of tokens, which are ignored by the parser.\nThese tokens will however still be part of the syntax tree.\n\u003e **Example**\n\u003e ```antlr\n\u003e skip Whitespace Comment;\n\u003e ```\n\n### Right\nA `right` definition allows to specify a list of tokens, which are handled as right associative operators in left recursive rules.\n\u003e **Example**\n\u003e ```antlr\n\u003e right '^' '=';\n\u003e ```\n\n### Start\nA `start` definition specifies the start rule of the grammar.\nThere must be exactly one start definition in a grammar.\nThe start rule must not be referenced in a regex.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e start translation_unit;\n\u003e ```\n\n### Rule\nA grammar rule must start with a lower case letter.\nA regular expression is used to specify the right hand side of the rule.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e translation_unit: declaration*;\n\u003e ```\n\n#### Regular Expressions\nRegular expressions are built from the following syntactic constructs.\n- **Grouping**: `(...)`\n- **Identifier**: `rule_name` or `TokenName`\n- **Symbol**: `'token symbol'`\n- **Concatenation**: `A B` which is `A` followed by `B`\n- **Alternation**: `A | B` which is either `A` or `B`\n- **Ordered Choice**: `A / B` which is `A` or else `B`\n- **Optional**: `[A]` which is either `A` or nothing\n- **Star Repetition**: `A*` which is a repetition of 0 or more `A`\n- **Plus Repetition**: `A+` which is a repetition of 1 or more `A`\n- **Semantic Predicate**: `?1` which is the semantic predicate number 1\n- **Semantic Action**: `#1` which is the semantic action number 1\n- **Semantic Assertion**: `!1` which is the semantic assertion number 1\n- **Node Rename**: `@new_node_name` renames the rule syntax tree node\n- **Node Elision**: `^` prevents creation of the rule syntax tree node\n- **Node Marker**: `\u003c1` marker with index 1 for a new node\n- **Node Creation**: `1\u003enew_node_name` insert node at position of marker with index 1\n- **Commit**: `~` commits to a parse in an ordered choice\n\n#### Ordered Choice\nThe ordered choice operator `/` has a precedence between alternation and concatenation. Unlike in the PEG formalism, the operator is restricted, such that only one ordered choice can be active at a time.\n\n\u003e [!WARNING]\n\u003e This restriction avoids the worst case exponential time complexity of recursive descent PEG implementations without the use of memoization.\n\u003e Due to the unlimited lookahead it is still possible to construct parsers with $O(n^2)$ worst case performance.\n\n\u003e [!WARNING]\n\u003e It is possible that an earlier branch prevents a later branch from ever being reached.\n\u003e Only use ordered choice as an instrument to explicitly resolve an ambiguity.\n\nThe commit operator `~` commits a parse to a choice, so in case of a failure there will be no backtracking.\n\n\u003e [!NOTE]\n\u003e This is useful as an optimization and to improve error reporting.\n\u003e Furthermore it can be used to avoid the ordered choice restriction in some cases, as in a concatenation no ordered choice is active after the commit operator.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e token Id Num Eq='=' Semi=';' LPar='(' RPar=')';\n\u003e start stmt;\n\u003e \n\u003e stmt^:\n\u003e   decl_stmt\n\u003e / expr_stmt\n\u003e ;\n\u003e decl_stmt: type Id ~ ['=' expr] ';';\n\u003e expr_stmt: expr ';';\n\u003e expr:\n\u003e   Id\n\u003e | Num\n\u003e | '(' (\n\u003e     type ')' !1 ~ expr\n\u003e   / expr ')'\n\u003e   )\n\u003e ;\n\u003e type: Id;\n\u003e ```\n\n#### Direct Left Recursion\nRules with direct left recursion are parsed using a Pratt parser.\nThe order of recursive rule branches in the top level alternation defines the binding power of the operator tokens.\nThe binding power decreases from the first to the last branch.\nIn a left recursive branch the follow set of the first concatenation element defines the operator tokens.\nOtherwise in a right recursive branch the first set of the first concatenation element defines the operator tokens.\n\n\u003e [!TIP]\n\u003e Use a Pratt parser for expressions, as it improves readability and efficiency compared to an encoding of operator precedence in grammar rules.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e expr:\n\u003e   expr '^' expr\n\u003e | ('-' | '+') expr\n\u003e | expr ('*' | '/') expr\n\u003e | expr ('+' | '-') expr\n\u003e | Num\n\u003e | '(' expr ')'\n\u003e ;\n\u003e ```\n\nMixing left and right associative operators in the same branch is not allowed.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e right '=';\n\u003e\n\u003e expr:\n\u003e   expr ('+' | '=') expr // ❌ error\n\u003e | Num\n\u003e ;\n\u003e ```\n\n#### Semantic Actions, Predicates, and Assertions\nSemantic actions can be defined at any point in a regex. They must not occur within an ordered choice.\n\nSemantic predicates can be defined at the beginning of an alternation branch, an optional or a repetition. The syntax `?t` can be used to define a constant `true` predicate, which is useful for disambiguation, where one parse is always prioritized (e.g. [dangling else problem](https://en.wikipedia.org/wiki/Dangling_else)).\n\nSemantic assertions can be placed at any position in a regex. An assertion fails if it does not return `None`. In the context of an ordered choice this can be used to steer the parser depending on semantic or syntactic information. Otherwise its diagnostic will just be emitted and the parser continues.\n\nWhen the semantic action, predicate, or assertion is visited in a parse it will execute the rust code of the corresponding associated function in the `ParserCallbacks` trait implementation of the `Parser` type.\n\nThe index of actions, predicates, and assertions can be used multiple times in a rule.\n\n\u003e [!TIP]\n\u003e If the `ParserCallbacks` trait is implemented in the `parser.rs` file next to the grammar file (where it is generated by default), you can use the language server to jump from the grammar to the rust code.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e token A B C D;\n\u003e \n\u003e foo:\n\u003e   ?1 A #1 (?2 B C)* B #2\n\u003e | A !1 C #2\n\u003e / A D\n\u003e ;\n\u003e ```\n\u003e ```rust\n\u003e impl ParserCallbacks for Parser\u003c'_\u003e {\n\u003e     // ...\n\u003e     fn predicate_foo_1(\u0026self) -\u003e bool {\n\u003e         self.peek(1) == Token::B\n\u003e     }\n\u003e     fn predicate_foo_2(\u0026self) -\u003e bool {\n\u003e         self.context.some_condition\n\u003e     }\n\u003e     fn action_foo_1(\u0026mut self, diags: \u0026mut Vec\u003cDiagnostic\u003e) {\n\u003e         println!(\"executed action 1\");\n\u003e     }\n\u003e     fn action_foo_2(\u0026mut self, diags: \u0026mut Vec\u003cDiagnostic\u003e) {\n\u003e         println!(\"executed action 2\");\n\u003e     }\n\u003e     fn assertion_foo_1(\u0026self) -\u003e Option\u003cDiagnostic\u003e {\n\u003e         None\n\u003e     }\n\u003e }\n\u003e ```\n\n#### Node Rename\nThe syntax tree node for a rule can be renamed when a node rename operator is visited during a parse.\n\u003e **Example**\n\u003e ```antlr\n\u003e expr:\n\u003e   Int @int_expr\n\u003e | '(' expr ')' @paren_expr\n\u003e ;\n\u003e ```\n\u003e May result in the following syntax tree.\n\u003e ```\n\u003e paren_expr\n\u003e ├─ '('\n\u003e ├─ int_expr\n\u003e │  └─ Int\n\u003e └─ ')'\n\u003e ```\n\n\n#### Node Elision\nCreation of a syntax tree node can be prevented if a node elision operator is visited during a parse.\n\u003e **Example**\n\u003e ```antlr\n\u003e foo: A bar bar;\n\u003e bar: B ^ | C\n\u003e ```\n\u003e May result in the following syntax tree.\n\u003e ```\n\u003e foo\n\u003e ├─ A\n\u003e ├─ B\n\u003e └─ bar\n\u003e    └─ C\n\u003e ```\n\nUnconditional node elision can also be achieved with a special syntax by writing the operator directly after the rule name.\n\n\u003e [!TIP]\n\u003e This is useful for rules with a top level alternation, as it avoids an extra nesting with parentheses.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e foo: A bar bar;\n\u003e bar^: B | C\n\u003e ```\n\u003e May result in the following syntax tree.\n\u003e ```\n\u003e foo\n\u003e ├─ A\n\u003e ├─ B\n\u003e └─ C\n\u003e ```\n\nNode elision is not allowed in left recursive branches of a rule.\n\u003e **Example**\n\u003e ```antlr\n\u003e foo:\n\u003e   foo A ^ // ❌ error\n\u003e | B ^     // ✅ ok\n\u003e ;\n\u003e ```\n\n#### Node Marker and Creation\nA node marker can be defined in a regex, which marks the position where a syntax tree node can be inserted during a parse.\nThe index of such a node marker must be unique for each rule.\n\nA node creation can be used with a corresponding node marker, if it is placed in a position where the node marker will be visited before the node creation during a parse.\n\n\u003e [!WARNING]\n\u003e The current implementation uses insertion into a vector for node creation, which may impact performance, if many rules/tokens are parsed between node marker and creation.\n\u003e In the worst case this may result in quadratic parse time complexity.\n\u003e However in practice it does not seem to matter too much, as the insertions mostly happen close to the end of the vector.\n\u003e Nevertheless, in the future this will probably be changed to a more efficient implementation.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e foo: A \u003c1 B C 1\u003ebar;\n\u003e ```\n\u003e May result in the following syntax tree.\n\u003e ```\n\u003e foo\n\u003e ├─ A\n\u003e └─ bar\n\u003e    ├─ B\n\u003e    └─ C\n\u003e ```\n\nThe index and node name is optional for the node creation.\nIf the index is missing the behavior is as if a node marker at the start of the rule was used.\nIf the node name is missing the node name of the current rule is used.\n\n\u003e [!TIP]\n\u003e This is useful in combination with unconditional node elision, so a node can still be created for certain parses.\n\n\u003e **Example**\n\u003e ```antlr\n\u003e assign_expr^: bin_expr ['=' assign_expr \u003e];\n\u003e ```\n\u003e This is usually preferable to\n\u003e ```antlr\n\u003e assign_expr: bin_expr ('=' assign_expr | ^);\n\u003e ```\n\u003e as the latter will not elide a node if `bin_expr` is followed by an invalid token that is not in the follow set.\n\nA node creation with missing index is not allowed in left recursive rules.\n\u003e **Example**\n\u003e ```antlr\n\u003e foo:\n\u003e   foo A \u003e // ❌ error\n\u003e | B \u003e     // ❌ error\n\u003e ;\n\u003e ```\n\n## License\nLelwel, its examples, and its generated code are licensed under either of\n\n * Apache License, Version 2.0\n   ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)\n * MIT license\n   ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)\n\nat your option.\n\n## Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x2a-42%2Flelwel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0x2a-42%2Flelwel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x2a-42%2Flelwel/lists"}