├── .cargo └── config.toml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── book.yml │ ├── checks.yml │ └── update-rust-overlay.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── art ├── rustemo-logo-small.jpg └── rustemo-logo.jpg ├── check-update-all.sh ├── config.toml ├── default.nix ├── docs-header.html ├── docs ├── .gitignore ├── README.md ├── book.toml ├── build-latex-images.sh ├── default.nix ├── mdbook-admonish.css ├── src │ ├── SUMMARY.md │ ├── architecture.md │ ├── bootstrapping.md │ ├── builders.md │ ├── cli.md │ ├── components.md │ ├── configuration.md │ ├── contributing.md │ ├── docs_setup.md │ ├── grammar_language.md │ ├── handling_errors │ │ ├── handling_errors.md │ │ ├── if_then_else.rustemo │ │ ├── if_then_else_end.rustemo │ │ ├── if_then_else_shift.rustemo │ │ └── images │ │ │ ├── if-else-1.tex │ │ │ └── if-else-2.tex │ ├── images │ │ ├── ast.tex │ │ ├── calc.dot.png │ │ └── calc.rustemo │ ├── introduction.md │ ├── lexers.md │ ├── mdbook-plantuml-img │ │ └── 9e65b16f38683a44d16aea04a658fdc1c24886d4.svg │ ├── parsers.md │ ├── parsing │ │ ├── expressions │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ └── src │ │ │ │ ├── expressions.rustemo │ │ │ │ └── main.rs │ │ ├── images │ │ │ └── tree.tex │ │ └── parsing.md │ ├── readme_example │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── testglr │ │ │ ├── calc.dot.png │ │ │ ├── calc.rs │ │ │ ├── calc.rustemo │ │ │ ├── calc_actions.rs │ │ │ └── mod.rs │ │ │ └── testlr │ │ │ ├── calclr.rs │ │ │ ├── calclr.rustemo │ │ │ ├── calclr_actions.rs │ │ │ └── mod.rs │ ├── references.bib │ └── tutorials │ │ ├── calculator │ │ ├── calculator.md │ │ ├── calculator1 │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ ├── calculator.rs │ │ │ │ ├── calculator.rustemo │ │ │ │ ├── calculator1_1.ast │ │ │ │ ├── calculator1_1.err │ │ │ │ ├── calculator_actions.rs │ │ │ │ ├── main.rs │ │ │ │ └── tests.rs │ │ ├── calculator2 │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ ├── calculator-ambig.rustemo │ │ │ │ ├── calculator-ambig2.rustemo │ │ │ │ ├── calculator.rs │ │ │ │ ├── calculator.rustemo │ │ │ │ ├── calculator2_1.ast │ │ │ │ ├── calculator2_1.err │ │ │ │ ├── calculator_actions.rs │ │ │ │ ├── main.rs │ │ │ │ └── tests.rs │ │ ├── calculator3 │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ ├── calculator.rs │ │ │ │ ├── calculator.rustemo │ │ │ │ ├── calculator3_1.ast │ │ │ │ ├── calculator3_1.err │ │ │ │ ├── calculator_actions.rs │ │ │ │ ├── main.rs │ │ │ │ └── tests.rs │ │ ├── calculator4 │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ ├── calculator.rs │ │ │ │ ├── calculator.rustemo │ │ │ │ ├── calculator4_1.ast │ │ │ │ ├── calculator4_1.err │ │ │ │ ├── calculator_actions.rs │ │ │ │ ├── main.rs │ │ │ │ └── tests.rs │ │ └── calculator5 │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ ├── calculator.rs │ │ │ ├── calculator.rustemo │ │ │ ├── calculator5_1.err │ │ │ ├── calculator_actions.rs │ │ │ ├── main.rs │ │ │ └── tests.rs │ │ └── index.md └── theme │ ├── css │ ├── chrome.css │ ├── general.css │ └── variables.css │ ├── index.hbs │ ├── pagetoc.css │ └── pagetoc.js ├── examples ├── calculator │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ ├── ast_actions │ │ ├── README.md │ │ ├── calculator01.ast │ │ ├── calculator01.rustemo │ │ ├── calculator01_actions.rs │ │ ├── calculator02_ambig.ast │ │ ├── calculator02_ambig.rustemo │ │ ├── calculator02_ambig_actions.rs │ │ ├── calculator03_ambig_prodkind.ast │ │ ├── calculator03_ambig_prodkind.rustemo │ │ ├── calculator03_ambig_prodkind_actions.rs │ │ ├── calculator04_ambig_lhs.ast │ │ ├── calculator04_ambig_lhs.rustemo │ │ ├── calculator04_ambig_lhs_actions.rs │ │ ├── mod.rs │ │ └── tests.rs │ │ ├── calc_actions │ │ ├── README.md │ │ ├── calculator01.err │ │ ├── calculator01.rustemo │ │ ├── calculator01_actions.rs │ │ ├── calculator02_ambig.rustemo │ │ ├── calculator02_ambig_actions.rs │ │ ├── calculator03_ambig_prodkind.rustemo │ │ ├── calculator03_ambig_prodkind_actions.rs │ │ ├── calculator04_ambig_lhs.rustemo │ │ ├── calculator04_ambig_lhs_actions.rs │ │ ├── mod.rs │ │ └── tests.rs │ │ └── main.rs └── json │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── README.md │ ├── example1.ast │ ├── example1.json │ ├── example2.ast │ ├── example2.json │ ├── example3.ast │ ├── example3.json │ ├── example4.ast │ ├── example4.json │ ├── example5.ast │ ├── example5.json │ ├── json.rustemo │ ├── json_actions.rs │ └── main.rs ├── flake.lock ├── flake.nix ├── gen-docs.sh ├── rustemo-compiler ├── Cargo.toml ├── build.rs └── src │ ├── error.rs │ ├── generator │ ├── actions │ │ ├── mod.rs │ │ └── production.rs │ ├── arrays.rs │ ├── base.rs │ ├── functions.rs │ └── mod.rs │ ├── grammar │ ├── builder.rs │ ├── mod.rs │ ├── tests │ │ ├── create_terminals_multiple.expected │ │ ├── invalid_names_1.err │ │ ├── invalid_names_2.err │ │ ├── invalid_names_3.err │ │ ├── mod.rs │ │ ├── productions_meta_data.expected │ │ ├── regex_sugar_one_or_more.expected │ │ ├── regex_sugar_optional.expected │ │ ├── regex_sugar_zero_or_more.expected │ │ ├── terminals_regex.expected │ │ └── unreachable_rules.expected │ └── types │ │ ├── mod.rs │ │ ├── symbols_type_deduction.expected │ │ └── tests.rs │ ├── index.rs │ ├── lang │ ├── mod.rs │ ├── rustemo.ast │ ├── rustemo.rs │ ├── rustemo.rustemo │ ├── rustemo_actions.rs │ └── tests.rs │ ├── lib.rs │ ├── main.rs │ ├── settings.rs │ ├── table │ ├── grammar_2.expected │ ├── grammar_nonlalr_lalr.expected │ ├── grammar_nonlalr_lalr_pagerw.expected │ └── mod.rs │ └── utils.rs ├── rustemo ├── Cargo.toml └── src │ ├── builder.rs │ ├── common.rs │ ├── context.rs │ ├── debug.rs │ ├── error.rs │ ├── glr │ ├── gss.rs │ ├── mod.rs │ └── parser.rs │ ├── input.rs │ ├── lexer.rs │ ├── lib.rs │ ├── location.rs │ ├── lr │ ├── builder.rs │ ├── context.rs │ ├── mod.rs │ └── parser.rs │ ├── parser.rs │ └── utils.rs ├── rustfmt.toml └── tests ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs └── src ├── ambiguity ├── mod.rs ├── no_prio_assoc_invalid.err ├── no_prio_assoc_invalid.rustemo ├── prio_assoc_prod.rustemo ├── prio_assoc_term.rustemo ├── reduce_empty_1.ast ├── reduce_empty_1.rustemo ├── reduce_empty_2.ast └── reduce_empty_2.rustemo ├── builder ├── custom_builder │ ├── custom_builder.rustemo │ ├── custom_builder_builder.rs │ └── mod.rs ├── generic_tree │ ├── generic_tree.ast │ ├── generic_tree.rustemo │ └── mod.rs ├── loc_info │ ├── json.rustemo │ ├── loc_info.ast │ ├── loc_info.json │ └── mod.rs ├── mod.rs └── use_context │ ├── mod.rs │ ├── use_context.ast │ ├── use_context.rustemo │ └── use_context_actions.rs ├── errors ├── infinite_recursion │ ├── infinite_recursion.err │ ├── infinite_recursion.rustemo │ ├── infinite_recursion_2.err │ ├── infinite_recursion_2.rustemo │ └── mod.rs ├── mod.rs ├── recognizer_not_defined │ ├── mod.rs │ ├── recognizer_not_defined.err │ └── recognizer_not_defined.rustemo ├── syntax_errors │ ├── calc.rustemo │ ├── calc_incomplete.err │ ├── calc_unexpected.err │ └── mod.rs ├── terminal_not_defined │ ├── mod.rs │ ├── terminal_not_defined.err │ ├── terminal_not_defined.rustemo │ ├── terminal_not_defined_sugar.err │ └── terminal_not_defined_sugar.rustemo └── unexisting_symbol │ ├── mod.rs │ ├── unexisting.rustemo │ └── unexisting_symbol.err ├── fancy_regex ├── fancy_regex.ast ├── fancy_regex.rustemo └── mod.rs ├── from_file ├── calculator.rustemo ├── input1.calc ├── input2.calc ├── mod.rs ├── parse_from_file_err.err └── parse_from_file_ok.ast ├── glr ├── build │ ├── basic │ │ ├── calc.rustemo │ │ ├── mod.rs │ │ ├── tree_build_default_1.ast │ │ ├── tree_build_default_2.ast │ │ ├── tree_build_generic_1.ast │ │ └── tree_build_generic_2.ast │ ├── loc_info │ │ ├── json.rustemo │ │ ├── loc_info_forest.ast │ │ └── mod.rs │ └── mod.rs ├── errors │ ├── calc.rustemo │ ├── calc_incomplete.err │ ├── calc_missing.err │ ├── calc_unexpected.err │ └── mod.rs ├── evaluate │ ├── calc.rustemo │ ├── calc_actions.rs │ ├── calc_eval.rustemo │ ├── calc_eval_actions.rs │ ├── forest_eval.ast │ └── mod.rs ├── forest │ ├── calc.ast │ ├── calc.rustemo │ ├── forest_into_iter.ast │ ├── forest_iter.ast │ ├── forest_tree_17.ast │ ├── forest_tree_children.ast │ ├── forest_tree_first.ast │ ├── forest_tree_last.ast │ └── mod.rs ├── lexical_ambiguity │ ├── grammar_order │ │ ├── grammar_order.ast │ │ ├── grammar_order.rustemo │ │ └── mod.rs │ ├── grammar_order_off │ │ ├── grammar_order.ast │ │ ├── grammar_order.rustemo │ │ └── mod.rs │ ├── longest_match │ │ ├── longest_match.ast │ │ ├── longest_match.rustemo │ │ └── mod.rs │ ├── longest_match_off │ │ ├── longest_match.ast │ │ ├── longest_match.rustemo │ │ └── mod.rs │ ├── mod.rs │ ├── most_specific │ │ ├── mod.rs │ │ ├── most_specific.ast │ │ └── most_specific.rustemo │ ├── most_specific_off │ │ ├── mod.rs │ │ ├── most_specific.ast │ │ └── most_specific.rustemo │ └── priorities │ │ ├── mod.rs │ │ ├── priorities.ast │ │ ├── priorities.rustemo │ │ ├── priorities_same.ast │ │ └── priorities_same.rustemo ├── mod.rs ├── regressions │ ├── issue_16_subtract_overflow_panic │ │ ├── inline.rustemo │ │ ├── mod.rs │ │ └── result.ast │ ├── issue_22_panic_get_conflicts │ │ ├── mod.rs │ │ ├── result.ast │ │ └── unreach.rustemo │ └── mod.rs └── special │ ├── bounded_ambiguity │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ └── tree_2.ast │ ├── bounded_direct_ambiguity │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ ├── tree_2.ast │ ├── tree_3.ast │ ├── tree_4.ast │ └── tree_5.ast │ ├── cyclic_1 │ ├── lang.rustemo │ └── mod.rs │ ├── cyclic_2 │ ├── lang.rustemo │ └── mod.rs │ ├── farshi_g7 │ ├── lang.rustemo │ ├── mod.rs │ └── tree.ast │ ├── farshi_g8 │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ ├── tree_2.ast │ ├── tree_3.ast │ ├── tree_4.ast │ ├── tree_5.ast │ ├── tree_6.ast │ ├── tree_7.ast │ └── tree_8.ast │ ├── highly_ambiguous │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ ├── tree_10.ast │ ├── tree_2.ast │ ├── tree_3.ast │ ├── tree_4.ast │ ├── tree_5.ast │ ├── tree_6.ast │ ├── tree_7.ast │ ├── tree_8.ast │ └── tree_9.ast │ ├── knuth_lr1 │ ├── lang.rustemo │ └── mod.rs │ ├── mod.rs │ ├── nondeterministic_palindromes │ ├── lang.rustemo │ ├── mod.rs │ └── tree.ast │ ├── reduce_enough_empty │ ├── lang.rustemo │ ├── mod.rs │ └── tree.ast │ ├── reduce_enough_many_empty │ ├── lang.rustemo │ ├── mod.rs │ └── tree.ast │ ├── right_nullable │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ └── tree_2.ast │ └── unbounded_ambiguity │ ├── lang.rustemo │ ├── mod.rs │ ├── tree_1.ast │ ├── tree_2.ast │ ├── tree_3.ast │ ├── tree_4.ast │ └── tree_5.ast ├── layout ├── ast │ ├── layout.ast │ ├── layout.rustemo │ └── mod.rs ├── generic_tree │ ├── layout.ast │ ├── layout.rustemo │ └── mod.rs └── mod.rs ├── lexer ├── custom_lexer │ ├── README.md │ ├── custom_lexer.bytes │ ├── custom_lexer_1.ast │ ├── custom_lexer_1.rustemo │ ├── custom_lexer_1_actions.rs │ ├── custom_lexer_1_lexer.rs │ ├── custom_lexer_2.ast │ ├── custom_lexer_2.rustemo │ ├── custom_lexer_2_actions.rs │ ├── custom_lexer_2_lexer.rs │ └── mod.rs └── mod.rs ├── lexical_ambiguity ├── grammar_order │ ├── grammar_order.ast │ ├── grammar_order.rustemo │ └── mod.rs ├── longest_match │ ├── longest_match.ast │ ├── longest_match.rustemo │ └── mod.rs ├── mod.rs ├── most_specific │ ├── mod.rs │ ├── most_specific.ast │ └── most_specific.rustemo ├── most_specific_off │ ├── mod.rs │ ├── most_specific.ast │ └── most_specific.rustemo └── priorities │ ├── mod.rs │ ├── priorities.ast │ ├── priorities.rustemo │ ├── priorities_same.ast │ └── priorities_same.rustemo ├── lr.rs ├── output_dir ├── README.md ├── mod.rs ├── output_dir.ast ├── output_dir.rs ├── output_dir.rustemo ├── output_dir_act.ast ├── output_dir_act.rustemo ├── output_dir_act_actions.rs └── output_dir_actions.rs ├── partial ├── mod.rs ├── partial.ast └── partial.rustemo ├── rule_patterns ├── mod.rs ├── one_or_more.ast ├── one_or_more.rustemo ├── optional.ast ├── optional.rustemo ├── zero_or_more_1.ast ├── zero_or_more_1.rustemo ├── zero_or_more_2.ast └── zero_or_more_2.rustemo ├── special ├── denny_pager_menhir │ └── denny.rustemo ├── lalr_reduce_reduce_conflict │ ├── lang.rustemo │ ├── mod.rs │ └── tree.ast ├── lalrpop768 │ ├── lalrpop768.ast │ ├── lalrpop768.rustemo │ └── mod.rs ├── mod.rs ├── nondeterministic_palindromes │ ├── lang.rustemo │ ├── message.err │ └── mod.rs └── pager_g1 │ ├── mod.rs │ ├── pager_g1.ast │ └── pager_g1.rustemo ├── sugar ├── mod.rs ├── one_or_more │ ├── mod.rs │ ├── one_or_more_1.rustemo │ ├── one_or_more_1_1.ast │ ├── one_or_more_1_1.err │ ├── one_or_more_1_1_sep.ast │ ├── one_or_more_1_1_sep.err │ ├── one_or_more_1_2.ast │ ├── one_or_more_1_2.err │ ├── one_or_more_1_3.err │ ├── one_or_more_1_sep.rustemo │ ├── one_or_more_2.rustemo │ ├── one_or_more_2_1.ast │ ├── one_or_more_2_1.err │ ├── one_or_more_2_2.ast │ ├── one_or_more_2_2.err │ └── one_or_more_2_3.err ├── optional │ ├── mod.rs │ ├── optional_1.rustemo │ ├── optional_1_1.ast │ ├── optional_1_1.err │ ├── optional_1_2.ast │ ├── optional_1_2.err │ ├── optional_1_3.ast │ ├── optional_2.rustemo │ ├── optional_2_1.ast │ ├── optional_2_1.err │ ├── optional_2_2.ast │ ├── optional_2_2.err │ └── optional_2_3.ast └── zero_or_more │ ├── mod.rs │ ├── zero_or_more_1.rustemo │ ├── zero_or_more_1_1.ast │ ├── zero_or_more_1_1.err │ ├── zero_or_more_1_1_sep.ast │ ├── zero_or_more_1_1_sep.err │ ├── zero_or_more_1_2.ast │ ├── zero_or_more_1_2.err │ ├── zero_or_more_1_3.ast │ ├── zero_or_more_1_sep.rustemo │ ├── zero_or_more_2.rustemo │ ├── zero_or_more_2_1.ast │ ├── zero_or_more_2_1.err │ ├── zero_or_more_2_2.ast │ └── zero_or_more_2_2.err └── unicode ├── mod.rs ├── unicode.ast └── unicode.rustemo /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # A workaround until CARGO_WORKSPACE_DIR is implemented 2 | # See: https://github.com/rust-lang/cargo/issues/3946 3 | # And: https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993 4 | 5 | [env] 6 | CARGO_WORKSPACE_DIR = { value = "", relative = true } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | 17 | 18 | **Reproduction** 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Question 3 | url: https://github.com/igordejanovic/rustemo/discussions 4 | about: Questions about Rustemo should be posted as a GitHub discussion. 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Motivation 2 | 6 | 7 | ## Checklist 8 | 12 | - [ ] added tests to verify new behavior 13 | - [ ] updated documentation if applicable. 14 | - [ ] updated `CHANGELOG.md` 15 | -------------------------------------------------------------------------------- /.github/workflows/book.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Rustemo book to Pages 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | paths: 7 | - 'docs/**' 8 | - '.github/workflows/book.yml' 9 | 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | # Allow one concurrent deployment 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: true 17 | 18 | 19 | 20 | jobs: 21 | # Single deploy job since we're just deploying 22 | deploy-book: 23 | runs-on: ubuntu-latest 24 | 25 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 26 | permissions: 27 | pages: write # to deploy to Pages 28 | id-token: write # to verify the deployment originates from an appropriate source 29 | 30 | environment: 31 | name: github-pages 32 | url: ${{ steps.deployment.outputs.page_url }} 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | submodules: true 37 | 38 | - uses: cachix/install-nix-action@v27 39 | with: 40 | nix_path: nixpkgs=channel:nixos-unstable 41 | - uses: DeterminateSystems/magic-nix-cache-action@v6 42 | 43 | - name: Build book 44 | run: nix build .#book 45 | 46 | - name: Setup Pages 47 | uses: actions/configure-pages@v5 48 | - name: Upload artifact 49 | uses: actions/upload-pages-artifact@v3 50 | with: 51 | path: 'result/html' 52 | - name: Deploy to GitHub Pages 53 | id: deployment 54 | uses: actions/deploy-pages@v4 55 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: ["main"] 9 | merge_group: 10 | workflow_run: 11 | workflows: ["Update rust-overlay"] 12 | types: 13 | - completed 14 | 15 | concurrency: 16 | group: ${{ github.ref }} 17 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} 18 | 19 | permissions: 20 | contents: read 21 | 22 | jobs: 23 | check: 24 | runs-on: ubuntu-latest 25 | env: 26 | TARGET_BRANCH: ${{ github.ref }} 27 | 28 | strategy: 29 | matrix: 30 | rust: 31 | - base 32 | - stable 33 | - beta 34 | - nightly 35 | 36 | steps: 37 | - name: Switch to rust-overlay branch if triggered by update 38 | if: ${{ github.event_name == 'workflow_run' }} 39 | run: echo "TARGET_BRANCH=rust-overlay" >> $GITHUB_ENV 40 | 41 | - uses: actions/checkout@v4 42 | with: 43 | ref: ${{ env.TARGET_BRANCH }} 44 | 45 | - uses: cachix/install-nix-action@v31 46 | with: 47 | nix_path: nixpkgs=channel:nixos-unstable 48 | 49 | - name: Run checks 50 | run: nix build .#checks.x86_64-linux.${{ matrix.rust }} 51 | -------------------------------------------------------------------------------- /.github/workflows/update-rust-overlay.yml: -------------------------------------------------------------------------------- 1 | name: Update rust-overlay 2 | on: 3 | schedule: 4 | - cron: "0 3 * * *" # Every day 03:00 UTC 5 | jobs: 6 | update: 7 | permissions: 8 | contents: write 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | ref: 'rust-overlay' 14 | fetch-depth: 0 15 | - uses: cachix/install-nix-action@v27 16 | with: 17 | nix_path: nixpkgs=channel:nixos-unstable 18 | - run: nix flake update rust-overlay 19 | - name: Commit changes 20 | id: commit 21 | continue-on-error: true 22 | run: | 23 | git config user.name "Github Action" 24 | git config user.email "action@github.com" 25 | git add flake.lock 26 | git commit --amend --reset-author -m "build: update rust-overlay" 27 | git rebase origin/main 28 | - name: Push changes 29 | if: steps.commit.outcome == 'success' 30 | uses: ad-m/github-push-action@master 31 | with: 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | branch: rust-overlay 34 | force_with_lease: true 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | 8 | 9 | /.gdb_history 10 | 11 | # Bootstrapping files 12 | /rustemo/src/lang/*_bootstrap.rs 13 | /CONTRIBUTING.html 14 | 15 | # Nix 16 | result 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | docs/src/contributing.md -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Igor Dejanović 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /art/rustemo-logo-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igordejanovic/rustemo/3e59f9db663ee2bb12929435a5ecf6973c34c0ed/art/rustemo-logo-small.jpg -------------------------------------------------------------------------------- /art/rustemo-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igordejanovic/rustemo/3e59f9db663ee2bb12929435a5ecf6973c34c0ed/art/rustemo-logo.jpg -------------------------------------------------------------------------------- /check-update-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e -u 3 | 4 | export RUST_BACKTRACE=1 5 | export CARGO_INCREMENTAL=1 6 | export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse 7 | 8 | # Test linter issues first 9 | cargo clippy --all --all-targets -- -D warnings 10 | 11 | # We must first test and install compiler itself 12 | cargo nextest run -p rustemo-compiler 13 | cargo install --path rustemo-compiler --debug 14 | 15 | # README Examples 16 | rcomp docs/src/readme_example/src/testlr/calclr.rustemo 17 | rcomp --parser-algo glr docs/src/readme_example/src/testglr/calc.rustemo 18 | 19 | cd docs/src/tutorials/calculator/ 20 | for i in {1..5}; do 21 | rcomp calculator$i/src/calculator.rustemo; 22 | done 23 | 24 | cd - 25 | 26 | # Default test 27 | cargo nextest run 28 | 29 | # Test with array-based table generator 30 | cargo nextest run --features arrays 31 | 32 | cargo fmt --all 33 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | docp = "doc --document-private-items" 3 | doca = "doc --all-features" 4 | docpa = "doc --all-features --document-private-items" 5 | -------------------------------------------------------------------------------- /docs-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | result 3 | *.generated.svg 4 | *mdbook-plantuml-img* 5 | CONTRIBUTING.html 6 | **/.auctex-auto/ 7 | index.html 8 | 9 | src/handling_errors/images/* 10 | !src/handling_errors/images/*.tex 11 | src/images/ast.* 12 | !src/images/ast.tex 13 | src/parsing/images/tree.* 14 | !src/parsing/images/tree.tex 15 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | See [src/docs_setup.md](src/docs_setup.md). 2 | -------------------------------------------------------------------------------- /docs/build-latex-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Creates images from LaTeX files. 3 | 4 | # Run only for html renderer 5 | if [ $2 != 'html' ]; then 6 | exit 1 7 | fi 8 | 9 | while IFS= read -d '' -r file ; do 10 | printf 'File found: %s\n' "$file" 11 | pdflatex -output-directory=$(dirname $file) $file 2>&1 >> ${file%.tex}.log 12 | pdftoppm ${file%tex}pdf ${file%.tex} -png -singlefile 13 | #convert -density 200 -alpha deactivate ${file%.tex}.pdf ${file%.tex}.png 14 | # rm ${file%.tex}.{log,pdf,aux} 15 | done < <(find . -iname '*.tex' -print0) 16 | 17 | # This is required for the script to be called as mdbook preprocessor 18 | # as we don't wan't actually to process md files. 19 | exit 1 20 | -------------------------------------------------------------------------------- /docs/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, crane, mdbook-theme }: 2 | let 3 | inherit (pkgs) stdenv lib; 4 | craneLib = crane.mkLib pkgs; 5 | 6 | mdbook-theme-pkg = pkgs.rustPlatform.buildRustPackage { 7 | pname = "mdbook-theme"; 8 | version = "0.1.4"; 9 | cargoLock.lockFile = mdbook-theme.outPath + "/Cargo.lock"; 10 | src = mdbook-theme; 11 | }; 12 | 13 | tex = pkgs.texlive.combine { 14 | inherit (pkgs.texlive) scheme-small standalone qtree pict2e preview; 15 | }; 16 | 17 | buildInputs = with pkgs; [ 18 | wget git bash 19 | mdbook mdbook-admonish mdbook-plantuml 20 | mdbook-graphviz mdbook-theme-pkg mdbook-linkcheck 21 | plantuml graphviz tex poppler_utils]; 22 | 23 | docsFileTypes = [ ".md" ".rustemo" ".err" ".ast" ".tex" ".png" ".css" ".js" ".sh"]; 24 | docsFilter = path: _type: builtins.any (ext: lib.hasSuffix ext path) docsFileTypes; 25 | docsOrCargoFilter = path: type: 26 | (docsFilter path type) || (craneLib.filterCargoSources path type); 27 | 28 | book = stdenv.mkDerivation { 29 | name = "rustemo-book"; 30 | src = lib.cleanSourceWith { 31 | src = ../.; 32 | filter = docsOrCargoFilter; 33 | }; 34 | inherit buildInputs; 35 | 36 | buildPhase = '' 37 | mdbook build docs 38 | ''; 39 | installPhase = '' 40 | mv docs/book $out 41 | ''; 42 | }; 43 | in 44 | { 45 | inherit buildInputs; 46 | packages = { inherit book; }; 47 | } 48 | -------------------------------------------------------------------------------- /docs/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](introduction.md) 4 | - [Grammar language](grammar_language.md) 5 | - [Parsing](parsing/parsing.md) 6 | - [Configuration](configuration.md) 7 | - [Components](components.md) 8 | - [Lexers](lexers.md) 9 | - [Parsers](parsers.md) 10 | - [Builders](builders.md) 11 | - [CLI](cli.md) 12 | - [Handling errors](handling_errors/handling_errors.md) 13 | - [Tutorials](./tutorials/index.md) 14 | - [Calculator](./tutorials/calculator/calculator.md) 15 | - [Contributing](contributing.md) 16 | - [Bootstrapping](bootstrapping.md) 17 | - [Architecture](architecture.md) 18 | - [Docs setup](docs_setup.md) 19 | -------------------------------------------------------------------------------- /docs/src/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | This Chapter describes the main building block of every Rustemo parser, namely 4 | lexer, parser, and builder. 5 | -------------------------------------------------------------------------------- /docs/src/handling_errors/if_then_else.rustemo: -------------------------------------------------------------------------------- 1 | IfStatement: 'if' Condition 'then' Statements 2 | | 'if' Condition 'then' Statements 'else' Statements; 3 | Statements: Statements Statement | Statement | EMPTY; 4 | Statement: IfStatement; 5 | 6 | terminals 7 | If: 'if'; 8 | Then: 'then'; 9 | Else: 'else'; 10 | Condition: 'cond'; 11 | -------------------------------------------------------------------------------- /docs/src/handling_errors/if_then_else_end.rustemo: -------------------------------------------------------------------------------- 1 | IfStatement: 'if' Condition 'then' Statements 'end' 2 | | 'if' Condition 'then' Statements 'else' Statements 'end'; 3 | Statements: Statements Statement | Statement | EMPTY; 4 | Statement: IfStatement; 5 | 6 | terminals 7 | If: 'if'; 8 | Then: 'then'; 9 | Else: 'else'; 10 | End: 'end'; 11 | Condition: 'cond'; 12 | -------------------------------------------------------------------------------- /docs/src/handling_errors/if_then_else_shift.rustemo: -------------------------------------------------------------------------------- 1 | IfStatement: 'if' Condition 'then' Statements 2 | | 'if' Condition 'then' Statements 'else' Statements; 3 | Statements: Statements Statement | Statement | EMPTY; 4 | Statement: IfStatement; 5 | 6 | terminals 7 | If: 'if' {shift}; 8 | Then: 'then'; 9 | Else: 'else' {shift}; 10 | Condition: 'cond'; 11 | -------------------------------------------------------------------------------- /docs/src/handling_errors/images/if-else-1.tex: -------------------------------------------------------------------------------- 1 | \documentclass[preview]{standalone} 2 | \usepackage{qtree} 3 | \begin{document} 4 | 5 | \Tree[.if (cond) 6 | [.then 7 | [.if (cond) 8 | [.then EMPTY ] 9 | [.else EMPTY ]]]] 10 | \end{document} 11 | -------------------------------------------------------------------------------- /docs/src/handling_errors/images/if-else-2.tex: -------------------------------------------------------------------------------- 1 | \documentclass[preview]{standalone} 2 | \usepackage{qtree} 3 | \begin{document} 4 | 5 | \Tree[.if (cond) 6 | [.then 7 | [.if (cond) 8 | [.then EMPTY ]]] 9 | [.else EMPTY ]] 10 | \end{document} 11 | -------------------------------------------------------------------------------- /docs/src/images/ast.tex: -------------------------------------------------------------------------------- 1 | \documentclass[preview]{standalone} 2 | \usepackage{qtree} 3 | \begin{document} 4 | 5 | \Tree[.Add 6 | [.3 ] 7 | [.Mul 8 | [.2 ] 9 | [.5 ]]] 10 | \end{document} 11 | -------------------------------------------------------------------------------- /docs/src/images/calc.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igordejanovic/rustemo/3e59f9db663ee2bb12929435a5ecf6973c34c0ed/docs/src/images/calc.dot.png -------------------------------------------------------------------------------- /docs/src/images/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '-' E 3 | | num; 4 | 5 | terminals 6 | Plus: '+'; 7 | Minus: '-'; 8 | num: /\d+/; 9 | -------------------------------------------------------------------------------- /docs/src/parsing/expressions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expressions" 3 | version = "0.1.0" 4 | authors = ["Igor Dejanović "] 5 | build = "build.rs" 6 | edition = "2021" 7 | workspace = "../../../.." 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | rustemo = { path = "../../../../rustemo" } 13 | 14 | [build-dependencies] 15 | rustemo-compiler = { path = "../../../../rustemo-compiler" } 16 | -------------------------------------------------------------------------------- /docs/src/parsing/expressions/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use rustemo_compiler::BuilderType; 4 | 5 | fn main() { 6 | if let Err(e) = rustemo_compiler::Settings::new() 7 | .builder_type(BuilderType::Generic) 8 | .process_dir() 9 | { 10 | eprintln!("{e}"); 11 | exit(1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/src/parsing/expressions/src/expressions.rustemo: -------------------------------------------------------------------------------- 1 | Expression: Add | Num; 2 | Add: Expression '+' Expression {left}; 3 | terminals 4 | Num: /\d+/; 5 | Plus: '+'; 6 | -------------------------------------------------------------------------------- /docs/src/parsing/expressions/src/main.rs: -------------------------------------------------------------------------------- 1 | use expressions::ExpressionsParser; 2 | use rustemo::rustemo_mod; 3 | use rustemo::Parser; 4 | 5 | rustemo_mod! {#[allow(unused_imports)] 6 | pub(crate) expressions, "/src"} 7 | 8 | fn main() { 9 | println!("{:#?}", ExpressionsParser::new().parse("3 + 2 + 1")); 10 | } 11 | -------------------------------------------------------------------------------- /docs/src/parsing/images/tree.tex: -------------------------------------------------------------------------------- 1 | \documentclass[preview]{standalone} 2 | \usepackage{qtree} 3 | \begin{document} 4 | 5 | \Tree[.Expression 6 | [.Add 7 | [.Expression 8 | [.Add 9 | [.Expression Num(3) ] 10 | [.Plus(+) ] 11 | [.Expression Num(2) ]]] 12 | [.Plus(+) ] 13 | [.Expression Num(1) ]]] 14 | 15 | \end{document} 16 | -------------------------------------------------------------------------------- /docs/src/readme_example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "readme_example" 3 | repository.workspace = true 4 | authors.workspace = true 5 | keywords.workspace = true 6 | categories.workspace = true 7 | license.workspace = true 8 | version.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rustemo = { path = "../../../rustemo" } 16 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod testglr; 3 | #[cfg(test)] 4 | mod testlr; 5 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testglr/calc.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igordejanovic/rustemo/3e59f9db663ee2bb12929435a5ecf6973c34c0ed/docs/src/readme_example/src/testglr/calc.dot.png -------------------------------------------------------------------------------- /docs/src/readme_example/src/testglr/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '*' E 3 | | Number 4 | ; 5 | 6 | terminals 7 | Number: /\d+/; 8 | Add: '+'; 9 | Mul: '*'; 10 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testglr/calc_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calc::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as RustemoToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = RustemoToken<'i, Input, TokenKind>; 9 | pub type E = i32; 10 | pub type Number = i32; 11 | pub fn number(_ctx: &Ctx, token: Token) -> Number { 12 | token.value.parse().unwrap() 13 | } 14 | pub fn e_c1(_ctx: &Ctx, e_1: E, e_3: E) -> E { 15 | e_1 + e_3 16 | } 17 | pub fn e_c2(_ctx: &Ctx, e_1: E, e_3: E) -> E { 18 | e_1 * e_3 19 | } 20 | pub fn e_number(_ctx: &Ctx, number: Number) -> E { 21 | number 22 | } 23 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testglr/mod.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | mod calc; 3 | mod calc_actions; 4 | use self::calc::{CalcParser, DefaultBuilder}; 5 | use rustemo::Parser; 6 | 7 | #[test] 8 | fn test_glr() { 9 | let forest = CalcParser::new().parse("2 + 3 * 4 + 1").unwrap(); 10 | 11 | // We have 5 possible solutions, see https://en.wikipedia.org/wiki/Catalan_number 12 | assert_eq!(forest.solutions(), 5); 13 | 14 | // Evaluate each tree from the forest 15 | let results = forest 16 | .into_iter() 17 | .map(|tree| { 18 | let mut builder = DefaultBuilder::new(); 19 | tree.build(&mut builder) 20 | }) 21 | .collect::>(); 22 | 23 | assert_eq!(results, vec![21, 15, 25, 15, 17]); 24 | } 25 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testlr/calclr.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {left, 1} 2 | | E '*' E {left, 2} 3 | | Number 4 | ; 5 | 6 | terminals 7 | Number: /\d+/; 8 | Add: '+'; 9 | Mul: '*'; 10 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testlr/calclr_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calclr::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as RustemoToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = RustemoToken<'i, Input, TokenKind>; 9 | pub type E = i32; 10 | pub type Number = i32; 11 | pub fn number(_ctx: &Ctx, token: Token) -> Number { 12 | token.value.parse().unwrap() 13 | } 14 | pub fn e_c1(_ctx: &Ctx, e_1: E, e_3: E) -> E { 15 | e_1 + e_3 16 | } 17 | pub fn e_c2(_ctx: &Ctx, e_1: E, e_3: E) -> E { 18 | e_1 * e_3 19 | } 20 | pub fn e_number(_ctx: &Ctx, number: Number) -> E { 21 | number 22 | } 23 | -------------------------------------------------------------------------------- /docs/src/readme_example/src/testlr/mod.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | mod calclr; 3 | mod calclr_actions; 4 | use self::calclr::CalclrParser; 5 | use rustemo::Parser; 6 | 7 | #[test] 8 | fn test_lr() { 9 | let result = CalclrParser::new().parse("2 + 3 * 4 + 1").unwrap(); 10 | 11 | // As the parsing is deterministic now we have just 1 solution which is 12 | // automatically evaluated using the default builder and provided actions 13 | assert_eq!(result, 15); 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/references.bib: -------------------------------------------------------------------------------- 1 | @book{grune2007, 2 | title = {Parsing techniques: a practical guide}, 3 | author = {Grune, Dick and Ceriel J. H., Jacobs}, 4 | year = {2007}, 5 | edition = {2nd}, 6 | isbn = {9780387202488,9780387689548,038720248X}, 7 | publisher = {Springer-Verlag New York Inc}, 8 | series = {Monographs in Computer Science} 9 | } 10 | 11 | @book{aho2006, 12 | title = {Compilers: Principles, Techniques, and Tools (2nd Edition)}, 13 | author = {Aho, Alfred V. and Lam, Monica S. and Sethi, Ravi and Ullman, Jeffrey D.}, 14 | year = {2006}, 15 | citeulike-article-id = {1033375}, 16 | description = {CiteULike: Everyone's library}, 17 | howpublished = {Hardcover}, 18 | isbn = {0321486811}, 19 | month = {August}, 20 | publisher = {{Addison Wesley}}, 21 | } 22 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "calculator" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | "rustemo", 12 | ] 13 | 14 | [[package]] 15 | name = "lazy_static" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 19 | 20 | [[package]] 21 | name = "regex" 22 | version = "1.7.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 25 | dependencies = [ 26 | "regex-syntax", 27 | ] 28 | 29 | [[package]] 30 | name = "regex-syntax" 31 | version = "0.6.28" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 34 | 35 | [[package]] 36 | name = "rustemo" 37 | version = "0.1.0" 38 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/Cargo.toml: -------------------------------------------------------------------------------- 1 | # ANCHOR: tutorial 2 | [package] 3 | name = "calculator1" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | # A relative path to rustemo crate is used here for usage in the rustemo project tree. 11 | # In your projects you should just specify the version. 12 | rustemo = { version = "0.7", path = "../../../../../rustemo" } 13 | # ANCHOR_END: tutorial 14 | 15 | [dev-dependencies] 16 | # For output_cmp for testing 17 | rustemo-compiler = { path = "../../../../../rustemo-compiler" } 18 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/calculator.rustemo: -------------------------------------------------------------------------------- 1 | Expression: Operand Operator Operand; 2 | 3 | terminals 4 | Operand: /\d+(\.\d+)?/; 5 | Operator: /\+|-|\*|\//; 6 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/calculator1_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Expression { 3 | operand_1: "2", 4 | operator: "+", 5 | operand_3: "3", 6 | }, 7 | ) -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/calculator1_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,2]: 2 | ...2 -->3... 3 | Expected Operator. -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/calculator_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use ::rustemo::{Context, Token as BaseToken}; 4 | use super::calculator::{self, TokenKind}; 5 | pub type Input = str; 6 | pub type Ctx<'i> = super::calculator::Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 9 | pub type Operand = String; 10 | pub fn operand(_ctx: &Ctx, token: Token) -> Operand { 11 | token.value.into() 12 | } 13 | pub type Operator = String; 14 | pub fn operator(_ctx: &Ctx, token: Token) -> Operator { 15 | token.value.into() 16 | } 17 | #[derive(Debug, Clone)] 18 | pub struct Expression { 19 | pub operand_1: Operand, 20 | pub operator: Operator, 21 | pub operand_3: Operand, 22 | } 23 | pub fn expression_c1( 24 | _ctx: &Ctx, 25 | operand_1: Operand, 26 | operator: Operator, 27 | operand_3: Operand, 28 | ) -> Expression { 29 | Expression { 30 | operand_1, 31 | operator, 32 | operand_3, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/main.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: header 2 | use rustemo::Parser; 3 | use std::io; 4 | // Use the generated parser 5 | use crate::calculator::CalculatorParser; 6 | 7 | // Include generated modules 8 | #[rustfmt::skip] 9 | mod calculator; 10 | #[allow(unused)] 11 | #[rustfmt::skip] 12 | mod calculator_actions; 13 | // ANCHOR_END: header 14 | 15 | #[cfg(test)] 16 | mod tests; 17 | 18 | // ANCHOR: main 19 | fn main() { 20 | let mut expression = String::new(); 21 | 22 | // Read the line from the input 23 | println!("Expression:"); 24 | io::stdin() 25 | .read_line(&mut expression) 26 | .expect("Failed to read line."); 27 | 28 | // Parse the line and get the result. 29 | let result = CalculatorParser::new().parse(&expression); 30 | 31 | // Print the result using Debug formatter. 32 | println!("{result:#?}"); 33 | } 34 | // ANCHOR_END: main 35 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator1/src/tests.rs: -------------------------------------------------------------------------------- 1 | // Use the generated parser 2 | use crate::calculator::CalculatorParser; 3 | use rustemo::Parser; 4 | use rustemo_compiler::output_cmp; 5 | 6 | #[test] 7 | fn calculator1_1() { 8 | let result = CalculatorParser::new().parse("2 + 3"); 9 | output_cmp!("src/calculator1_1.ast", format!("{result:#?}")); 10 | } 11 | 12 | #[test] 13 | fn calculator1_1_error() { 14 | let result = CalculatorParser::new().parse("2 3"); 15 | output_cmp!("src/calculator1_1.err", result.unwrap_err().to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "calculator" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | "rustemo", 12 | ] 13 | 14 | [[package]] 15 | name = "lazy_static" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 19 | 20 | [[package]] 21 | name = "regex" 22 | version = "1.7.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 25 | dependencies = [ 26 | "regex-syntax", 27 | ] 28 | 29 | [[package]] 30 | name = "regex-syntax" 31 | version = "0.6.28" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 34 | 35 | [[package]] 36 | name = "rustemo" 37 | version = "0.1.0" 38 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rustemo = { workspace = true } 10 | 11 | [dev-dependencies] 12 | # For output_cmp for testing 13 | rustemo-compiler = { workspace = true } 14 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/calculator-ambig.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '-' E 3 | | E '*' E 4 | | E '/' E 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/calculator-ambig2.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {1} 2 | | E '-' E {1} 3 | | E '*' E {2} 4 | | E '/' E {2} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/calculator.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {1, left} 2 | | E '-' E {1, left} 3 | | E '*' E {2, left} 4 | | E '/' E {2, left} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/calculator2_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | C1( 3 | EC1 { 4 | e_1: Number( 5 | "2", 6 | ), 7 | e_3: C4( 8 | EC4 { 9 | e_1: C3( 10 | EC3 { 11 | e_1: Number( 12 | "3", 13 | ), 14 | e_3: Number( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | e_3: Number( 20 | "2.3", 21 | ), 22 | }, 23 | ), 24 | }, 25 | ), 26 | ) -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/calculator2_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,10]: 2 | ...2 + 3 * 7 -->^ 2.3... 3 | Expected one of STOP, Plus, Minus, Mul, Div. -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/main.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | use std::io; 3 | // Use the generated parser 4 | use crate::calculator::CalculatorParser; 5 | 6 | // Include generated modules 7 | #[rustfmt::skip] 8 | mod calculator; 9 | #[allow(unused)] 10 | #[rustfmt::skip] 11 | mod calculator_actions; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | fn main() { 17 | let mut expression = String::new(); 18 | 19 | // Read the line from the input 20 | println!("Expression:"); 21 | io::stdin() 22 | .read_line(&mut expression) 23 | .expect("Failed to read line."); 24 | 25 | // Parse the line and get the result. 26 | let result = CalculatorParser::new().parse(&expression); 27 | 28 | // Print the result using Debug formatter. 29 | println!("{result:#?}"); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator2/src/tests.rs: -------------------------------------------------------------------------------- 1 | // Use the generated parser 2 | use crate::calculator::CalculatorParser; 3 | use rustemo::Parser; 4 | use rustemo_compiler::output_cmp; 5 | 6 | #[test] 7 | fn calculator2_1() { 8 | let result = CalculatorParser::new().parse("2 + 3 * 7 / 2.3"); 9 | output_cmp!("src/calculator2_1.ast", format!("{result:#?}")); 10 | } 11 | 12 | #[test] 13 | fn calculator2_1_error() { 14 | let result = CalculatorParser::new().parse("2 + 3 * 7 ^ 2.3"); 15 | output_cmp!("src/calculator2_1.err", result.unwrap_err().to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "calculator" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | "rustemo", 12 | ] 13 | 14 | [[package]] 15 | name = "lazy_static" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 19 | 20 | [[package]] 21 | name = "regex" 22 | version = "1.7.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 25 | dependencies = [ 26 | "regex-syntax", 27 | ] 28 | 29 | [[package]] 30 | name = "regex-syntax" 31 | version = "0.6.28" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 34 | 35 | [[package]] 36 | name = "rustemo" 37 | version = "0.1.0" 38 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rustemo = { workspace = true } 10 | 11 | [dev-dependencies] 12 | # For output_cmp for testing 13 | rustemo-compiler = { workspace = true } 14 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/src/calculator.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {Add, 1, left} 2 | | E '-' E {Sub, 1, left} 3 | | E '*' E {Mul, 2, left} 4 | | E '/' E {Div, 2, left} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/src/calculator3_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Add( 3 | Add { 4 | e_1: Number( 5 | "2", 6 | ), 7 | e_3: Div( 8 | Div { 9 | e_1: Mul( 10 | Mul { 11 | e_1: Number( 12 | "3", 13 | ), 14 | e_3: Number( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | e_3: Number( 20 | "2.3", 21 | ), 22 | }, 23 | ), 24 | }, 25 | ), 26 | ) -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/src/calculator3_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,10]: 2 | ...2 + 3 * 7 -->^ 2.3... 3 | Expected one of STOP, Plus, Minus, Mul, Div. -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/src/main.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | use std::io; 3 | // Use the generated parser 4 | use crate::calculator::CalculatorParser; 5 | 6 | // Include generated modules 7 | #[rustfmt::skip] 8 | mod calculator; 9 | #[allow(unused)] 10 | #[rustfmt::skip] 11 | mod calculator_actions; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | fn main() { 17 | let mut expression = String::new(); 18 | 19 | // Read the line from the input 20 | println!("Expression:"); 21 | io::stdin() 22 | .read_line(&mut expression) 23 | .expect("Failed to read line."); 24 | 25 | // Parse the line and get the result. 26 | let result = CalculatorParser::new().parse(&expression); 27 | 28 | // Print the result using Debug formatter. 29 | println!("{result:#?}"); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator3/src/tests.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | // Use the generated parser 3 | use crate::calculator::CalculatorParser; 4 | use rustemo_compiler::output_cmp; 5 | 6 | #[test] 7 | fn calculator3_1() { 8 | let result = CalculatorParser::new().parse("2 + 3 * 7 / 2.3"); 9 | output_cmp!("src/calculator3_1.ast", format!("{result:#?}")); 10 | } 11 | 12 | #[test] 13 | fn calculator3_1_error() { 14 | let result = CalculatorParser::new().parse("2 + 3 * 7 ^ 2.3"); 15 | output_cmp!("src/calculator3_1.err", result.unwrap_err().to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "calculator" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | "rustemo", 12 | ] 13 | 14 | [[package]] 15 | name = "lazy_static" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 19 | 20 | [[package]] 21 | name = "regex" 22 | version = "1.7.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 25 | dependencies = [ 26 | "regex-syntax", 27 | ] 28 | 29 | [[package]] 30 | name = "regex-syntax" 31 | version = "0.6.28" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 34 | 35 | [[package]] 36 | name = "rustemo" 37 | version = "0.1.0" 38 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rustemo = { workspace = true } 10 | 11 | [dev-dependencies] 12 | # For output_cmp for testing 13 | rustemo-compiler = { workspace = true } 14 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/src/calculator.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add, 1, left} 2 | | left=E '-' right=E {Sub, 1, left} 3 | | left=E '*' right=E {Mul, 2, left} 4 | | left=E '/' right=E {Div, 2, left} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/src/calculator4_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Add( 3 | Add { 4 | left: Number( 5 | "2", 6 | ), 7 | right: Div( 8 | Div { 9 | left: Mul( 10 | Mul { 11 | left: Number( 12 | "3", 13 | ), 14 | right: Number( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | right: Number( 20 | "2.3", 21 | ), 22 | }, 23 | ), 24 | }, 25 | ), 26 | ) -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/src/calculator4_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,10]: 2 | ...2 + 3 * 7 -->^ 2.3... 3 | Expected one of STOP, Plus, Minus, Mul, Div. -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/src/main.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | use std::io; 3 | // Use the generated parser 4 | use crate::calculator::CalculatorParser; 5 | 6 | // Include generated modules 7 | #[rustfmt::skip] 8 | mod calculator; 9 | #[allow(unused)] 10 | #[rustfmt::skip] 11 | mod calculator_actions; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | fn main() { 17 | let mut expression = String::new(); 18 | 19 | // Read the line from the input 20 | println!("Expression:"); 21 | io::stdin() 22 | .read_line(&mut expression) 23 | .expect("Failed to read line."); 24 | 25 | // Parse the line and get the result. 26 | let result = CalculatorParser::new().parse(&expression); 27 | 28 | // Print the result using Debug formatter. 29 | println!("{result:?}"); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator4/src/tests.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | // Use the generated parser 3 | use crate::calculator::CalculatorParser; 4 | use rustemo_compiler::output_cmp; 5 | 6 | #[test] 7 | fn calculator4_1() { 8 | let result = CalculatorParser::new().parse("2 + 3 * 7 / 2.3"); 9 | output_cmp!("src/calculator4_1.ast", format!("{result:#?}")); 10 | } 11 | 12 | #[test] 13 | fn calculator4_1_error() { 14 | let result = CalculatorParser::new().parse("2 + 3 * 7 ^ 2.3"); 15 | output_cmp!("src/calculator4_1.err", result.unwrap_err().to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "calculator" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | "rustemo", 12 | ] 13 | 14 | [[package]] 15 | name = "lazy_static" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 19 | 20 | [[package]] 21 | name = "regex" 22 | version = "1.7.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 25 | dependencies = [ 26 | "regex-syntax", 27 | ] 28 | 29 | [[package]] 30 | name = "regex-syntax" 31 | version = "0.6.28" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 34 | 35 | [[package]] 36 | name = "rustemo" 37 | version = "0.1.0" 38 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator5" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rustemo = { workspace = true } 10 | 11 | [dev-dependencies] 12 | # For output_cmp for testing 13 | rustemo-compiler = { workspace = true } 14 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/src/calculator.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add, 1, left} 2 | | left=E '-' right=E {Sub, 1, left} 3 | | left=E '*' right=E {Mul, 2, left} 4 | | left=E '/' right=E {Div, 2, left} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/src/calculator5_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,10]: 2 | ...2 + 3 * 7 -->^ 2.3... 3 | Expected one of STOP, Plus, Minus, Mul, Div. -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/src/calculator_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use ::rustemo::{Context, Token as BaseToken}; 4 | use super::calculator::{self, TokenKind}; 5 | pub type Input = str; 6 | pub type Ctx<'i> = super::calculator::Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 9 | /// ANCHOR: number 10 | pub type Number = f32; 11 | /// ANCHOR_END: number 12 | /// ANCHOR: number_action 13 | pub fn number(_ctx: &Ctx, token: Token) -> Number { 14 | token.value.parse().unwrap() 15 | } 16 | /// ANCHOR_END: number_action 17 | /// ANCHOR: expression 18 | pub type E = f32; 19 | /// ANCHOR_END: expression 20 | /// ANCHOR: actions 21 | pub fn e_add(_ctx: &Ctx, left: E, right: E) -> E { 22 | left + right 23 | } 24 | pub fn e_sub(_ctx: &Ctx, left: E, right: E) -> E { 25 | left - right 26 | } 27 | pub fn e_mul(_ctx: &Ctx, left: E, right: E) -> E { 28 | left * right 29 | } 30 | pub fn e_div(_ctx: &Ctx, left: E, right: E) -> E { 31 | left / right 32 | } 33 | pub fn e_number(_ctx: &Ctx, number: Number) -> E { 34 | number 35 | } 36 | /// ANCHOR_END: actions 37 | #[allow(dead_code)] 38 | type Dummy = u32; 39 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/src/main.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | use std::io; 3 | // Use the generated parser 4 | use crate::calculator::CalculatorParser; 5 | 6 | // Include generated modules 7 | #[rustfmt::skip] 8 | mod calculator; 9 | #[allow(unused)] 10 | #[rustfmt::skip] 11 | mod calculator_actions; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | fn main() { 17 | let mut expression = String::new(); 18 | 19 | // Read the line from the input 20 | println!("Expression:"); 21 | io::stdin() 22 | .read_line(&mut expression) 23 | .expect("Failed to read line."); 24 | 25 | // Parse the line and get the result. 26 | let result = CalculatorParser::new().parse(&expression); 27 | 28 | // Print the result using Debug formatter. 29 | println!("{result:?}"); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/tutorials/calculator/calculator5/src/tests.rs: -------------------------------------------------------------------------------- 1 | use rustemo::Parser; 2 | // Use the generated parser 3 | use crate::calculator::CalculatorParser; 4 | use rustemo_compiler::output_cmp; 5 | 6 | #[test] 7 | fn calculator5_1() { 8 | let result = CalculatorParser::new().parse("2 + 3 * 7 / 2.3"); 9 | assert_eq!(result.unwrap(), 2.0 + 3.0 * 7.0 / 2.3); 10 | } 11 | 12 | #[test] 13 | fn calculator5_1_error() { 14 | let result = CalculatorParser::new().parse("2 + 3 * 7 ^ 2.3"); 15 | output_cmp!("src/calculator5_1.err", result.unwrap_err().to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/tutorials/index.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | 3 | This Chapter provides tutorials which will guide you through the process of 4 | creating fully working rustemo parsers. 5 | -------------------------------------------------------------------------------- /docs/theme/pagetoc.css: -------------------------------------------------------------------------------- 1 | /* src: https://github.com/JorelAli/mdBook-pagetoc */ 2 | 3 | @media only screen and (max-width:1439px) { 4 | .sidetoc { 5 | display: none; 6 | } 7 | } 8 | 9 | @media only screen and (min-width:1440px) { 10 | main { 11 | position: relative; 12 | } 13 | .sidetoc { 14 | margin-left: auto; 15 | margin-right: auto; 16 | /* left: calc(90% + (var(--content-min-width))/4 - 110px); */ 17 | left: 101%; 18 | position: absolute; 19 | font-size: var(--pagetoc-fontsize); 20 | } 21 | .pagetoc { 22 | position: fixed; 23 | width: var(--pagetoc-width); 24 | } 25 | .pagetoc a { 26 | border-left: 1px solid var(--sidebar-bg); 27 | /* color: var(--fg); */ 28 | /* color: var(--sidebar-fg); */ 29 | color: var(--links); 30 | display: block; 31 | padding-bottom: 5px; 32 | padding-top: 5px; 33 | padding-left: 10px; 34 | text-align: left; 35 | text-decoration: none; 36 | font-weight: normal; 37 | background: var(--sidebar-bg); 38 | } 39 | .pagetoc a:hover, 40 | .pagetoc a.active { 41 | background: var(--sidebar-bg); 42 | /* color: var(--sidebar-fg); */ 43 | color: var(--sidebar-active); 44 | font-weight: bold; 45 | font-size: var(--pagetoc-fontsize); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/calculator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator" 3 | 4 | workspace = "../.." 5 | repository.workspace = true 6 | edition.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | version.workspace = true 10 | rust-version.workspace = true 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | # Needed by rustemo generated parsers. 16 | rustemo = { workspace = true } 17 | 18 | [dev-dependencies] 19 | # For output_cmp for testing 20 | rustemo-compiler = { workspace = true } 21 | 22 | [build-dependencies] 23 | rustemo-compiler = { workspace = true } 24 | 25 | [features] 26 | 27 | # Used for testing different table generator approaches 28 | arrays = [] 29 | -------------------------------------------------------------------------------- /examples/calculator/README.md: -------------------------------------------------------------------------------- 1 | # Calculator examples 2 | 3 | - `src/ast_actions` - examples with auto-generated AST actions. 4 | - `src/calc_actions` - examples with manually modified actions. 5 | 6 | See README.md in the corresponding directory. 7 | -------------------------------------------------------------------------------- /examples/calculator/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | fn main() { 4 | let mut settings = rustemo_compiler::Settings::new(); 5 | if std::env::var("CARGO_FEATURE_ARRAYS").is_ok() { 6 | settings = settings.generator_table_type(rustemo_compiler::GeneratorTableType::Arrays); 7 | } 8 | 9 | if let Err(e) = settings.process_dir() { 10 | eprintln!("{e}"); 11 | exit(1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/README.md: -------------------------------------------------------------------------------- 1 | These tests demonstrate automatic generation of ASTs. 2 | 3 | Check `.ast` files and compare to `.rustemo` grammars. The tests are ordered in 4 | the increasing level of refinement. 5 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator01.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | C1( 3 | EC1 { 4 | e: C1( 5 | EC1 { 6 | e: T( 7 | F( 8 | Num( 9 | "2", 10 | ), 11 | ), 12 | ), 13 | t: C1( 14 | TC1 { 15 | t: F( 16 | Num( 17 | "3", 18 | ), 19 | ), 20 | f: Num( 21 | "7", 22 | ), 23 | }, 24 | ), 25 | }, 26 | ), 27 | t: C1( 28 | TC1 { 29 | t: F( 30 | Num( 31 | "6", 32 | ), 33 | ), 34 | f: Num( 35 | "3", 36 | ), 37 | }, 38 | ), 39 | }, 40 | ), 41 | ) -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator01.rustemo: -------------------------------------------------------------------------------- 1 | E: E Plus T | T; 2 | T: T Mul F | F; 3 | F: LParen E RParen | Num; 4 | 5 | terminals 6 | Plus: "+"; 7 | Mul: "*"; 8 | LParen: "("; 9 | RParen: ")"; 10 | Num: /\d+/; 11 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator01_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calculator01::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 9 | pub type Num = String; 10 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 11 | token.value.into() 12 | } 13 | #[derive(Debug, Clone)] 14 | pub struct EC1 { 15 | pub e: Box, 16 | pub t: T, 17 | } 18 | #[derive(Debug, Clone)] 19 | pub enum E { 20 | C1(EC1), 21 | T(T), 22 | } 23 | pub fn e_c1(_ctx: &Ctx, e: E, t: T) -> E { 24 | E::C1(EC1 { e: Box::new(e), t }) 25 | } 26 | pub fn e_t(_ctx: &Ctx, t: T) -> E { 27 | E::T(t) 28 | } 29 | #[derive(Debug, Clone)] 30 | pub struct TC1 { 31 | pub t: Box, 32 | pub f: F, 33 | } 34 | #[derive(Debug, Clone)] 35 | pub enum T { 36 | C1(TC1), 37 | F(F), 38 | } 39 | pub fn t_c1(_ctx: &Ctx, t: T, f: F) -> T { 40 | T::C1(TC1 { t: Box::new(t), f }) 41 | } 42 | pub fn t_f(_ctx: &Ctx, f: F) -> T { 43 | T::F(f) 44 | } 45 | #[derive(Debug, Clone)] 46 | pub enum F { 47 | E(Box), 48 | Num(Num), 49 | } 50 | pub fn f_e(_ctx: &Ctx, e: E) -> F { 51 | F::E(Box::new(e)) 52 | } 53 | pub fn f_num(_ctx: &Ctx, num: Num) -> F { 54 | F::Num(num) 55 | } 56 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator02_ambig.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | C1( 3 | EC1 { 4 | e_1: C1( 5 | EC1 { 6 | e_1: Num( 7 | "2", 8 | ), 9 | e_3: C3( 10 | EC3 { 11 | e_1: Num( 12 | "3", 13 | ), 14 | e_3: Num( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | }, 20 | ), 21 | e_3: C3( 22 | EC3 { 23 | e_1: Num( 24 | "6", 25 | ), 26 | e_3: Num( 27 | "3", 28 | ), 29 | }, 30 | ), 31 | }, 32 | ), 33 | ) -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator02_ambig.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {1, left} 2 | | E '-' E {1, left} 3 | | E '*' E {2, left} 4 | | E '/' E {2, left} 5 | | E '^' E {3, right} 6 | | '(' E ')' 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator03_ambig_prodkind.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Add( 3 | Add { 4 | e_1: Add( 5 | Add { 6 | e_1: Num( 7 | "2", 8 | ), 9 | e_3: Mul( 10 | Mul { 11 | e_1: Num( 12 | "3", 13 | ), 14 | e_3: Num( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | }, 20 | ), 21 | e_3: Mul( 22 | Mul { 23 | e_1: Num( 24 | "6", 25 | ), 26 | e_3: Num( 27 | "3", 28 | ), 29 | }, 30 | ), 31 | }, 32 | ), 33 | ) -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator03_ambig_prodkind.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {Add, 1, left} 2 | | E '-' E {Sub, 1, left} 3 | | E '*' E {Mul, 2, left} 4 | | E '/' E {Div, 2, left} 5 | | E '^' E {Pow, 3, right} 6 | | '(' E ')' {Paren} 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator04_ambig_lhs.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Add( 3 | Add { 4 | left: Add( 5 | Add { 6 | left: Num( 7 | "2", 8 | ), 9 | right: Mul( 10 | Mul { 11 | left: Num( 12 | "3", 13 | ), 14 | right: Num( 15 | "7", 16 | ), 17 | }, 18 | ), 19 | }, 20 | ), 21 | right: Mul( 22 | Mul { 23 | left: Num( 24 | "6", 25 | ), 26 | right: Num( 27 | "3", 28 | ), 29 | }, 30 | ), 31 | }, 32 | ), 33 | ) -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/calculator04_ambig_lhs.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add, 1, left} 2 | | left=E '-' right=E {Sub, 1, left} 3 | | left=E '*' right=E {Mul, 2, left} 4 | | left=E '/' right=E {Div, 2, left} 5 | | base=E '^' exp=E {Pow, 3, right} 6 | | '(' E ')' {Paren} 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::rustemo_mod; 2 | 3 | rustemo_mod!(calculator01, "/src/ast_actions"); 4 | #[allow(dead_code)] 5 | mod calculator01_actions; 6 | 7 | rustemo_mod!(calculator02_ambig, "/src/ast_actions"); 8 | #[allow(dead_code)] 9 | mod calculator02_ambig_actions; 10 | 11 | rustemo_mod!(calculator03_ambig_prodkind, "/src/ast_actions"); 12 | #[allow(dead_code)] 13 | mod calculator03_ambig_prodkind_actions; 14 | 15 | rustemo_mod!(calculator04_ambig_lhs, "/src/ast_actions"); 16 | #[allow(dead_code)] 17 | mod calculator04_ambig_lhs_actions; 18 | 19 | #[cfg(test)] 20 | mod tests; 21 | -------------------------------------------------------------------------------- /examples/calculator/src/ast_actions/tests.rs: -------------------------------------------------------------------------------- 1 | mod calculator01_ast_tests { 2 | use rustemo::Parser; 3 | use rustemo_compiler::output_cmp; 4 | 5 | use crate::ast_actions::{ 6 | calculator01::Calculator01Parser, calculator02_ambig::Calculator02AmbigParser, 7 | calculator03_ambig_prodkind::Calculator03AmbigProdkindParser, 8 | calculator04_ambig_lhs::Calculator04AmbigLhsParser, 9 | }; 10 | 11 | #[test] 12 | fn test_calculator01() { 13 | let result = Calculator01Parser::new().parse("2 + 3 * 7 + 6 * 3"); 14 | output_cmp!("src/ast_actions/calculator01.ast", format!("{:#?}", result)); 15 | } 16 | 17 | #[test] 18 | fn test_calculator02_ambig() { 19 | let result = Calculator02AmbigParser::new().parse("2 + 3 * 7 + 6 * 3"); 20 | output_cmp!( 21 | "src/ast_actions/calculator02_ambig.ast", 22 | format!("{:#?}", result) 23 | ); 24 | } 25 | 26 | #[test] 27 | fn test_calculator03_ambig_prodkind() { 28 | let result = Calculator03AmbigProdkindParser::new().parse("2 + 3 * 7 + 6 * 3"); 29 | output_cmp!( 30 | "src/ast_actions/calculator03_ambig_prodkind.ast", 31 | format!("{:#?}", result) 32 | ); 33 | } 34 | 35 | #[test] 36 | fn test_calculator04_ambig_lhs() { 37 | let result = Calculator04AmbigLhsParser::new().parse("2 + 3 * 7 + 6 * 3"); 38 | output_cmp!( 39 | "src/ast_actions/calculator04_ambig_lhs.ast", 40 | format!("{:#?}", result) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/README.md: -------------------------------------------------------------------------------- 1 | These tests demonstrate manual modification of `_actions.rs` file to supply 2 | semantics of the parsed content. 3 | 4 | Notice that changes will be preserved on action file regenerating. See 5 | `ast_actions` examples in the sibling directory for auto-generated ASTs. 6 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator01.err: -------------------------------------------------------------------------------- 1 | Error at :[1,12]: 2 | ...2 + ( 3 * -->+ 7 ) + 2 * 4... 3 | Expected one of LParen, Num. -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator01.rustemo: -------------------------------------------------------------------------------- 1 | E: E Plus T | T; 2 | T: T Mul F | F; 3 | F: LParen E RParen | Num; 4 | 5 | terminals 6 | Plus: "+"; 7 | Mul: "*"; 8 | LParen: "("; 9 | RParen: ")"; 10 | Num: /\d+/; 11 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator01_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calculator01::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 7 | pub type Ctx<'i> = Context<'i, Input>; 8 | pub type Num = f32; 9 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 10 | token.value.parse().unwrap() 11 | } 12 | pub type E = f32; 13 | pub fn e_c1(_ctx: &Ctx, e_1: E, t_3: T) -> E { 14 | e_1 + t_3 15 | } 16 | pub fn e_t(_ctx: &Ctx, t: T) -> E { 17 | t 18 | } 19 | pub type T = f32; 20 | pub fn t_c1(_ctx: &Ctx, t_1: T, f_3: F) -> T { 21 | t_1 * f_3 22 | } 23 | pub fn t_f(_ctx: &Ctx, f: F) -> T { 24 | f 25 | } 26 | pub type F = f32; 27 | pub fn f_e(_ctx: &Ctx, e: E) -> F { 28 | e 29 | } 30 | pub fn f_num(_ctx: &Ctx, num: Num) -> F { 31 | num 32 | } 33 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator02_ambig.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {1, left} 2 | | E '-' E {1, left} 3 | | E '*' E {2, left} 4 | | E '/' E {2, left} 5 | | E '^' E {3, right} 6 | | '(' E ')' 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator02_ambig_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calculator02_ambig::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 7 | pub type Ctx<'i> = Context<'i, Input>; 8 | pub type Num = f32; 9 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 10 | token.value.parse().unwrap() 11 | } 12 | pub type E = f32; 13 | pub fn e_c1(_ctx: &Ctx, e_1: E, e_3: E) -> E { 14 | e_1 + e_3 15 | } 16 | pub fn e_c2(_ctx: &Ctx, e_1: E, e_3: E) -> E { 17 | e_1 - e_3 18 | } 19 | pub fn e_c3(_ctx: &Ctx, e_1: E, e_3: E) -> E { 20 | e_1 * e_3 21 | } 22 | pub fn e_c4(_ctx: &Ctx, e_1: E, e_3: E) -> E { 23 | e_1 / e_3 24 | } 25 | pub fn e_c5(_ctx: &Ctx, e_1: E, e_3: E) -> E { 26 | f32::powf(e_1, e_3) 27 | } 28 | pub fn e_e(_ctx: &Ctx, e: E) -> E { 29 | e 30 | } 31 | pub fn e_num(_ctx: &Ctx, num: Num) -> E { 32 | num 33 | } 34 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator03_ambig_prodkind.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {Add, 1, left} 2 | | E '-' E {Sub, 1, left} 3 | | E '*' E {Mul, 2, left} 4 | | E '/' E {Div, 2, left} 5 | | E '^' E {Pow, 3, right} 6 | | '(' E ')' {Paren} 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator03_ambig_prodkind_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calculator03_ambig_prodkind::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 8 | pub type Num = f32; 9 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 10 | token.value.parse().unwrap() 11 | } 12 | pub type E = f32; 13 | pub fn e_add(_ctx: &Ctx, e_1: E, e_3: E) -> E { 14 | e_1 + e_3 15 | } 16 | pub fn e_sub(_ctx: &Ctx, e_1: E, e_3: E) -> E { 17 | e_1 - e_3 18 | } 19 | pub fn e_mul(_ctx: &Ctx, e_1: E, e_3: E) -> E { 20 | e_1 * e_3 21 | } 22 | pub fn e_div(_ctx: &Ctx, e_1: E, e_3: E) -> E { 23 | e_1 / e_3 24 | } 25 | pub fn e_pow(_ctx: &Ctx, e_1: E, e_3: E) -> E { 26 | f32::powf(e_1, e_3) 27 | } 28 | pub fn e_paren(_ctx: &Ctx, e: E) -> E { 29 | e 30 | } 31 | pub fn e_num(_ctx: &Ctx, num: Num) -> E { 32 | num 33 | } 34 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator04_ambig_lhs.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add, 1, left} 2 | | left=E '-' right=E {Sub, 1, left} 3 | | left=E '*' right=E {Mul, 2, left} 4 | | left=E '/' right=E {Div, 2, left} 5 | | base=E '^' exp=E {Pow, 3, right} 6 | | '(' E ')' {Paren} 7 | | Num {Num}; 8 | 9 | terminals 10 | 11 | Plus: '+'; 12 | Sub: '-'; 13 | Mul: '*'; 14 | Div: '/'; 15 | Pow: '^'; 16 | LParen: '('; 17 | RParen: ')'; 18 | Num: /\d+(\.\d+)?/; 19 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/calculator04_ambig_lhs_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calculator04_ambig_lhs::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 8 | pub type Num = f32; 9 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 10 | token.value.parse().unwrap() 11 | } 12 | pub type E = f32; 13 | pub fn e_add(_ctx: &Ctx, left: E, right: E) -> E { 14 | left + right 15 | } 16 | pub fn e_sub(_ctx: &Ctx, left: E, right: E) -> E { 17 | left - right 18 | } 19 | pub fn e_mul(_ctx: &Ctx, left: E, right: E) -> E { 20 | left * right 21 | } 22 | pub fn e_div(_ctx: &Ctx, left: E, right: E) -> E { 23 | left / right 24 | } 25 | pub fn e_pow(_ctx: &Ctx, base: E, exp: E) -> E { 26 | f32::powf(base, exp) 27 | } 28 | pub fn e_paren(_ctx: &Ctx, e: E) -> E { 29 | e 30 | } 31 | pub fn e_num(_ctx: &Ctx, num: Num) -> E { 32 | num 33 | } 34 | -------------------------------------------------------------------------------- /examples/calculator/src/calc_actions/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::rustemo_mod; 2 | 3 | rustemo_mod!(calculator01, "/src/calc_actions"); 4 | mod calculator01_actions; 5 | 6 | rustemo_mod!(calculator02_ambig, "/src/calc_actions"); 7 | mod calculator02_ambig_actions; 8 | 9 | rustemo_mod!(calculator03_ambig_prodkind, "/src/calc_actions"); 10 | mod calculator03_ambig_prodkind_actions; 11 | 12 | rustemo_mod!(calculator04_ambig_lhs, "/src/calc_actions"); 13 | mod calculator04_ambig_lhs_actions; 14 | 15 | #[cfg(test)] 16 | mod tests; 17 | -------------------------------------------------------------------------------- /examples/calculator/src/main.rs: -------------------------------------------------------------------------------- 1 | mod ast_actions; 2 | mod calc_actions; 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /examples/json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "json" 3 | 4 | workspace = "../.." 5 | repository.workspace = true 6 | edition.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | version.workspace = true 10 | rust-version.workspace = true 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | # Needed by rustemo generated parsers. 16 | rustemo = { workspace = true } 17 | 18 | [dev-dependencies] 19 | # For output_cmp for testing 20 | rustemo-compiler = { workspace = true } 21 | 22 | [build-dependencies] 23 | rustemo-compiler = { workspace = true } 24 | 25 | [features] 26 | 27 | # Used for testing different table generator approaches 28 | arrays = [] 29 | -------------------------------------------------------------------------------- /examples/json/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | fn main() { 4 | let mut settings = rustemo_compiler::Settings::new(); 5 | if std::env::var("CARGO_FEATURE_ARRAYS").is_ok() { 6 | settings = settings.generator_table_type(rustemo_compiler::GeneratorTableType::Arrays); 7 | } 8 | 9 | if let Err(e) = settings.process_dir() { 10 | eprintln!("{e}"); 11 | exit(1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/json/src/README.md: -------------------------------------------------------------------------------- 1 | JSON examples taken from https://www.json.org/example.html 2 | -------------------------------------------------------------------------------- /examples/json/src/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Markup Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": ["GML", "XML"] 16 | }, 17 | "GlossSee": "markup" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/json/src/example2.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "id": "file", 3 | "value": "File", 4 | "popup": { 5 | "menuitem": [ 6 | {"value": "New", "onclick": "CreateNewDoc()"}, 7 | {"value": "Open", "onclick": "OpenDoc()"}, 8 | {"value": "Close", "onclick": "CloseDoc()"} 9 | ] 10 | } 11 | }} 12 | -------------------------------------------------------------------------------- /examples/json/src/example3.json: -------------------------------------------------------------------------------- 1 | {"widget": { 2 | "debug": "on", 3 | "window": { 4 | "title": "Sample Konfabulator Widget", 5 | "name": "main_window", 6 | "width": 500, 7 | "height": 500 8 | }, 9 | "image": { 10 | "src": "Images/Sun.png", 11 | "name": "sun1", 12 | "hOffset": 250, 13 | "vOffset": 250, 14 | "alignment": "center" 15 | }, 16 | "text": { 17 | "data": "Click Here", 18 | "size": 36, 19 | "style": "bold", 20 | "name": "text1", 21 | "hOffset": 250, 22 | "vOffset": 100, 23 | "alignment": "center", 24 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 25 | } 26 | }} 27 | 28 | -------------------------------------------------------------------------------- /examples/json/src/example5.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "header": "SVG Viewer", 3 | "items": [ 4 | {"id": "Open"}, 5 | {"id": "OpenNew", "label": "Open New"}, 6 | null, 7 | {"id": "ZoomIn", "label": "Zoom In"}, 8 | {"id": "ZoomOut", "label": "Zoom Out"}, 9 | {"id": "OriginalView", "label": "Original View"}, 10 | null, 11 | {"id": "Quality"}, 12 | {"id": "Pause"}, 13 | {"id": "Mute"}, 14 | null, 15 | {"id": "Find", "label": "Find..."}, 16 | {"id": "FindAgain", "label": "Find Again"}, 17 | {"id": "Copy"}, 18 | {"id": "CopyAgain", "label": "Copy Again"}, 19 | {"id": "CopySVG", "label": "Copy SVG"}, 20 | {"id": "ViewSVG", "label": "View SVG"}, 21 | {"id": "ViewSource", "label": "View Source"}, 22 | {"id": "SaveAs", "label": "Save As"}, 23 | null, 24 | {"id": "Help"}, 25 | {"id": "About", "label": "About Adobe CVG Viewer..."} 26 | ] 27 | }} 28 | -------------------------------------------------------------------------------- /examples/json/src/json.rustemo: -------------------------------------------------------------------------------- 1 | Value: False | True | Null | Object | Array | JsonNumber | JsonString; 2 | Object: "{" Member*[Comma] "}"; 3 | Member: JsonString ":" Value; 4 | Array: "[" Value*[Comma] "]"; 5 | 6 | terminals 7 | False: 'false'; 8 | True: 'true'; 9 | Null: 'null'; 10 | Comma: ','; 11 | JsonNumber: /-?\d+(\.\d+)?(e|E[-+]?\d+)?/; 12 | JsonString: /"((\\")|[^"])*"/; 13 | 14 | OBracket: '['; 15 | CBracket: ']'; 16 | OBrace: '{'; 17 | CBrace: '}'; 18 | Colon: ':'; 19 | -------------------------------------------------------------------------------- /gen-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Insert custom html header for katex usage 3 | RUSTDOCFLAGS="--html-in-header docs-header.html" cargo doc --no-deps --document-private-items $1 4 | -------------------------------------------------------------------------------- /rustemo-compiler/src/grammar/tests/invalid_names_1.err: -------------------------------------------------------------------------------- 1 | Err( 2 | Error { 3 | message: "Can't use 'fn' as a valid Rust identifier.", 4 | file: Some( 5 | "", 6 | ), 7 | location: Some( 8 | [5,8-5,10], 9 | ), 10 | }, 11 | ) -------------------------------------------------------------------------------- /rustemo-compiler/src/grammar/tests/invalid_names_2.err: -------------------------------------------------------------------------------- 1 | Err( 2 | Error { 3 | message: "Can't use 'for' as a valid Rust identifier.", 4 | file: Some( 5 | "", 6 | ), 7 | location: Some( 8 | [3,8-3,11], 9 | ), 10 | }, 11 | ) -------------------------------------------------------------------------------- /rustemo-compiler/src/grammar/tests/invalid_names_3.err: -------------------------------------------------------------------------------- 1 | Err( 2 | Error { 3 | message: "Can't use 'impl' as a valid Rust identifier.", 4 | file: Some( 5 | "", 6 | ), 7 | location: Some( 8 | [2,13-2,17], 9 | ), 10 | }, 11 | ) -------------------------------------------------------------------------------- /rustemo-compiler/src/lang/mod.rs: -------------------------------------------------------------------------------- 1 | //! If we start Cargo with bootstrap feature we will load parser code checked 2 | //! out from the git `main` branch. 3 | //! 4 | //! In regular builds parser code from the source tree will be used. 5 | // TODO: Allow clippy warning for unit_arg when Layout is removed from actions. 6 | #[allow(clippy::unit_arg)] 7 | #[rustfmt::skip] 8 | #[cfg(not(feature="bootstrap"))] 9 | pub(crate) mod rustemo; 10 | 11 | // Relax these checks as we have generated code from the grammar. 12 | #[allow(non_camel_case_types, clippy::enum_variant_names)] 13 | #[rustfmt::skip] 14 | #[cfg(not(feature = "bootstrap"))] 15 | pub(crate) mod rustemo_actions; 16 | 17 | #[allow(clippy::unit_arg)] 18 | #[cfg(feature = "bootstrap")] 19 | rustemo_mod! {pub(crate) rustemo, "/src/lang"} 20 | 21 | #[cfg(feature = "bootstrap")] 22 | rustemo_mod! {pub(crate) rustemo_actions, "/src/lang"} 23 | 24 | #[cfg(test)] 25 | mod tests; 26 | -------------------------------------------------------------------------------- /rustemo-compiler/src/lang/tests.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use crate::{grammar::Grammar, output_cmp}; 4 | 5 | #[test] 6 | fn test_rustemo_grammar() { 7 | use std::path::PathBuf; 8 | 9 | let path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "src", "lang", "rustemo.rustemo"] 10 | .iter() 11 | .collect(); 12 | let grammar: Grammar = fs::read_to_string(path).unwrap().parse().unwrap(); 13 | 14 | output_cmp!("src/lang/rustemo.ast", format!("{:#?}", grammar)); 15 | } 16 | -------------------------------------------------------------------------------- /rustemo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustemo" 3 | description = "A LR/GLR parser generator" 4 | readme = "../README.md" 5 | 6 | workspace = ".." 7 | repository.workspace = true 8 | keywords.workspace = true 9 | categories.workspace = true 10 | edition.workspace = true 11 | authors.workspace = true 12 | license.workspace = true 13 | version.workspace = true 14 | rust-version.workspace = true 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | colored = { workspace = true } 20 | regex = { workspace = true } 21 | fancy-regex = { workspace = true } 22 | once_cell = { workspace = true } 23 | petgraph = { workspace = true, optional = true } 24 | 25 | [features] 26 | default = ["glr"] 27 | glr = ["dep:petgraph"] 28 | -------------------------------------------------------------------------------- /rustemo/src/builder.rs: -------------------------------------------------------------------------------- 1 | /// Builds output during parsing by using semantic actions. 2 | /// 3 | /// This trait is implemented by types that are in charge of building output of 4 | /// the parsing process (e.g. a parse tree). 5 | pub trait Builder { 6 | /// A type produced by this builder. See `get_result`. 7 | type Output; 8 | 9 | /// Returns the product of parsing. Usually the one and only element left on 10 | /// the result stack. 11 | fn get_result(&mut self) -> Self::Output; 12 | } 13 | -------------------------------------------------------------------------------- /rustemo/src/common.rs: -------------------------------------------------------------------------------- 1 | /// Loads generated parser modules from the Cargo OUT_DIR location. 2 | /// 3 | /// Used when the parser is generated from the `build.rs` script. 4 | /// 5 | /// This macro and the general idea of bootstrapping approach is based on idea 6 | /// from [lalrpop project](https://github.com/lalrpop/lalrpop) 7 | #[macro_export] 8 | macro_rules! rustemo_mod { 9 | ($(#[$attr:meta])* $vis:vis $modname:ident, $source:expr) => { 10 | #[allow(dead_code)] 11 | $(#[$attr])* $vis mod $modname { include!(concat!(env!("OUT_DIR"), 12 | $source, "/", 13 | stringify!($modname), ".rs")); } 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /rustemo/src/context.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use crate::{input::Input, lexer::Token, location::Location, parser::State}; 4 | 5 | /// Lexer/Parser context is used to keep the state. It provides necessary 6 | /// information to parsers and actions. 7 | pub trait Context<'i, I: Input + ?Sized, S: State, TK>: Default { 8 | /// The current parser state. 9 | fn state(&self) -> S; 10 | fn set_state(&mut self, state: S); 11 | 12 | /// An absolute position in the input sequence 13 | /// 14 | /// The input must be indexable type. 15 | fn position(&self) -> usize; 16 | fn set_position(&mut self, position: usize); 17 | 18 | /// A span in the input sequence, possibly in line-column style. 19 | fn location(&self) -> Location; 20 | fn set_location(&mut self, location: Location); 21 | 22 | /// A span in the input sequence 23 | fn range(&self) -> Range; 24 | fn set_range(&mut self, range: Range); 25 | 26 | /// Next token recognized in the input at the current parsing location 27 | fn token_ahead(&self) -> Option<&Token<'i, I, TK>>; 28 | fn set_token_ahead(&mut self, token: Token<'i, I, TK>); 29 | 30 | /// A layout before the token ahead 31 | fn layout_ahead(&self) -> Option<&'i I>; 32 | fn set_layout_ahead(&mut self, layout: Option<&'i I>); 33 | } 34 | -------------------------------------------------------------------------------- /rustemo/src/debug.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_macros)] 2 | /// Prints without newline to stdout in debug profile 3 | /// 4 | /// See 5 | #[macro_export] 6 | #[cfg(debug_assertions)] 7 | macro_rules! logn { 8 | ($( $args:expr ),*) => { if std::env::var("RUSTEMO_NOTRACE").is_err() { eprint!( $( $args ),* )}; } 9 | } 10 | 11 | /// Prints with newline to stdout in debug profile 12 | #[macro_export] 13 | #[cfg(debug_assertions)] 14 | macro_rules! log { 15 | ($( $args:expr ),*) => { if std::env::var("RUSTEMO_NOTRACE").is_err() { eprintln!( $( $args ),* )}; } 16 | } 17 | 18 | #[macro_export] 19 | #[cfg(not(debug_assertions))] 20 | macro_rules! log { 21 | ($( $args:expr ),*) => { 22 | () 23 | }; 24 | } 25 | 26 | #[macro_export] 27 | #[cfg(not(debug_assertions))] 28 | macro_rules! logn { 29 | ($( $args:expr ),*) => { 30 | () 31 | }; 32 | } 33 | 34 | // See: 35 | pub use log; 36 | pub use logn; 37 | -------------------------------------------------------------------------------- /rustemo/src/glr/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod gss; 2 | pub mod parser; 3 | -------------------------------------------------------------------------------- /rustemo/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate is the runtime for the generated Rustemo parsers. 2 | // See: https://github.com/rust-lang/rfcs/issues/2324 3 | // For local std docs browsing 4 | // #[doc(inline)] 5 | // pub use std; 6 | 7 | #[macro_use] 8 | mod common; 9 | #[macro_use] 10 | pub mod debug; 11 | 12 | mod builder; 13 | mod context; 14 | mod error; 15 | mod input; 16 | mod lexer; 17 | mod location; 18 | mod parser; 19 | mod utils; 20 | 21 | mod lr; 22 | //#[cfg(feature = "glr")] 23 | mod glr; 24 | 25 | // Public API 26 | pub use crate::context::Context; 27 | pub use crate::error::Error; 28 | pub use crate::error::Result; 29 | pub use crate::input::Input; 30 | pub use crate::location::{LineColumn, Location, Position, ValLoc}; 31 | 32 | pub use crate::builder::Builder; 33 | pub use crate::lexer::{Lexer, StringLexer, Token, TokenRecognizer}; 34 | pub use crate::lr::{ 35 | builder::{LRBuilder, SliceBuilder, TreeBuilder, TreeNode}, 36 | context::LRContext, 37 | parser::{Action, LRParser, ParserDefinition}, 38 | }; 39 | pub use crate::parser::{Parser, State}; 40 | 41 | //#[cfg(feature = "glr")] 42 | pub use crate::glr::{ 43 | gss::{Forest, GssHead}, 44 | parser::GlrParser, 45 | }; 46 | 47 | // Reexporting dependencies of generated parsers so that users of the library 48 | // do not have to pollute their Cargo.toml. 49 | // See: https://github.com/igordejanovic/rustemo/issues/15 50 | pub use colored; 51 | pub use fancy_regex; 52 | pub use once_cell; 53 | pub use regex; 54 | -------------------------------------------------------------------------------- /rustemo/src/lr/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod context; 3 | pub mod parser; 4 | -------------------------------------------------------------------------------- /rustemo/src/utils.rs: -------------------------------------------------------------------------------- 1 | /// Simple deduplication. 2 | /// See: 3 | pub trait Dedup { 4 | fn clear_duplicates(&mut self); 5 | } 6 | 7 | impl Dedup for Vec { 8 | fn clear_duplicates(&mut self) { 9 | let mut already_seen = Vec::with_capacity(self.len()); 10 | self.retain(|item| match already_seen.contains(item) { 11 | true => false, 12 | _ => { 13 | already_seen.push(item.clone()); 14 | true 15 | } 16 | }) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Note: parser generated files are ignored using #[rustfmt:skip] on module 2 | # imports. 3 | max_width = 99 4 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | src/ambiguity/prio_assoc_prod.rs 2 | src/ambiguity/prio_assoc_prod_actions.rs 3 | src/ambiguity/prio_assoc_term.rs 4 | src/ambiguity/prio_assoc_term_actions.rs 5 | src/errors/recognizer_not_defined/recognizer_not_defined.rs 6 | src/errors/recognizer_not_defined/recognizer_not_defined_actions.rs 7 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustemo-tests" 3 | description = "Integration tests for Rustemo" 4 | build = "build.rs" 5 | 6 | workspace = ".." 7 | edition.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | version.workspace = true 11 | rust-version.workspace = true 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | # Needed by rustemo generated parsers. 17 | rustemo.workspace = true 18 | 19 | [dev-dependencies] 20 | # For output_cmp for testing 21 | rustemo-compiler.workspace = true 22 | serial_test.workspace = true 23 | 24 | [build-dependencies] 25 | rustemo-compiler.workspace = true 26 | 27 | [[test]] 28 | name = "glr" 29 | path = "src/glr/mod.rs" 30 | 31 | [[test]] 32 | name = "lr" 33 | path = "src/lr.rs" 34 | 35 | [features] 36 | 37 | # Used for testing different table generator approaches 38 | arrays = [] 39 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | This crate is for integration tests. 2 | 3 | Should be used only by running: 4 | 5 | ``` 6 | cargo test 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /tests/src/ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | 4 | use self::reduce_empty_1::ReduceEmpty1Parser; 5 | use self::reduce_empty_2::ReduceEmpty2Parser; 6 | 7 | rustemo_mod!(reduce_empty_1, "/src/ambiguity"); 8 | rustemo_mod!(reduce_empty_1_actions, "/src/ambiguity"); 9 | rustemo_mod!(reduce_empty_2, "/src/ambiguity"); 10 | rustemo_mod!(reduce_empty_2_actions, "/src/ambiguity"); 11 | 12 | #[test] 13 | fn reduce_empty_1() { 14 | let result = ReduceEmpty1Parser::new().parse("b b b"); 15 | output_cmp!("src/ambiguity/reduce_empty_1.ast", format!("{:#?}", result)); 16 | } 17 | 18 | #[test] 19 | fn reduce_empty_2() { 20 | let result = ReduceEmpty2Parser::new().parse("1 42 2 b"); 21 | output_cmp!("src/ambiguity/reduce_empty_2.ast", format!("{:#?}", result)); 22 | } 23 | 24 | #[test] 25 | fn prod_assoc_prio() { 26 | rustemo_compiler::process_grammar(local_file!(file!(), "prio_assoc_prod.rustemo")).unwrap(); 27 | } 28 | 29 | #[test] 30 | fn term_assoc_prod_prio() { 31 | rustemo_compiler::process_grammar(local_file!(file!(), "prio_assoc_term.rustemo")).unwrap(); 32 | } 33 | 34 | #[test] 35 | fn no_assoc_prod_conflicts() { 36 | let result = 37 | rustemo_compiler::process_grammar(local_file!(file!(), "no_prio_assoc_invalid.rustemo")); 38 | output_cmp!( 39 | "src/ambiguity/no_prio_assoc_invalid.err", 40 | format!("{:#?}", result) 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/ambiguity/no_prio_assoc_invalid.err: -------------------------------------------------------------------------------- 1 | Err( 2 | Error( 3 | "Grammar is not deterministic. There are conflicts.", 4 | ), 5 | ) -------------------------------------------------------------------------------- /tests/src/ambiguity/no_prio_assoc_invalid.rustemo: -------------------------------------------------------------------------------- 1 | // Testing non-deterministic grammar 2 | E: E '+' E 3 | | E '*' E 4 | | Num; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/ambiguity/prio_assoc_prod.rustemo: -------------------------------------------------------------------------------- 1 | // Testing priorities and associativities on productions 2 | E: E '+' E {1, left} 3 | | E '*' E {2, reduce} 4 | | Num; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/ambiguity/prio_assoc_term.rustemo: -------------------------------------------------------------------------------- 1 | // Testing prod priorities and associativites on terminals 2 | E: E '+' E {1} 3 | | E '*' E {2} 4 | | Num; 5 | 6 | terminals 7 | Plus: '+' {left}; 8 | Mul: '*' {reduce}; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/ambiguity/reduce_empty_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | Some( 4 | Tb, 5 | ), 6 | Some( 7 | Tb, 8 | ), 9 | Some( 10 | Tb, 11 | ), 12 | ], 13 | ) -------------------------------------------------------------------------------- /tests/src/ambiguity/reduce_empty_1.rustemo: -------------------------------------------------------------------------------- 1 | A: B+; 2 | B: 'b' | EMPTY; 3 | terminals 4 | Tb: 'b'; 5 | -------------------------------------------------------------------------------- /tests/src/ambiguity/reduce_empty_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | Some( 4 | [ 5 | "1", 6 | "42", 7 | "2", 8 | ], 9 | ), 10 | ], 11 | ) -------------------------------------------------------------------------------- /tests/src/ambiguity/reduce_empty_2.rustemo: -------------------------------------------------------------------------------- 1 | A: B+ Tb; 2 | B: Num*; 3 | terminals 4 | Num: /\d+/; 5 | Tb: 'b'; 6 | -------------------------------------------------------------------------------- /tests/src/builder/custom_builder/custom_builder.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {Add, 1, left} 2 | | E '*' E {Mul, 2, left} 3 | | Num {Num}; 4 | 5 | terminals 6 | Plus: '+'; 7 | Mul: '*'; 8 | Num: /\d+/; 9 | -------------------------------------------------------------------------------- /tests/src/builder/custom_builder/mod.rs: -------------------------------------------------------------------------------- 1 | mod custom_builder_builder; 2 | 3 | use rustemo::{rustemo_mod, Builder, Parser}; 4 | use rustemo_compiler::output_cmp; 5 | 6 | use self::custom_builder::CustomBuilderParser; 7 | use self::custom_builder_builder::MyCustomBuilder; 8 | 9 | rustemo_mod!(custom_builder, "/src/builder/custom_builder"); 10 | 11 | #[test] 12 | fn custom_builder() { 13 | // ANCHOR: custom-builder 14 | let result = CustomBuilderParser::new(MyCustomBuilder::new()).parse("2 + 4 * 5 + 20"); 15 | // ANCHOR_END: custom-builder 16 | assert!(matches!(result, Ok(42))); 17 | } 18 | -------------------------------------------------------------------------------- /tests/src/builder/generic_tree/generic_tree.rustemo: -------------------------------------------------------------------------------- 1 | S: A+ B; 2 | A: 'a' Num; 3 | 4 | terminals 5 | B: 'b'; 6 | Ta: 'a'; 7 | Num: /\d+/; 8 | -------------------------------------------------------------------------------- /tests/src/builder/generic_tree/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | use self::generic_tree::GenericTreeParser; 5 | 6 | // Only parser, no actions are generated for generic builder. 7 | rustemo_mod!(generic_tree, "/src/builder/generic_tree"); 8 | 9 | // ANCHOR: generic_tree 10 | #[test] 11 | fn generic_tree() { 12 | let result = GenericTreeParser::new().parse("a 42 a 3 b"); 13 | output_cmp!( 14 | "src/builder/generic_tree/generic_tree.ast", 15 | format!("{:#?}", result) 16 | ); 17 | } 18 | // ANCHOR_END: generic_tree 19 | -------------------------------------------------------------------------------- /tests/src/builder/loc_info/json.rustemo: -------------------------------------------------------------------------------- 1 | Value: False | True | Null | Object | Array | JsonNumber | JsonString; 2 | Object: "{" Member*[Comma] "}"; 3 | Member: JsonString ":" Value; 4 | Array: "[" Value*[Comma] "]"; 5 | 6 | terminals 7 | False: 'false'; 8 | True: 'true'; 9 | Null: 'null'; 10 | Comma: ','; 11 | JsonNumber: /-?\d+(\.\d+)?(e|E[-+]?\d+)?/; 12 | JsonString: /"((\\")|[^"])*"/; 13 | 14 | OBracket: '['; 15 | CBracket: ']'; 16 | OBrace: '{'; 17 | CBrace: '}'; 18 | Colon: ':'; 19 | -------------------------------------------------------------------------------- /tests/src/builder/loc_info/loc_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Markup Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": ["GML", "XML"] 16 | }, 17 | "GlossSee": "markup" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/src/builder/loc_info/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | use serial_test::serial; 4 | 5 | use self::json::JsonParser; 6 | 7 | rustemo_mod!(json, "/src/builder/loc_info"); 8 | rustemo_mod!(json_actions, "/src/builder/loc_info"); 9 | 10 | #[test] 11 | #[serial(loc_info)] 12 | fn loc_info() { 13 | let mut parser = JsonParser::new(); 14 | let result = parser 15 | .parse_file(local_file!(file!(), "loc_info.json")) 16 | .unwrap(); 17 | output_cmp!( 18 | "src/builder/loc_info/loc_info.ast", 19 | format!("{:#?}", result) 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /tests/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | mod custom_builder; 2 | mod generic_tree; 3 | mod loc_info; 4 | mod use_context; 5 | -------------------------------------------------------------------------------- /tests/src/builder/use_context/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | mod use_context_actions; 4 | 5 | rustemo_mod!(use_context, "/src/builder/use_context"); 6 | 7 | use self::use_context::UseContextParser; 8 | 9 | // ANCHOR: use_context 10 | #[test] 11 | fn use_context() { 12 | let result = UseContextParser::new().parse("a 1 42 b"); 13 | output_cmp!( 14 | "src/builder/use_context/use_context.ast", 15 | format!("{:#?}", result) 16 | ); 17 | } 18 | // ANCHOR_END: use_context 19 | -------------------------------------------------------------------------------- /tests/src/builder/use_context/use_context.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | 3, 4 | 46, 5 | ], 6 | ) -------------------------------------------------------------------------------- /tests/src/builder/use_context/use_context.rustemo: -------------------------------------------------------------------------------- 1 | A: 'a' Num+ Tb; 2 | terminals 3 | Ta: 'a'; 4 | Num: /\d+/; 5 | Tb: 'b'; 6 | -------------------------------------------------------------------------------- /tests/src/builder/use_context/use_context_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use super::use_context::{self, TokenKind}; 4 | use rustemo::{Context, Token as BaseToken}; 5 | pub type Input = str; 6 | pub type Ctx<'i> = use_context::Context<'i, Input>; 7 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 8 | pub type Num = u32; 9 | pub fn num(context: &Ctx, token: Token) -> Num { 10 | token.value.parse::().unwrap() + context.position() as u32 11 | } 12 | pub type A = Num1; 13 | pub fn a_num1(_ctx: &Ctx, num1: Num1) -> A { 14 | num1 15 | } 16 | pub type Num1 = Vec; 17 | pub fn num1_c1(_context: &Ctx, mut num1: Num1, num: Num) -> Num1 { 18 | num1.push(num); 19 | num1 20 | } 21 | pub fn num1_num(_ctx: &Ctx, num: Num) -> Num1 { 22 | vec![num] 23 | } 24 | -------------------------------------------------------------------------------- /tests/src/errors/infinite_recursion/infinite_recursion.err: -------------------------------------------------------------------------------- 1 | Error at infinite_recursion.rustemo:[2,3-2,4]: 2 | Infinite recursion on symbol 'B' in production '2: B'. -------------------------------------------------------------------------------- /tests/src/errors/infinite_recursion/infinite_recursion.rustemo: -------------------------------------------------------------------------------- 1 | A: B Ta; 2 | B: B; 3 | 4 | terminals 5 | Ta: 'a'; 6 | -------------------------------------------------------------------------------- /tests/src/errors/infinite_recursion/infinite_recursion_2.err: -------------------------------------------------------------------------------- 1 | Error: First set empty for grammar symbol "AUG". 2 | An infinite recursion on the grammar symbol. -------------------------------------------------------------------------------- /tests/src/errors/infinite_recursion/infinite_recursion_2.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {1} 2 | | E '*' E {2}; 3 | 4 | terminals 5 | Plus: '+'; 6 | Mul: '*'; 7 | -------------------------------------------------------------------------------- /tests/src/errors/infinite_recursion/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo_compiler::{local_file, output_cmp}; 2 | 3 | #[test] 4 | fn infinite_recursion() { 5 | let result = 6 | rustemo_compiler::process_grammar(local_file!(file!(), "infinite_recursion.rustemo")); 7 | output_cmp!( 8 | "src/errors/infinite_recursion/infinite_recursion.err", 9 | result.unwrap_err().to_locfile_str() 10 | ); 11 | } 12 | 13 | #[test] 14 | fn infinite_recursion_2() { 15 | let result = 16 | rustemo_compiler::process_grammar(local_file!(file!(), "infinite_recursion_2.rustemo")); 17 | output_cmp!( 18 | "src/errors/infinite_recursion/infinite_recursion_2.err", 19 | result.unwrap_err().to_string() 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /tests/src/errors/mod.rs: -------------------------------------------------------------------------------- 1 | mod infinite_recursion; 2 | mod recognizer_not_defined; 3 | mod syntax_errors; 4 | mod terminal_not_defined; 5 | mod unexisting_symbol; 6 | -------------------------------------------------------------------------------- /tests/src/errors/recognizer_not_defined/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo_compiler::{local_file, output_cmp, LexerType::Custom}; 2 | 3 | #[test] 4 | fn recognizer_not_defined_for_default_lexer() { 5 | let result = 6 | rustemo_compiler::process_grammar(local_file!(file!(), "recognizer_not_defined.rustemo")); 7 | output_cmp!( 8 | "src/errors/recognizer_not_defined/recognizer_not_defined.err", 9 | result.unwrap_err().to_string() 10 | ); 11 | } 12 | 13 | /// In custom lexer is used recognizers don't need to be defined. 14 | #[test] 15 | fn recognizer_not_defined_for_custom_lexer() { 16 | let result = rustemo_compiler::Settings::new() 17 | .lexer_type(Custom) 18 | .process_grammar(local_file!(file!(), "recognizer_not_defined.rustemo")); 19 | result.unwrap(); 20 | } 21 | -------------------------------------------------------------------------------- /tests/src/errors/recognizer_not_defined/recognizer_not_defined.err: -------------------------------------------------------------------------------- 1 | Error: Recognizer not defined for terminal 'B'. -------------------------------------------------------------------------------- /tests/src/errors/recognizer_not_defined/recognizer_not_defined.rustemo: -------------------------------------------------------------------------------- 1 | A: B+; 2 | 3 | terminals 4 | B:; // recognizer must be defined for default lexer but not for custom lexer. 5 | -------------------------------------------------------------------------------- /tests/src/errors/syntax_errors/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E {left} 2 | | Number; 3 | 4 | terminals 5 | Plus: '+'; 6 | Number: /\d+/; 7 | -------------------------------------------------------------------------------- /tests/src/errors/syntax_errors/calc_incomplete.err: -------------------------------------------------------------------------------- 1 | "Error at :[1,11]:\n\t...2 + 3 + 5 +-->...\n\tExpected Number." -------------------------------------------------------------------------------- /tests/src/errors/syntax_errors/calc_unexpected.err: -------------------------------------------------------------------------------- 1 | "Error at :[1,6]:\n\t...2 + 3 -->/ 4 + 5...\n\tExpected one of STOP, Plus." -------------------------------------------------------------------------------- /tests/src/errors/syntax_errors/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | 4 | rustemo_mod!(calc, "/src/errors/syntax_errors"); 5 | rustemo_mod!(calc_actions, "/src/errors/syntax_errors"); 6 | 7 | use self::calc::CalcParser; 8 | 9 | #[test] 10 | fn syntax_error_unexpected() { 11 | let result = CalcParser::new().parse("2 + 3 / 4 + 5"); 12 | output_cmp!( 13 | local_file!(file!(), "calc_unexpected.err") 14 | .to_str() 15 | .unwrap(), 16 | format!("{:#?}", result.unwrap_err().to_string()) 17 | ); 18 | } 19 | 20 | #[test] 21 | fn syntax_error_incomplete() { 22 | let result = CalcParser::new().parse("2 + 3 + 5 +"); 23 | output_cmp!( 24 | local_file!(file!(), "calc_incomplete.err") 25 | .to_str() 26 | .unwrap(), 27 | format!("{:#?}", result.unwrap_err().to_string()) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/src/errors/terminal_not_defined/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo_compiler::{local_file, output_cmp}; 2 | 3 | #[test] 4 | fn terminal_not_defined() { 5 | let result = 6 | rustemo_compiler::process_grammar(local_file!(file!(), "terminal_not_defined.rustemo")); 7 | output_cmp!( 8 | "src/errors/terminal_not_defined/terminal_not_defined.err", 9 | result.unwrap_err().to_locfile_str() 10 | ); 11 | } 12 | 13 | #[test] 14 | fn terminal_not_defined_sugar() { 15 | let result = rustemo_compiler::process_grammar(local_file!( 16 | file!(), 17 | "terminal_not_defined_sugar.rustemo" 18 | )); 19 | output_cmp!( 20 | "src/errors/terminal_not_defined/terminal_not_defined_sugar.err", 21 | result.unwrap_err().to_locfile_str() 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /tests/src/errors/terminal_not_defined/terminal_not_defined.err: -------------------------------------------------------------------------------- 1 | Error at terminal_not_defined.rustemo:[1,6-1,9]: 2 | Terminal "c" used in production "1: B1 "c"" is not defined in the 'terminals' section. -------------------------------------------------------------------------------- /tests/src/errors/terminal_not_defined/terminal_not_defined.rustemo: -------------------------------------------------------------------------------- 1 | A: B+ 'c'; 2 | 3 | terminals 4 | B: 'b'; 5 | -------------------------------------------------------------------------------- /tests/src/errors/terminal_not_defined/terminal_not_defined_sugar.err: -------------------------------------------------------------------------------- 1 | Error at terminal_not_defined_sugar.rustemo:[1,6-1,9]: 2 | Terminal "c" is not defined in the terminals section. -------------------------------------------------------------------------------- /tests/src/errors/terminal_not_defined/terminal_not_defined_sugar.rustemo: -------------------------------------------------------------------------------- 1 | A: B+ 'c'+; 2 | 3 | terminals 4 | B: 'b'; 5 | -------------------------------------------------------------------------------- /tests/src/errors/unexisting_symbol/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo_compiler::{local_file, output_cmp}; 2 | 3 | #[test] 4 | fn unexisting() { 5 | let result = rustemo_compiler::process_grammar(local_file!(file!(), "unexisting.rustemo")); 6 | output_cmp!( 7 | "src/errors/unexisting_symbol/unexisting_symbol.err", 8 | result.unwrap_err().to_locfile_str() 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /tests/src/errors/unexisting_symbol/unexisting.rustemo: -------------------------------------------------------------------------------- 1 | A: A B Tc; 2 | B: C Tc; 3 | 4 | terminals 5 | Tc: 'c'; 6 | -------------------------------------------------------------------------------- /tests/src/errors/unexisting_symbol/unexisting_symbol.err: -------------------------------------------------------------------------------- 1 | Error at unexisting.rustemo:[2,3-2,4]: 2 | Unexisting symbol 'C' in production '2: C Tc'. -------------------------------------------------------------------------------- /tests/src/fancy_regex/fancy_regex.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | S { 3 | double_word: "foo foo", 4 | number_before_number1: [ 5 | "42 ", 6 | "27 ", 7 | ], 8 | number: "13", 9 | }, 10 | ) -------------------------------------------------------------------------------- /tests/src/fancy_regex/fancy_regex.rustemo: -------------------------------------------------------------------------------- 1 | S: DoubleWord NumberBeforeNumber+ Number; 2 | 3 | terminals 4 | 5 | // this should match repeated word 6 | DoubleWord: /(\w+) (\1)/; 7 | 8 | // Look ahead. Number matched before other number. 9 | NumberBeforeNumber: /\d+ (?=\d+)/; 10 | 11 | // A regular number that matches always 12 | Number: /\d+/; 13 | -------------------------------------------------------------------------------- /tests/src/fancy_regex/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | 4 | use self::fancy_regex::FancyRegexParser; 5 | 6 | rustemo_mod!(fancy_regex, "/src/fancy_regex"); 7 | rustemo_mod!(fancy_regex_actions, "/src/fancy_regex"); 8 | 9 | #[test] 10 | fn fancy_regex() { 11 | let result = FancyRegexParser::new().parse("foo foo 42 27 13"); 12 | output_cmp!("src/fancy_regex/fancy_regex.ast", format!("{:#?}", result)); 13 | } 14 | -------------------------------------------------------------------------------- /tests/src/from_file/calculator.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add, 1, left} 2 | | left=E '-' right=E {Sub, 1, left} 3 | | left=E '*' right=E {Mul, 2, left} 4 | | left=E '/' right=E {Div, 2, left} 5 | | Number; 6 | 7 | terminals 8 | Number: /\d+(\.\d+)?/; 9 | Plus: '+'; 10 | Minus: '-'; 11 | Mul: '*'; 12 | Div: '/'; 13 | -------------------------------------------------------------------------------- /tests/src/from_file/input1.calc: -------------------------------------------------------------------------------- 1 | 2 + 5 * 7 / 2.3 2 | + 3 - 17 3 | / 2 4 | -------------------------------------------------------------------------------- /tests/src/from_file/input2.calc: -------------------------------------------------------------------------------- 1 | 2 + 3 + 5 2 | / 7.54 * / 78 + 3 3 | -------------------------------------------------------------------------------- /tests/src/from_file/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | 4 | rustemo_mod!(calculator, "/src/from_file"); 5 | rustemo_mod!(calculator_actions, "/src/from_file"); 6 | 7 | use calculator::CalculatorParser; 8 | 9 | #[test] 10 | fn parse_from_file_ok() { 11 | let mut parser = CalculatorParser::new(); 12 | let result = parser.parse_file(local_file!(file!(), "input1.calc")); 13 | output_cmp!( 14 | "src/from_file/parse_from_file_ok.ast", 15 | format!("{result:#?}") 16 | ) 17 | } 18 | 19 | #[test] 20 | fn parse_from_file_err() { 21 | // ANCHOR: parser-call 22 | let mut parser = CalculatorParser::new(); 23 | let result = parser.parse_file(local_file!(file!(), "input2.calc")); 24 | // ANCHOR_END: parser-call 25 | output_cmp!( 26 | "src/from_file/parse_from_file_err.err", 27 | result.unwrap_err().to_locfile_str() 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /tests/src/from_file/parse_from_file_err.err: -------------------------------------------------------------------------------- 1 | Error at input2.calc:[2,9]: 2 | ...3 + 5 3 | / 7.54 * -->/ 78 + 3 4 | ... 5 | Expected Number. -------------------------------------------------------------------------------- /tests/src/glr/build/basic/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '*' E 3 | | Num 4 | ; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/glr/build/basic/tree_build_default_1.ast: -------------------------------------------------------------------------------- 1 | C2( 2 | EC2 { 3 | e_1: C1( 4 | EC1 { 5 | e_1: Num( 6 | "1", 7 | ), 8 | e_3: Num( 9 | "4", 10 | ), 11 | }, 12 | ), 13 | e_3: Num( 14 | "9", 15 | ), 16 | }, 17 | ) -------------------------------------------------------------------------------- /tests/src/glr/build/basic/tree_build_default_2.ast: -------------------------------------------------------------------------------- 1 | C1( 2 | EC1 { 3 | e_1: Num( 4 | "1", 5 | ), 6 | e_3: C2( 7 | EC2 { 8 | e_1: Num( 9 | "4", 10 | ), 11 | e_3: Num( 12 | "9", 13 | ), 14 | }, 15 | ), 16 | }, 17 | ) -------------------------------------------------------------------------------- /tests/src/glr/build/loc_info/json.rustemo: -------------------------------------------------------------------------------- 1 | Value: False | True | Null | Object | Array | JsonNumber | JsonString; 2 | Object: "{" Member*[Comma] "}"; 3 | Member: JsonString ":" Value; 4 | Array: "[" Value*[Comma] "]"; 5 | 6 | terminals 7 | False: 'false'; 8 | True: 'true'; 9 | Null: 'null'; 10 | Comma: ','; 11 | JsonNumber: /-?\d+(\.\d+)?(e|E[-+]?\d+)?/; 12 | JsonString: /"((\\")|[^"])*"/; 13 | 14 | OBracket: '['; 15 | CBracket: ']'; 16 | OBrace: '{'; 17 | CBrace: '}'; 18 | Colon: ':'; 19 | -------------------------------------------------------------------------------- /tests/src/glr/build/loc_info/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::{local_file, output_cmp}; 3 | use serial_test::serial; 4 | 5 | use self::json::JsonParser; 6 | 7 | rustemo_mod!(json, "/src/glr/build/loc_info"); 8 | rustemo_mod!(json_actions, "/src/glr/build/loc_info"); 9 | 10 | #[test] 11 | #[serial(loc_info)] 12 | fn glr_loc_info() { 13 | let mut parser = JsonParser::new(); 14 | let forest = parser 15 | // Using the same input file from LR test. 16 | .parse_file(local_file!( 17 | file!(), 18 | "../../../builder/loc_info/loc_info.json" 19 | )) 20 | .unwrap(); 21 | 22 | output_cmp!( 23 | "src/glr/build/loc_info/loc_info_forest.ast", 24 | format!("{:#?}", forest) 25 | ); 26 | 27 | let mut builder = self::json::DefaultBuilder::new(); 28 | let result = forest.get_first_tree().unwrap().build(&mut builder); 29 | 30 | output_cmp!( 31 | // Using the same AST output from LR parser test as we expect the same result. 32 | "src/builder/loc_info/loc_info.ast", 33 | format!("{:#?}", result) 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/glr/build/mod.rs: -------------------------------------------------------------------------------- 1 | mod basic; 2 | mod loc_info; 3 | -------------------------------------------------------------------------------- /tests/src/glr/errors/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '*' E 3 | | Num 4 | ; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/glr/errors/calc_incomplete.err: -------------------------------------------------------------------------------- 1 | Error { 2 | message: "...4 * 9 + 3 * 2 +-->...\nExpected Num.", 3 | file: Some( 4 | "", 5 | ), 6 | location: Some( 7 | [1,18-1,19], 8 | ), 9 | } -------------------------------------------------------------------------------- /tests/src/glr/errors/calc_missing.err: -------------------------------------------------------------------------------- 1 | Error { 2 | message: "...1 + 4 * 9 -->3 * 2 + 7...\nExpected one of STOP, Plus, Mul.", 3 | file: Some( 4 | "", 5 | ), 6 | location: Some( 7 | [1,10], 8 | ), 9 | } -------------------------------------------------------------------------------- /tests/src/glr/errors/calc_unexpected.err: -------------------------------------------------------------------------------- 1 | Error { 2 | message: "...1 + 4 * 9 -->! 3 * 2 + 7...\nExpected one of STOP, Plus, Mul.", 3 | file: Some( 4 | "", 5 | ), 6 | location: Some( 7 | [1,10], 8 | ), 9 | } -------------------------------------------------------------------------------- /tests/src/glr/errors/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(calc, "/src/glr/errors"); 5 | rustemo_mod!(calc_actions, "/src/glr/errors"); 6 | use self::calc::CalcParser; 7 | 8 | #[test] 9 | fn glr_syntax_error_unexpected() { 10 | let input = "1 + 4 * 9 ! 3 * 2 + 7"; 11 | let result = CalcParser::new().parse(input); 12 | output_cmp!( 13 | "src/glr/errors/calc_unexpected.err", 14 | format!("{:#?}", result.unwrap_err()) 15 | ); 16 | } 17 | 18 | #[test] 19 | fn glr_syntax_error_missing() { 20 | let input = "1 + 4 * 9 3 * 2 + 7"; 21 | let result = CalcParser::new().parse(input); 22 | output_cmp!( 23 | "src/glr/errors/calc_missing.err", 24 | format!("{:#?}", result.unwrap_err()) 25 | ); 26 | } 27 | 28 | #[test] 29 | fn glr_syntax_error_incomplete() { 30 | let input = "1 + 4 * 9 + 3 * 2 +"; 31 | let result = CalcParser::new().parse(input); 32 | output_cmp!( 33 | "src/glr/errors/calc_incomplete.err", 34 | format!("{:#?}", result.unwrap_err()) 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /tests/src/glr/evaluate/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '*' E 3 | | Num 4 | ; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/glr/evaluate/calc_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calc::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 9 | pub type Num = String; 10 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 11 | token.value.into() 12 | } 13 | #[derive(Debug, Clone)] 14 | pub struct EC1 { 15 | pub e_1: Box, 16 | pub e_3: Box, 17 | } 18 | #[derive(Debug, Clone)] 19 | pub struct EC2 { 20 | pub e_1: Box, 21 | pub e_3: Box, 22 | } 23 | #[derive(Debug, Clone)] 24 | pub enum E { 25 | C1(EC1), 26 | C2(EC2), 27 | Num(Num), 28 | } 29 | pub fn e_c1(_ctx: &Ctx, e_1: E, e_3: E) -> E { 30 | E::C1(EC1 { 31 | e_1: Box::new(e_1), 32 | e_3: Box::new(e_3), 33 | }) 34 | } 35 | pub fn e_c2(_ctx: &Ctx, e_1: E, e_3: E) -> E { 36 | E::C2(EC2 { 37 | e_1: Box::new(e_1), 38 | e_3: Box::new(e_3), 39 | }) 40 | } 41 | pub fn e_num(_ctx: &Ctx, num: Num) -> E { 42 | E::Num(num) 43 | } 44 | -------------------------------------------------------------------------------- /tests/src/glr/evaluate/calc_eval.rustemo: -------------------------------------------------------------------------------- 1 | E: left=E '+' right=E {Add} 2 | | left=E '*' right=E {Mul} 3 | | Num 4 | ; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/glr/evaluate/calc_eval_actions.rs: -------------------------------------------------------------------------------- 1 | use super::calc_eval::{Context, TokenKind}; 2 | /// This file is maintained by rustemo but can be modified manually. 3 | /// All manual changes will be preserved except non-doc comments. 4 | use rustemo::Token as BaseToken; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 9 | pub type Num = f32; 10 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 11 | token.value.parse().unwrap() 12 | } 13 | pub type E = f32; 14 | pub fn e_add(_ctx: &Ctx, left: E, right: E) -> E { 15 | left + right 16 | } 17 | pub fn e_mul(_ctx: &Ctx, left: E, right: E) -> E { 18 | left * right 19 | } 20 | pub fn e_num(_ctx: &Ctx, num: Num) -> E { 21 | num 22 | } 23 | -------------------------------------------------------------------------------- /tests/src/glr/evaluate/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(calc, "/src/glr/evaluate"); 5 | #[allow(dead_code)] 6 | mod calc_actions; 7 | rustemo_mod!(calc_eval, "/src/glr/evaluate"); 8 | mod calc_eval_actions; 9 | 10 | use self::calc::CalcParser; 11 | use self::calc_eval::CalcEvalParser; 12 | 13 | #[test] 14 | fn glr_tree_calc_eval() { 15 | let input = "1 + 4 * 9 + 3 * 2 + 7"; 16 | let forest = CalcParser::new().parse(input).unwrap(); 17 | let forest_eval = CalcEvalParser::new().parse(input).unwrap(); 18 | assert_eq!(forest.solutions(), 42); 19 | assert_eq!(forest_eval.solutions(), 42); 20 | 21 | let mut res = vec![]; 22 | let mut builder = calc_eval::DefaultBuilder::new(); 23 | for i in 0..42 { 24 | res.push(( 25 | forest.get_tree(i).unwrap(), 26 | forest_eval.get_tree(i).unwrap().build(&mut builder), 27 | )); 28 | } 29 | 30 | output_cmp!("src/glr/evaluate/forest_eval.ast", format!("{:#?}", res)); 31 | } 32 | -------------------------------------------------------------------------------- /tests/src/glr/forest/calc.rustemo: -------------------------------------------------------------------------------- 1 | E: E '+' E 2 | | E '*' E 3 | | Num 4 | ; 5 | 6 | terminals 7 | Plus: '+'; 8 | Mul: '*'; 9 | Num: /\d+/; 10 | -------------------------------------------------------------------------------- /tests/src/glr/forest/forest_tree_17.ast: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | [ 4 | "1", 5 | ], 6 | "+", 7 | [ 8 | "4", 9 | ], 10 | ], 11 | "*", 12 | [ 13 | [ 14 | "9", 15 | ], 16 | "+", 17 | [ 18 | [ 19 | [ 20 | "3", 21 | ], 22 | "*", 23 | [ 24 | "2", 25 | ], 26 | ], 27 | "+", 28 | [ 29 | "7", 30 | ], 31 | ], 32 | ], 33 | ] -------------------------------------------------------------------------------- /tests/src/glr/forest/forest_tree_children.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "1", 3 | ] -------------------------------------------------------------------------------- /tests/src/glr/forest/forest_tree_first.ast: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | [ 4 | [ 5 | [ 6 | [ 7 | "1", 8 | ], 9 | "+", 10 | [ 11 | "4", 12 | ], 13 | ], 14 | "*", 15 | [ 16 | "9", 17 | ], 18 | ], 19 | "+", 20 | [ 21 | "3", 22 | ], 23 | ], 24 | "*", 25 | [ 26 | "2", 27 | ], 28 | ], 29 | "+", 30 | [ 31 | "7", 32 | ], 33 | ] -------------------------------------------------------------------------------- /tests/src/glr/forest/forest_tree_last.ast: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "1", 4 | ], 5 | "+", 6 | [ 7 | [ 8 | [ 9 | "4", 10 | ], 11 | "*", 12 | [ 13 | "9", 14 | ], 15 | ], 16 | "+", 17 | [ 18 | [ 19 | "3", 20 | ], 21 | "*", 22 | [ 23 | [ 24 | "2", 25 | ], 26 | "+", 27 | [ 28 | "7", 29 | ], 30 | ], 31 | ], 32 | ], 33 | ] -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order/grammar_order.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a 42", 5 | ], 6 | [ 7 | ".42", 8 | ], 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order/grammar_order.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A Rest?; 2 | A: Tregex1 | Tregex2 | Tstr; 3 | 4 | terminals 5 | Ts: 's'; 6 | 7 | // This will take precendence as it is first in the grammar order. 8 | Tregex1: /a \d+/; 9 | 10 | // This regex match will match longer part of the string but since we disabled 11 | // longest match strategy next strategy will be considered (grammar order). 12 | Tregex2: /a \d+\.\d+/; 13 | 14 | // In case most specific is turned on (which by default is), this str match 15 | // would take precendence. But, for this test we have disabled most specific 16 | // strategy. 17 | Tstr: 'a 4'; 18 | 19 | Rest: /.+/; 20 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(grammar_order, "/src/glr/lexical_ambiguity/grammar_order"); 5 | rustemo_mod!( 6 | grammar_order_actions, 7 | "/src/glr/lexical_ambiguity/grammar_order" 8 | ); 9 | 10 | use self::grammar_order::GrammarOrderParser; 11 | 12 | #[test] 13 | fn glr_lexical_ambiguity_grammar_order() { 14 | let forest = GrammarOrderParser::new().parse("s a 42.42").unwrap(); 15 | assert_eq!(forest.solutions(), 1); 16 | 17 | let mut trees = String::new(); 18 | for tree in &forest { 19 | trees.push_str(&format!("{tree:#?}\n\n")); 20 | } 21 | output_cmp!( 22 | "src/glr/lexical_ambiguity/grammar_order/grammar_order.ast", 23 | format!("{trees}") 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order_off/grammar_order.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a 42.42", 5 | ], 6 | [], 7 | ] 8 | 9 | [ 10 | "s", 11 | [ 12 | "a 4", 13 | ], 14 | [ 15 | "2.42", 16 | ], 17 | ] 18 | 19 | [ 20 | "s", 21 | [ 22 | "a 42", 23 | ], 24 | [ 25 | ".42", 26 | ], 27 | ] 28 | 29 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order_off/grammar_order.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A Rest?; 2 | // All three alternatives are possible since we have disabled 3 | // lexical disambiguation strategies 4 | A: Tregex1 | Tregex2 | Tstr; 5 | 6 | terminals 7 | Ts: 's'; 8 | 9 | Tregex1: /a \d+/; 10 | 11 | // This is longer match but longest match is disabled. 12 | Tregex2: /a \d+\.\d+/; 13 | 14 | // In case most specific is turned on (which by default is), this str match 15 | // would take precendence. But, for this test we have disabled most specific 16 | // strategy. 17 | Tstr: 'a 4'; 18 | 19 | Rest: /.+/; 20 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/grammar_order_off/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!( 5 | grammar_order, 6 | "/src/glr/lexical_ambiguity/grammar_order_off" 7 | ); 8 | rustemo_mod!( 9 | grammar_order_actions, 10 | "/src/glr/lexical_ambiguity/grammar_order_off" 11 | ); 12 | 13 | use self::grammar_order::GrammarOrderParser; 14 | 15 | #[test] 16 | fn glr_lexical_ambiguity_grammar_order_off() { 17 | let forest = GrammarOrderParser::new().parse("s a 42.42").unwrap(); 18 | assert_eq!(forest.solutions(), 3); 19 | 20 | let mut trees = String::new(); 21 | for tree in &forest { 22 | trees.push_str(&format!("{tree:#?}\n\n")); 23 | } 24 | output_cmp!( 25 | "src/glr/lexical_ambiguity/grammar_order_off/grammar_order.ast", 26 | format!("{trees}") 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match/longest_match.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a 42.42", 5 | ], 6 | [], 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match/longest_match.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar 2 | S: 's' A Rest?; 3 | A: Tregex1 | Tregex2 | Tstr; 4 | 5 | terminals 6 | Ts: 's'; 7 | Tregex1: /a \d+/; 8 | 9 | // This regex match will match longer part of the string and will take 10 | // precendence over Tregex1 if longest match strategy is on (by default is). 11 | Tregex2: /a \d+\.\d+/; 12 | 13 | // In case most specific is turned on (which by default is), this str match 14 | // would take precendence. But, for this test we have disabled most specific 15 | // strategy. 16 | Tstr: 'a 4'; 17 | 18 | Rest: /.+/; 19 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(longest_match, "/src/glr/lexical_ambiguity/longest_match"); 5 | rustemo_mod!( 6 | longest_match_actions, 7 | "/src/glr/lexical_ambiguity/longest_match" 8 | ); 9 | 10 | use self::longest_match::LongestMatchParser; 11 | 12 | #[test] 13 | fn glr_lexical_ambiguity_longest_match() { 14 | let forest = LongestMatchParser::new().parse("s a 42.42").unwrap(); 15 | assert_eq!(forest.solutions(), 1); 16 | 17 | let mut trees = String::new(); 18 | for tree in &forest { 19 | trees.push_str(&format!("{tree:#?}\n\n")); 20 | } 21 | output_cmp!( 22 | "src/glr/lexical_ambiguity/longest_match/longest_match.ast", 23 | format!("{trees}") 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match_off/longest_match.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a 42.42", 5 | ], 6 | [], 7 | ] 8 | 9 | [ 10 | "s", 11 | [ 12 | "a 4", 13 | ], 14 | [ 15 | "2.42", 16 | ], 17 | ] 18 | 19 | [ 20 | "s", 21 | [ 22 | "a 42", 23 | ], 24 | [ 25 | ".42", 26 | ], 27 | ] 28 | 29 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match_off/longest_match.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar 2 | S: 's' A Rest?; 3 | A: Tregex1 | Tregex2 | Tstr; 4 | 5 | terminals 6 | Ts: 's'; 7 | Tregex1: /a \d+/; 8 | 9 | // This regex match will match longer part of the string but since we 10 | // disabled longest match strategy we have all possible solutions. 11 | Tregex2: /a \d+\.\d+/; 12 | 13 | // In case most specific is turned on (which by default is), this str match 14 | // would take precendence. But, for this test we have disabled most specific 15 | // strategy. 16 | Tstr: 'a 4'; 17 | 18 | Rest: /.+/; 19 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/longest_match_off/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!( 5 | longest_match, 6 | "/src/glr/lexical_ambiguity/longest_match_off" 7 | ); 8 | rustemo_mod!( 9 | longest_match_actions, 10 | "/src/glr/lexical_ambiguity/longest_match_off" 11 | ); 12 | 13 | use self::longest_match::LongestMatchParser; 14 | 15 | #[test] 16 | fn glr_lexical_ambiguity_longest_match_off() { 17 | let forest = LongestMatchParser::new().parse("s a 42.42").unwrap(); 18 | assert_eq!(forest.solutions(), 3); 19 | 20 | let mut trees = String::new(); 21 | for tree in &forest { 22 | trees.push_str(&format!("{tree:#?}\n\n")); 23 | } 24 | output_cmp!( 25 | "src/glr/lexical_ambiguity/longest_match_off/longest_match.ast", 26 | format!("{trees}") 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | mod grammar_order; 2 | mod grammar_order_off; 3 | mod longest_match; 4 | mod longest_match_off; 5 | mod most_specific; 6 | mod most_specific_off; 7 | mod priorities; 8 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(most_specific, "/src/glr/lexical_ambiguity/most_specific"); 5 | rustemo_mod!( 6 | most_specific_actions, 7 | "/src/glr/lexical_ambiguity/most_specific" 8 | ); 9 | 10 | use self::most_specific::MostSpecificParser; 11 | 12 | #[test] 13 | fn glr_lexical_ambiguity_most_specific() { 14 | let forest = MostSpecificParser::new().parse("s a 42.42").unwrap(); 15 | assert_eq!(forest.solutions(), 1); 16 | 17 | let mut trees = String::new(); 18 | for tree in &forest { 19 | trees.push_str(&format!("{tree:#?}\n\n")); 20 | } 21 | output_cmp!( 22 | "src/glr/lexical_ambiguity/most_specific/most_specific.ast", 23 | format!("{trees}") 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific/most_specific.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a", 5 | "42.42", 6 | ], 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific/most_specific.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A; 2 | // These two alternative overlaps at the lexical level but the first one wins as 3 | // it uses a string match which is more specific than regex match. 4 | // 5 | // This stategy is by default on but can be turned off in the settings. 6 | A: 'a' Float | Complex; 7 | 8 | 9 | terminals 10 | Ta: 'a'; 11 | Ts: 's'; 12 | Float: /\d+\.\d+/; 13 | Complex: /a \d+\.\d+/; 14 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific_off/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!( 5 | most_specific, 6 | "/src/glr/lexical_ambiguity/most_specific_off" 7 | ); 8 | rustemo_mod!( 9 | most_specific_actions, 10 | "/src/glr/lexical_ambiguity/most_specific_off" 11 | ); 12 | 13 | use self::most_specific::MostSpecificParser; 14 | 15 | #[test] 16 | fn glr_lexical_ambiguity_most_specific_off() { 17 | let forest = MostSpecificParser::new().parse("s a 42.42").unwrap(); 18 | assert_eq!(forest.solutions(), 2); 19 | 20 | let mut trees = String::new(); 21 | for tree in &forest { 22 | trees.push_str(&format!("{tree:#?}\n\n")); 23 | } 24 | output_cmp!( 25 | "src/glr/lexical_ambiguity/most_specific_off/most_specific.ast", 26 | format!("{trees}") 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific_off/most_specific.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "s", 3 | [ 4 | "a 42.42", 5 | ], 6 | ] 7 | 8 | [ 9 | "s", 10 | [ 11 | "a", 12 | "42.42", 13 | ], 14 | ] 15 | 16 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/most_specific_off/most_specific.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A; 2 | // These two alternative overlaps at the lexical level and since most specific 3 | // strategy is turned off both alternatives will be found. 4 | A: 'a' Float | Complex; 5 | 6 | 7 | terminals 8 | Ta: 'a'; 9 | Ts: 's'; 10 | Float: /\d+\.\d+/; 11 | Complex: /a \d+\.\d+/; 12 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/priorities/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(priorities, "/src/glr/lexical_ambiguity/priorities"); 5 | rustemo_mod!(priorities_actions, "/src/glr/lexical_ambiguity/priorities"); 6 | rustemo_mod!(priorities_same, "/src/glr/lexical_ambiguity/priorities"); 7 | rustemo_mod!( 8 | priorities_same_actions, 9 | "/src/glr/lexical_ambiguity/priorities" 10 | ); 11 | 12 | use self::priorities::PrioritiesParser; 13 | use self::priorities_same::PrioritiesSameParser; 14 | 15 | #[test] 16 | fn glr_lexical_ambiguity_priorities() { 17 | let forest = PrioritiesParser::new().parse("a firstone").unwrap(); 18 | assert_eq!(forest.solutions(), 1); 19 | 20 | let mut trees = String::new(); 21 | for tree in &forest { 22 | trees.push_str(&format!("{tree:#?}\n\n")); 23 | } 24 | output_cmp!( 25 | "src/glr/lexical_ambiguity/priorities/priorities.ast", 26 | format!("{trees}") 27 | ); 28 | } 29 | 30 | #[test] 31 | fn glr_lexical_ambiguity_priorities_same() { 32 | let forest = PrioritiesSameParser::new().parse("a firstone").unwrap(); 33 | assert_eq!(forest.solutions(), 1); 34 | 35 | let mut trees = String::new(); 36 | for tree in &forest { 37 | trees.push_str(&format!("{tree:#?}\n\n")); 38 | } 39 | output_cmp!( 40 | "src/glr/lexical_ambiguity/priorities/priorities_same.ast", 41 | format!("{trees}") 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/priorities/priorities.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "a", 3 | [ 4 | "first", 5 | ], 6 | [ 7 | "one", 8 | ], 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/priorities/priorities.rustemo: -------------------------------------------------------------------------------- 1 | S: 'a' A 'one'?; 2 | A: 'firstone' | 'first'; 3 | 4 | 5 | terminals 6 | TA: 'a'; 7 | One: 'one'; 8 | FO: 'firstone'; 9 | 10 | // This match, although shorter, has higher 11 | // priority and will be favored 12 | F: 'first' {15}; 13 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/priorities/priorities_same.ast: -------------------------------------------------------------------------------- 1 | [ 2 | "a", 3 | [ 4 | "firstone", 5 | ], 6 | [], 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /tests/src/glr/lexical_ambiguity/priorities/priorities_same.rustemo: -------------------------------------------------------------------------------- 1 | // This is a slight modification of priorities.rustemo to remove the explicit 2 | // priority on F terminal. Since priorities are the same the disambiguation will 3 | // be done by the most specific match which will favour 'firstone' as longer 4 | // string. 5 | S: 'a' A 'one'?; 6 | A: 'firstone' | 'first'; 7 | 8 | 9 | terminals 10 | TA: 'a'; 11 | One: 'one'; 12 | FO: 'firstone'; 13 | 14 | F: 'first'; 15 | -------------------------------------------------------------------------------- /tests/src/glr/mod.rs: -------------------------------------------------------------------------------- 1 | mod build; 2 | mod errors; 3 | mod evaluate; 4 | mod forest; 5 | mod lexical_ambiguity; 6 | mod regressions; 7 | mod special; 8 | -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_16_subtract_overflow_panic/inline.rustemo: -------------------------------------------------------------------------------- 1 | Inline: InlineEl*; 2 | InlineEl: prefix=StrongDelim children=Inline suffix=StrongDelim? {Strong, 11, left} 3 | | prefix=EmphasisDelim children=Inline suffix=EmphasisDelim? {Emphasis, 11, left} 4 | | title_prefix=LinkTitlePrefix title=Inline continuation=Link1? {Link , 11, left} 5 | | prefix=CodeDelim text=CodeChar* suffix=CodeDelim? {Code, 11, left} 6 | | text=TextChar+ {Text, 9, left}; 7 | 8 | StrongDelim: StrongT; 9 | 10 | EmphasisDelim: EmphasisT; 11 | 12 | LinkTitlePrefix: LinkTitlePrefixT; 13 | LinkTitleSuffix: LinkTitleSuffixT; 14 | LinkAddressPrefix: LinkAddressPrefixT; 15 | LinkAddressSuffix: LinkAddressSuffixT; 16 | Link1: title_suffix=LinkTitleSuffix address=LinkAddress?; 17 | LinkAddress: prefix=LinkAddressPrefix address=LinkAddressChar* suffix=LinkAddressSuffix?; 18 | LinkAddressChar: LinkAddressCharT | EscapedChar; 19 | 20 | CodeDelim: CodeDelimT; 21 | CodeChar: CodeCharT | EscapedChar; 22 | 23 | TextChar: TextCharT | EscapedChar; 24 | 25 | EscapeChar: EscapeT; 26 | EscapedChar: prefix=EscapeChar text=AnyCharT; 27 | 28 | Layout: Empty; 29 | 30 | terminals 31 | Empty: ""; 32 | EscapeT: "\\"; 33 | AnyCharT: /./; 34 | TextCharT: /[^*_`\\]/; 35 | CodeDelimT: "`"; 36 | CodeCharT: /[^`\\]/; 37 | LinkTitlePrefixT: "["; 38 | LinkTitleSuffixT: "]("; 39 | LinkAddressPrefixT: "("; 40 | LinkAddressSuffixT: ")"; 41 | LinkAddressCharT: /[^)\\]/; 42 | StrongT: "*"; 43 | EmphasisT: "_"; -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_16_subtract_overflow_panic/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!( 5 | inline, 6 | "/src/glr/regressions/issue_16_subtract_overflow_panic" 7 | ); 8 | rustemo_mod!( 9 | inline_actions, 10 | "/src/glr/regressions/issue_16_subtract_overflow_panic" 11 | ); 12 | 13 | use self::inline::{DefaultBuilder, InlineParser}; 14 | 15 | #[test] 16 | fn subtract_overflow() { 17 | let result = InlineParser::new() 18 | .parse("*ld 2") 19 | .unwrap() 20 | .get_first_tree() 21 | .unwrap() 22 | .build(&mut DefaultBuilder::new()); 23 | output_cmp!( 24 | "src/glr/regressions/issue_16_subtract_overflow_panic/result.ast", 25 | format!("{:#?}", result) 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_16_subtract_overflow_panic/result.ast: -------------------------------------------------------------------------------- 1 | Some( 2 | [ 3 | Strong( 4 | Strong { 5 | prefix: StrongT, 6 | children: None, 7 | suffix: None, 8 | }, 9 | ), 10 | Text( 11 | Text { 12 | text: [ 13 | TextCharT( 14 | "l", 15 | ), 16 | TextCharT( 17 | "d", 18 | ), 19 | TextCharT( 20 | " ", 21 | ), 22 | TextCharT( 23 | "2", 24 | ), 25 | ], 26 | }, 27 | ), 28 | ], 29 | ) -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_22_panic_get_conflicts/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(unreach, "/src/glr/regressions/issue_22_panic_get_conflicts"); 5 | rustemo_mod!( 6 | unreach_actions, 7 | "/src/glr/regressions/issue_22_panic_get_conflicts" 8 | ); 9 | 10 | use self::unreach::{DefaultBuilder, UnreachParser}; 11 | 12 | #[test] 13 | fn issue_22_panic_get_conflicts() { 14 | let result = UnreachParser::new() 15 | .parse("") 16 | .unwrap() 17 | .get_first_tree() 18 | .unwrap() 19 | .build(&mut DefaultBuilder::new()); 20 | output_cmp!( 21 | "src/glr/regressions/issue_22_panic_get_conflicts/result.ast", 22 | format!("{:#?}", result) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_22_panic_get_conflicts/result.ast: -------------------------------------------------------------------------------- 1 | None -------------------------------------------------------------------------------- /tests/src/glr/regressions/issue_22_panic_get_conflicts/unreach.rustemo: -------------------------------------------------------------------------------- 1 | A: X*; 2 | 3 | terminals 4 | X: "x"; 5 | -------------------------------------------------------------------------------- /tests/src/glr/regressions/mod.rs: -------------------------------------------------------------------------------- 1 | mod issue_16_subtract_overflow_panic; 2 | mod issue_22_panic_get_conflicts; 3 | -------------------------------------------------------------------------------- /tests/src/glr/special/bounded_ambiguity/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar has bounded ambiguity. 2 | // 3 | // The language is: xb^n, n>=0 but each valid sentence will 4 | // always have two derivations. 5 | // 6 | // Grammar G4 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers" 7 | 8 | S: M | N; 9 | M: A M Tb | Tx; 10 | N: A N Tb | Tx; 11 | A: EMPTY; 12 | 13 | terminals 14 | Tb: "b"; 15 | Tx: "x"; 16 | -------------------------------------------------------------------------------- /tests/src/glr/special/bounded_ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/bounded_ambiguity"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/bounded_ambiguity"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_bounded_ambiguity() { 10 | let forest = LangParser::new().parse("xbbb").unwrap(); 11 | assert_eq!(forest.solutions(), 2); 12 | 13 | (1..=forest.solutions()).for_each(|i| { 14 | let tree = forest.get_tree(i - 1); 15 | let mut builder = TreeBuilder::new(); 16 | output_cmp!( 17 | &format!("src/glr/special/bounded_ambiguity/tree_{i}.ast"), 18 | format!( 19 | "{:#?}", 20 | tree.unwrap() 21 | .build::, lang::State>( 22 | &mut builder 23 | ) 24 | ) 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/glr/special/bounded_direct_ambiguity/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar has bounded direct ambiguity of degree 2, in spite of being 2 | // unboundedly ambiguous as for every k we can find a string that will give at 3 | // least k solutions. 4 | // 5 | // The language is t^{m}xb^{n}, n>=m>=0 6 | // 7 | // Grammar G5 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers" 8 | 9 | S: A S Tb | Tx; 10 | A: Tt | EMPTY; 11 | 12 | terminals 13 | 14 | Tb: "b"; 15 | Tx: "x"; 16 | Tt: "t"; 17 | -------------------------------------------------------------------------------- /tests/src/glr/special/bounded_direct_ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/bounded_direct_ambiguity"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/bounded_direct_ambiguity"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_bounded_direct_ambiguity() { 10 | let forest = LangParser::new().parse("txbbbbb").unwrap(); 11 | assert_eq!(forest.solutions(), 5); 12 | 13 | (1..=forest.solutions()).for_each(|i| { 14 | let tree = forest.get_tree(i - 1); 15 | let mut builder = TreeBuilder::new(); 16 | output_cmp!( 17 | &format!("src/glr/special/bounded_direct_ambiguity/tree_{i}.ast"), 18 | format!( 19 | "{:#?}", 20 | tree.unwrap() 21 | .build::, lang::State>( 22 | &mut builder 23 | ) 24 | ) 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/glr/special/cyclic_1/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Cyclic grammar G1 from the paper: "GLR Parsing for e-Grammers" by Rahman Nozohoor-Farshi 2 | S: A; 3 | A: S | Ta; 4 | 5 | terminals 6 | Ta: 'x'; 7 | -------------------------------------------------------------------------------- /tests/src/glr/special/cyclic_1/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | 3 | rustemo_mod!(lang, "/src/glr/special/cyclic_1"); 4 | rustemo_mod!(lang_actions, "/src/glr/special/cyclic_1"); 5 | use self::lang::LangParser; 6 | 7 | #[test] 8 | #[ignore] 9 | fn glr_special_cyclic_1() { 10 | let forest = LangParser::new().parse("x").unwrap(); 11 | 12 | // This will cause stack overflow as the forest is circular 13 | 14 | // TODO: Detection of circular forest should be implemented and a panic with 15 | // the cause should be done. 16 | assert_eq!(forest.solutions(), 1); 17 | } 18 | -------------------------------------------------------------------------------- /tests/src/glr/special/cyclic_2/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Grammar G2 from the paper: "GLR Parsing for e-Grammers" by Rahman Nozohoor-Farshi 2 | // Classic Tomita's GLR algorithm doesn't terminate with this grammar. 3 | S: S S | Ts | EMPTY; 4 | 5 | terminals 6 | Ts: 'x'; 7 | -------------------------------------------------------------------------------- /tests/src/glr/special/cyclic_2/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | 3 | rustemo_mod!(lang, "/src/glr/special/cyclic_2"); 4 | rustemo_mod!(lang_actions, "/src/glr/special/cyclic_2"); 5 | use self::lang::LangParser; 6 | 7 | #[test] 8 | #[ignore] 9 | fn glr_special_cyclic_2() { 10 | let forest = LangParser::new().parse("x").unwrap(); 11 | 12 | // This will cause stack overflow as the forest is circular 13 | // 14 | // TODO: Detection of circular forest should be implemented and a panic with 15 | // the cause should be done. 16 | assert_eq!(forest.solutions(), 1); 17 | } 18 | -------------------------------------------------------------------------------- /tests/src/glr/special/farshi_g7/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Grammar G7 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers" 2 | 3 | S: Ta S Ta | B S Tb | C S Tc | Tx; 4 | B: Ta; 5 | C: Ta; 6 | 7 | terminals 8 | 9 | Ta: "a"; 10 | Tb: "b"; 11 | Tc: "c"; 12 | Tx: "x"; 13 | -------------------------------------------------------------------------------- /tests/src/glr/special/farshi_g7/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/farshi_g7"); 5 | rustemo_mod!( 6 | #[allow(clippy::enum_variant_names)] 7 | lang_actions, 8 | "/src/glr/special/farshi_g7" 9 | ); 10 | use self::lang::LangParser; 11 | 12 | #[test] 13 | fn glr_special_farshi_g7() { 14 | let forest = LangParser::new().parse("aaaaaaaaxbbcaacaa").unwrap(); 15 | assert_eq!(forest.solutions(), 1); 16 | 17 | let tree = forest.get_first_tree(); 18 | let mut builder = TreeBuilder::new(); 19 | output_cmp!( 20 | "src/glr/special/farshi_g7/tree.ast", 21 | format!( 22 | "{:#?}", 23 | tree.unwrap() 24 | .build::, lang::State>( 25 | &mut builder 26 | ) 27 | ) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/src/glr/special/farshi_g8/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This is another interesting ambiguous grammar. 2 | // 3 | // Grammar G8 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers" 4 | 5 | S: Tx | B S Tb | A S Tb; 6 | B: A A; 7 | A: EMPTY; 8 | 9 | terminals 10 | 11 | Tx: "x"; 12 | Tb: "b"; 13 | -------------------------------------------------------------------------------- /tests/src/glr/special/farshi_g8/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/farshi_g8"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/farshi_g8"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_farshi_g8() { 10 | let forest = LangParser::new().parse("xbbb").unwrap(); 11 | assert_eq!(forest.solutions(), 8); 12 | 13 | (1..=forest.solutions()).for_each(|i| { 14 | let tree = forest.get_tree(i - 1); 15 | let mut builder = TreeBuilder::new(); 16 | output_cmp!( 17 | &format!("src/glr/special/farshi_g8/tree_{i}.ast"), 18 | format!( 19 | "{:#?}", 20 | tree.unwrap() 21 | .build::, lang::State>( 22 | &mut builder 23 | ) 24 | ) 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/glr/special/highly_ambiguous/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar has both Shift/Reduce and Reduce/Reduce conflicts and 2 | // thus can't be parsed by a deterministic LR parsing. 3 | // Shift/Reduce can be resolved by prefer_shifts strategy. 4 | 5 | S: Tb | S S | S S S; 6 | 7 | terminals 8 | 9 | Tb: "b"; 10 | -------------------------------------------------------------------------------- /tests/src/glr/special/highly_ambiguous/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/highly_ambiguous"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/highly_ambiguous"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_highly_ambiguous() { 10 | let forest = LangParser::new().parse("bbb").unwrap(); 11 | assert_eq!(forest.solutions(), 3); 12 | 13 | let forest = LangParser::new().parse("bbbb").unwrap(); 14 | assert_eq!(forest.solutions(), 10); 15 | 16 | (1..=forest.solutions()).for_each(|i| { 17 | let tree = forest.get_tree(i - 1); 18 | let mut builder = TreeBuilder::new(); 19 | output_cmp!( 20 | &format!("src/glr/special/highly_ambiguous/tree_{i}.ast"), 21 | format!( 22 | "{:#?}", 23 | tree.unwrap() 24 | .build::, lang::State>( 25 | &mut builder 26 | ) 27 | ) 28 | ); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /tests/src/glr/special/knuth_lr1/lang.rustemo: -------------------------------------------------------------------------------- 1 | // From the Knuth's 1965 paper: On the Translation of Languages from Left to Right 2 | 3 | S: 'a' A 'd' | 'b' A 'd'; 4 | A: 'c' A | 'c'; 5 | 6 | terminals 7 | 8 | Ta: 'a'; 9 | Tb: 'b'; 10 | Tc: 'c'; 11 | Td: 'd'; 12 | -------------------------------------------------------------------------------- /tests/src/glr/special/knuth_lr1/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | 3 | rustemo_mod!(lang, "/src/glr/special/knuth_lr1"); 4 | rustemo_mod!(lang_actions, "/src/glr/special/knuth_lr1"); 5 | 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_knuth_lr1() { 10 | let forest = LangParser::new().parse("acccccccccd").unwrap(); 11 | assert_eq!(forest.solutions(), 1); 12 | 13 | let forest = LangParser::new().parse("bcccccccccd").unwrap(); 14 | assert_eq!(forest.solutions(), 1); 15 | } 16 | -------------------------------------------------------------------------------- /tests/src/glr/special/mod.rs: -------------------------------------------------------------------------------- 1 | //! Special grammars from the literature on GLR parsing. Crafted specifically to 2 | //! trip GLR parsers. 3 | 4 | mod cyclic_1; 5 | mod cyclic_2; 6 | mod knuth_lr1; 7 | mod nondeterministic_palindromes; 8 | 9 | mod bounded_ambiguity; 10 | mod bounded_direct_ambiguity; 11 | mod farshi_g7; 12 | mod farshi_g8; 13 | mod highly_ambiguous; 14 | mod reduce_enough_empty; 15 | mod reduce_enough_many_empty; 16 | mod right_nullable; 17 | mod unbounded_ambiguity; 18 | -------------------------------------------------------------------------------- /tests/src/glr/special/nondeterministic_palindromes/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Language of even length palindromes. 2 | // 3 | // This is a non-deterministic grammar and the language is non-ambiguous. 4 | // 5 | // If the string is a even length palindrome parser should reduce EMPTY at he 6 | // middle of the string and start to reduce by A and B. 7 | // 8 | // LR parsing is deterministic so this grammar can't parse the input as the 9 | // EMPTY reduction will be tried only after consuming all the input by implicit 10 | // disambiguation strategy of favouring shifts over empty reductions. 11 | // 12 | // OTOH, GLR parser can handle this by forking parser at each step and trying 13 | // both empty reductions and shifts. Only the parser that has reduced empty at 14 | // the middle of the input will succeed. 15 | // 16 | // Check the same test in glr/special directory. 17 | 18 | S: A | B | EMPTY; 19 | A: '1' S '1'; 20 | B: '0' S '0'; 21 | 22 | terminals 23 | 24 | One: '1'; 25 | Zero: '0'; 26 | -------------------------------------------------------------------------------- /tests/src/glr/special/nondeterministic_palindromes/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/nondeterministic_palindromes"); 5 | rustemo_mod!( 6 | lang_actions, 7 | "/src/glr/special/nondeterministic_palindromes" 8 | ); 9 | use self::lang::LangParser; 10 | 11 | #[test] 12 | fn glr_special_nondeterministic_palindromes() { 13 | let forest = LangParser::new().parse("01100100100110").unwrap(); 14 | 15 | assert_eq!(forest.solutions(), 1); 16 | let result = forest.get_first_tree(); 17 | 18 | output_cmp!( 19 | "src/glr/special/nondeterministic_palindromes/tree.ast", 20 | format!("{result:#?}") 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tests/src/glr/special/reduce_enough_empty/lang.rustemo: -------------------------------------------------------------------------------- 1 | // In this unambiguous grammar parser must reduce as many empty A productions 2 | // as there are "b" tokens ahead to be able to finish successfully, thus it 3 | // needs unlimited lookahead 4 | // 5 | // Language is: xb^n, n>=0 6 | // 7 | // References: 8 | // 9 | // Grammar G3 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers", 10 | // Generalized LR parsing, Springer, 1991. 11 | // 12 | // Rekers, Joan Gerard: "Parser generation for interactive environments", 13 | // phD thesis, Universiteit van Amsterdam, 1992. 14 | 15 | S: A S Tb | Tx; 16 | A: EMPTY; 17 | 18 | terminals 19 | Tb: "b"; 20 | Tx: "x"; 21 | -------------------------------------------------------------------------------- /tests/src/glr/special/reduce_enough_empty/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/reduce_enough_empty"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/reduce_enough_empty"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_reduce_enough_empty() { 10 | let forest = LangParser::new().parse("xbbb").unwrap(); 11 | assert_eq!(forest.solutions(), 1); 12 | 13 | let tree = forest.get_first_tree(); 14 | let mut builder = TreeBuilder::new(); 15 | output_cmp!( 16 | "src/glr/special/reduce_enough_empty/tree.ast", 17 | format!( 18 | "{:#?}", 19 | tree.unwrap() 20 | .build::, lang::State>( 21 | &mut builder 22 | ) 23 | ) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/glr/special/reduce_enough_many_empty/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This is an extension of the grammar "reduce_enough_empty" where parser must reduce 2 | // enough A B pairs to succeed. 3 | // 4 | // The language is the same: xb^n, n>=0 5 | 6 | S: A B S Tb | Tx; 7 | A: EMPTY; 8 | B: EMPTY; 9 | 10 | terminals 11 | Tb: "b"; 12 | Tx: "x"; 13 | -------------------------------------------------------------------------------- /tests/src/glr/special/reduce_enough_many_empty/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/reduce_enough_many_empty"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/reduce_enough_many_empty"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_reduce_enough_many_empty() { 10 | let forest = LangParser::new().parse("xbbb").unwrap(); 11 | assert_eq!(forest.solutions(), 1); 12 | 13 | let tree = forest.get_first_tree(); 14 | let mut builder = TreeBuilder::new(); 15 | output_cmp!( 16 | "src/glr/special/reduce_enough_many_empty/tree.ast", 17 | format!( 18 | "{:#?}", 19 | tree.unwrap() 20 | .build::, lang::State>( 21 | &mut builder 22 | ) 23 | ) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/glr/special/right_nullable/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Grammar Г2 from paper: Scott, E., & Johnstone, A. (2006). Right nulled GLR parsers. 2 | // ACM Transactions on Programming Languages and Systems (TOPLAS), 28(4), 577-618. 3 | // 4 | // Extended with production S->A to check for alternative empty reductions in right nulled 5 | // productions. 6 | S: Ta S A | A | EMPTY; 7 | A: EMPTY; 8 | 9 | terminals 10 | 11 | Ta: 'a'; 12 | -------------------------------------------------------------------------------- /tests/src/glr/special/right_nullable/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/right_nullable"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/right_nullable"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_right_nullable_g2() { 10 | let forest = LangParser::new().parse("aa").unwrap(); 11 | assert_eq!(forest.solutions(), 2); 12 | (1..=forest.solutions()).for_each(|i| { 13 | let tree = forest.get_tree(i - 1); 14 | let mut builder = TreeBuilder::new(); 15 | output_cmp!( 16 | &format!("src/glr/special/right_nullable/tree_{i}.ast"), 17 | format!( 18 | "{:#?}", 19 | tree.unwrap() 20 | .build::, lang::State>( 21 | &mut builder 22 | ) 23 | ) 24 | ); 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /tests/src/glr/special/right_nullable/tree_1.ast: -------------------------------------------------------------------------------- 1 | NonTermNode { 2 | prod: S: Ta S A, 3 | location: [1,0-1,2], 4 | children: [ 5 | TermNode { 6 | token: Ta("\"a\"" [1,0-1,1]), 7 | layout: None, 8 | }, 9 | NonTermNode { 10 | prod: S: Ta S A, 11 | location: [1,1-1,2], 12 | children: [ 13 | TermNode { 14 | token: Ta("\"a\"" [1,1-1,2]), 15 | layout: None, 16 | }, 17 | NonTermNode { 18 | prod: S: A, 19 | location: [1,2-1,2], 20 | children: [ 21 | NonTermNode { 22 | prod: A: , 23 | location: [1,2-1,2], 24 | children: [], 25 | layout: None, 26 | }, 27 | ], 28 | layout: None, 29 | }, 30 | NonTermNode { 31 | prod: A: , 32 | location: [1,2-1,2], 33 | children: [], 34 | layout: None, 35 | }, 36 | ], 37 | layout: None, 38 | }, 39 | NonTermNode { 40 | prod: A: , 41 | location: [1,2-1,2], 42 | children: [], 43 | layout: None, 44 | }, 45 | ], 46 | layout: None, 47 | } -------------------------------------------------------------------------------- /tests/src/glr/special/right_nullable/tree_2.ast: -------------------------------------------------------------------------------- 1 | NonTermNode { 2 | prod: S: Ta S A, 3 | location: [1,0-1,2], 4 | children: [ 5 | TermNode { 6 | token: Ta("\"a\"" [1,0-1,1]), 7 | layout: None, 8 | }, 9 | NonTermNode { 10 | prod: S: Ta S A, 11 | location: [1,1-1,2], 12 | children: [ 13 | TermNode { 14 | token: Ta("\"a\"" [1,1-1,2]), 15 | layout: None, 16 | }, 17 | NonTermNode { 18 | prod: S: , 19 | location: [1,2-1,2], 20 | children: [], 21 | layout: None, 22 | }, 23 | NonTermNode { 24 | prod: A: , 25 | location: [1,2-1,2], 26 | children: [], 27 | layout: None, 28 | }, 29 | ], 30 | layout: None, 31 | }, 32 | NonTermNode { 33 | prod: A: , 34 | location: [1,2-1,2], 35 | children: [], 36 | layout: None, 37 | }, 38 | ], 39 | layout: None, 40 | } -------------------------------------------------------------------------------- /tests/src/glr/special/unbounded_ambiguity/lang.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar has unbounded ambiguity. 2 | // 3 | // Grammar G6 from: Nozohoor-Farshi, Rahman: "GLR Parsing for ε-Grammers" 4 | 5 | S: M N; 6 | M: A M Tb | Tx; 7 | N: Tb N A | Tx; 8 | A: EMPTY; 9 | 10 | terminals 11 | 12 | Tb: "b"; 13 | Tx: "x"; 14 | -------------------------------------------------------------------------------- /tests/src/glr/special/unbounded_ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser, TreeBuilder}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/glr/special/unbounded_ambiguity"); 5 | rustemo_mod!(lang_actions, "/src/glr/special/unbounded_ambiguity"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn glr_special_unbounded_ambiguity() { 10 | let forest = LangParser::new().parse("xbbbbx").unwrap(); 11 | assert_eq!(forest.solutions(), 5); 12 | 13 | (1..=forest.solutions()).for_each(|i| { 14 | let tree = forest.get_tree(i - 1); 15 | let mut builder = TreeBuilder::new(); 16 | output_cmp!( 17 | &format!("src/glr/special/unbounded_ambiguity/tree_{i}.ast"), 18 | format!( 19 | "{:#?}", 20 | tree.unwrap() 21 | .build::, lang::State>( 22 | &mut builder 23 | ) 24 | ) 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/layout/ast/layout.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | S { 3 | digit: "4", 4 | two_digits: TwoDigits { 5 | digit_1: "2", 6 | digit_2: "6", 7 | }, 8 | digit1: [ 9 | "8", 10 | "9", 11 | ], 12 | }, 13 | ) -------------------------------------------------------------------------------- /tests/src/layout/ast/layout.rustemo: -------------------------------------------------------------------------------- 1 | // Digits with some words in between that should be ignored. 2 | S: Digit TwoDigits Digit+; 3 | TwoDigits: Digit Digit; 4 | Layout: LayoutItem+; 5 | LayoutItem: Word | WS; 6 | 7 | terminals 8 | Digit: /\d/; 9 | Word: /[a-zA-Z]+/; 10 | WS: /\s+/; 11 | -------------------------------------------------------------------------------- /tests/src/layout/ast/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | use self::layout::LayoutParser; 5 | 6 | rustemo_mod!(layout, "/src/layout/ast"); 7 | rustemo_mod!(layout_actions, "/src/layout/ast"); 8 | 9 | #[test] 10 | fn layout_ast() { 11 | // ANCHOR: input 12 | let result = LayoutParser::new().parse("42 This6 should be 8 ignored 9 "); 13 | // ANCHOR_END: input 14 | output_cmp!("src/layout/ast/layout.ast", format!("{:#?}", result)) 15 | } 16 | -------------------------------------------------------------------------------- /tests/src/layout/generic_tree/layout.rustemo: -------------------------------------------------------------------------------- 1 | // Digits with some words in between that should be ignored. 2 | S: Digit TwoDigits Digit+; 3 | TwoDigits: Digit Digit; 4 | Layout: LayoutItem+; 5 | LayoutItem: Word | WS; 6 | 7 | terminals 8 | Digit: /\d/; 9 | Word: /[a-zA-Z]+/; 10 | WS: /\s+/; 11 | -------------------------------------------------------------------------------- /tests/src/layout/generic_tree/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | use self::layout::LayoutParser; 5 | 6 | rustemo_mod!(layout, "/src/layout/generic_tree"); 7 | 8 | #[test] 9 | fn layout_generic() { 10 | // ANCHOR: input 11 | let result = LayoutParser::new().parse("42 This6 should be 8 ignored 9 "); 12 | // ANCHOR_END: input 13 | output_cmp!( 14 | "src/layout/generic_tree/layout.ast", 15 | format!("{:#?}", result) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /tests/src/layout/mod.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | mod generic_tree; 3 | -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/README.md: -------------------------------------------------------------------------------- 1 | These examples show how to create a custom lexer. 2 | 3 | In both example a sequence of 4 | [VarInts](https://developers.google.com/protocol-buffers/docs/encoding#varints) 5 | are parsed. The sequence is provided in file `custom_lexer.bytes` and forms 6 | numbers: 300, 42, and 57345. You can see files with `.ast` extensions for 7 | results of parsing. 8 | 9 | In the example `custom_lexer_1` lexer is in charge of recognizing each `VarInt` 10 | while in the example `custom_lexer_2` lexer is recognizing constituent bytes of 11 | `VarInts` while the grammar defines how these bytes are organized into 12 | `VarInts`. 13 | 14 | In both examples, the conversion to numeric values is done in the actions which 15 | are generated by Rustemo and then manually edited at appropriate places to 16 | provide the conversion. 17 | -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer.bytes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igordejanovic/rustemo/3e59f9db663ee2bb12929435a5ecf6973c34c0ed/tests/src/lexer/custom_lexer/custom_lexer.bytes -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | 300, 4 | 42, 5 | 57345, 6 | ], 7 | ) -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer_1.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar defines a language which consists of a sequence of VarInts 2 | // https://developers.google.com/protocol-buffers/docs/encoding#varints 3 | 4 | VarInts: VarInt+; 5 | 6 | terminals 7 | // We don't provide a body of this terminal def. as we are not parsing text. 8 | // Lexer will do the recognition of a single VarInt from the input stream. 9 | VarInt:; 10 | -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer_1_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use super::custom_lexer_1::{self, TokenKind}; 4 | use super::custom_lexer_1_lexer::Input; 5 | use rustemo::Token as BaseToken; 6 | #[allow(dead_code)] 7 | pub type Token<'i> = BaseToken<'i, Input, TokenKind>; 8 | pub type Context<'i> = custom_lexer_1::Context<'i, Input>; 9 | pub type VarInt = i128; 10 | /// Here we convert varint slice of u8 to i128 11 | pub fn var_int<'i>(_ctx: &Context<'i>, token: Token<'i>) -> VarInt { 12 | let mut res: VarInt = 0; 13 | token 14 | .value 15 | .iter() 16 | .rev() 17 | .for_each(|b| { 18 | res <<= 7; 19 | res |= (b & 0b0111_1111) as i128; 20 | }); 21 | res 22 | } 23 | pub type VarInts = VarInt1; 24 | pub fn var_ints_var_int1(_ctx: &Context, var_int1: VarInt1) -> VarInts { 25 | var_int1 26 | } 27 | pub type VarInt1 = Vec; 28 | pub fn var_int1_c1(_ctx: &Context, mut var_int1: VarInt1, var_int: VarInt) -> VarInt1 { 29 | var_int1.push(var_int); 30 | var_int1 31 | } 32 | pub fn var_int1_var_int(_ctx: &Context, var_int: VarInt) -> VarInt1 { 33 | vec![var_int] 34 | } 35 | -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | 300, 4 | 42, 5 | 57345, 6 | ], 7 | ) -------------------------------------------------------------------------------- /tests/src/lexer/custom_lexer/custom_lexer_2.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar defines a language which consists of a sequence of VarInts 2 | // https://developers.google.com/protocol-buffers/docs/encoding#varints 3 | // 4 | // In this variant we are doing more work in the parser. The lexer will 5 | // just recognize if the current byte is MSB or not. 6 | // ANCHOR: lexer-doc 7 | VarInts: VarInt+; 8 | VarInt: MSBByte* NonMSBByte; 9 | 10 | terminals 11 | MSBByte:; 12 | NonMSBByte:; 13 | // ANCHOR_END: lexer-doc 14 | -------------------------------------------------------------------------------- /tests/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | mod custom_lexer; 2 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/grammar_order/grammar_order.ast: -------------------------------------------------------------------------------- 1 | S { a: Tregex1("a 42"), rest_opt: Some(".42") } -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/grammar_order/grammar_order.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A Rest?; 2 | A: Tregex1 | Tregex2 | Tstr; 3 | 4 | terminals 5 | Ts: 's'; 6 | 7 | // This regex takes precendence as it is first by the grammar order. 8 | Tregex1: /a \d+/; 9 | 10 | // This regex match will match longer part of the string and will take 11 | // precendence over Tregex1 if longest match strategy is on. But in this test we 12 | // have disabled longest match. 13 | Tregex2: /a \d+\.\d+/; 14 | 15 | // In case most specific is turned on (which by default is), this str match 16 | // would take precendence. But, for this test we have disabled most specific 17 | // strategy. 18 | Tstr: 'a 4'; 19 | 20 | Rest: /.+/; 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/grammar_order/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(grammar_order, "/src/lexical_ambiguity/grammar_order"); 5 | rustemo_mod!( 6 | grammar_order_actions, 7 | "/src/lexical_ambiguity/grammar_order" 8 | ); 9 | 10 | use self::grammar_order::GrammarOrderParser; 11 | 12 | #[test] 13 | fn lr_lexical_ambiguity_grammar_order() { 14 | let result = GrammarOrderParser::new().parse("s a 42.42").unwrap(); 15 | 16 | output_cmp!( 17 | "src/lexical_ambiguity/grammar_order/grammar_order.ast", 18 | format!("{result:?}") 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/longest_match/longest_match.ast: -------------------------------------------------------------------------------- 1 | S { a: Tregex2("a 42.42"), rest_opt: None } -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/longest_match/longest_match.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A Rest?; 2 | A: Tregex1 | Tregex2 | Tstr; 3 | 4 | terminals 5 | Ts: 's'; 6 | 7 | // This regex will match shorter part of the input string. Thus, with longest 8 | // match strategy, it will not be a part of the solution. 9 | Tregex1: /a \d+/; 10 | 11 | // This regex match will match longer part of the string and will take 12 | // precendence over Tregex1 if longest match strategy is on (by default is). 13 | Tregex2: /a \d+\.\d+/; 14 | 15 | // In case most specific is turned on (which by default is), this str match 16 | // would take precendence. But, for this test we have disabled most specific 17 | // strategy. 18 | Tstr: 'a 4'; 19 | 20 | Rest: /.+/; 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/longest_match/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(longest_match, "/src/lexical_ambiguity/longest_match"); 5 | rustemo_mod!( 6 | longest_match_actions, 7 | "/src/lexical_ambiguity/longest_match" 8 | ); 9 | 10 | use self::longest_match::LongestMatchParser; 11 | 12 | #[test] 13 | fn lr_lexical_ambiguity_longest_match() { 14 | let result = LongestMatchParser::new().parse("s a 42.42").unwrap(); 15 | 16 | output_cmp!( 17 | "src/lexical_ambiguity/longest_match/longest_match.ast", 18 | format!("{result:?}") 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/mod.rs: -------------------------------------------------------------------------------- 1 | mod grammar_order; 2 | mod longest_match; 3 | mod most_specific; 4 | mod most_specific_off; 5 | mod priorities; 6 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(most_specific, "/src/lexical_ambiguity/most_specific"); 5 | rustemo_mod!( 6 | most_specific_actions, 7 | "/src/lexical_ambiguity/most_specific" 8 | ); 9 | 10 | use self::most_specific::MostSpecificParser; 11 | 12 | #[test] 13 | fn lr_lexical_ambiguity_most_specific() { 14 | let result = MostSpecificParser::new().parse("s a 42.42").unwrap(); 15 | 16 | output_cmp!( 17 | "src/lexical_ambiguity/most_specific/most_specific.ast", 18 | format!("{result:?}") 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific/most_specific.ast: -------------------------------------------------------------------------------- 1 | Float("42.42") -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific/most_specific.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A; 2 | // These two alternative overlaps at the lexical level but the first one wins as 3 | // it uses a string match which is more specific than regex match. 4 | // 5 | // This stategy is by default on but can be turned off in the settings. 6 | A: 'a' Float | Complex; 7 | 8 | 9 | terminals 10 | Ta: 'a'; 11 | Ts: 's'; 12 | Float: /\d+\.\d+/; 13 | Complex: /a \d+\.\d+/; 14 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific_off/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(most_specific, "/src/lexical_ambiguity/most_specific_off"); 5 | rustemo_mod!( 6 | most_specific_actions, 7 | "/src/lexical_ambiguity/most_specific_off" 8 | ); 9 | 10 | use self::most_specific::MostSpecificParser; 11 | 12 | #[test] 13 | fn lr_lexical_ambiguity_most_specific_off() { 14 | let result = MostSpecificParser::new().parse("s a 42.42").unwrap(); 15 | 16 | output_cmp!( 17 | "src/lexical_ambiguity/most_specific_off/most_specific.ast", 18 | format!("{result:?}") 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific_off/most_specific.ast: -------------------------------------------------------------------------------- 1 | Complex("a 42.42") -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/most_specific_off/most_specific.rustemo: -------------------------------------------------------------------------------- 1 | S: 's' A; 2 | // These two alternative overlaps at the lexical level and since most specific 3 | // strategy is turned off other strategies will take over (first longest match 4 | // and finally grammar order). Thus, "Complex" terminal will take over. 5 | A: 'a' Float | Complex; 6 | 7 | 8 | terminals 9 | Complex: /a \d+\.\d+/; 10 | Ta: 'a'; 11 | Ts: 's'; 12 | Float: /\d+\.\d+/; 13 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/priorities/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(priorities, "/src/lexical_ambiguity/priorities"); 5 | rustemo_mod!(priorities_actions, "/src/lexical_ambiguity/priorities"); 6 | rustemo_mod!(priorities_same, "/src/lexical_ambiguity/priorities"); 7 | rustemo_mod!(priorities_same_actions, "/src/lexical_ambiguity/priorities"); 8 | 9 | use self::priorities::PrioritiesParser; 10 | use self::priorities_same::PrioritiesSameParser; 11 | 12 | #[test] 13 | fn lr_lexical_ambiguity_priorities() { 14 | let result = PrioritiesParser::new().parse("a firstone").unwrap(); 15 | 16 | output_cmp!( 17 | "src/lexical_ambiguity/priorities/priorities.ast", 18 | format!("{result:?}") 19 | ); 20 | } 21 | 22 | #[test] 23 | fn lr_lexical_ambiguity_priorities_same() { 24 | let result = PrioritiesSameParser::new().parse("a firstone").unwrap(); 25 | 26 | output_cmp!( 27 | "src/lexical_ambiguity/priorities/priorities_same.ast", 28 | format!("{result:?}") 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/priorities/priorities.ast: -------------------------------------------------------------------------------- 1 | S { a: F, one_opt: Some(One) } -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/priorities/priorities.rustemo: -------------------------------------------------------------------------------- 1 | S: 'a' A 'one'?; 2 | A: 'firstone' | 'first'; 3 | 4 | 5 | terminals 6 | TA: 'a'; 7 | One: 'one'; 8 | FO: 'firstone'; 9 | 10 | // This match, although shorter, has higher 11 | // priority and will be favored 12 | F: 'first' {15}; 13 | -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/priorities/priorities_same.ast: -------------------------------------------------------------------------------- 1 | S { a: FO, one_opt: None } -------------------------------------------------------------------------------- /tests/src/lexical_ambiguity/priorities/priorities_same.rustemo: -------------------------------------------------------------------------------- 1 | // This is a slight modification of priorities.rustemo to remove the explicit 2 | // priority on F terminal. Since priorities are the same the disambiguation will 3 | // be done by the most specific match which will favour 'firstone' as longer 4 | // string. 5 | S: 'a' A 'one'?; 6 | A: 'firstone' | 'first'; 7 | 8 | 9 | terminals 10 | TA: 'a'; 11 | One: 'one'; 12 | FO: 'firstone'; 13 | 14 | F: 'first'; 15 | -------------------------------------------------------------------------------- /tests/src/lr.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | #![cfg(test)] 3 | mod ambiguity; 4 | mod builder; 5 | mod errors; 6 | mod fancy_regex; 7 | mod from_file; 8 | mod layout; 9 | mod lexer; 10 | mod lexical_ambiguity; 11 | mod output_dir; 12 | mod partial; 13 | mod rule_patterns; 14 | mod special; 15 | mod sugar; 16 | mod unicode; 17 | -------------------------------------------------------------------------------- /tests/src/output_dir/README.md: -------------------------------------------------------------------------------- 1 | This tests shows various configuration of output directory for generated parsers 2 | and actions when the generator is called from `build.rs` script. 3 | 4 | By default, everithing is generated in Cargo `OUT_DIR` folder. But in this case we make a different configurations. 5 | 6 | 1. For grammar `out_dir.rustemo` the generator is configured to generate both 7 | parser and actions in the this directory. 8 | 2. For grammar `our_dir_act.rustemo` the generator is configured to generate 9 | only actions in this directory while the parser is still generated in 10 | `OUT_DIR`. 11 | -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b1: [ 4 | Tb, 5 | Tb, 6 | Tb, 7 | ], 8 | num: "1", 9 | }, 10 | ) -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir.rustemo: -------------------------------------------------------------------------------- 1 | A: B+ Num; 2 | B: 'b'; 3 | 4 | terminals 5 | Tb: 'b'; 6 | Num: /\d+/; 7 | -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir_act.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b1: [ 4 | Tb, 5 | Tb, 6 | Tb, 7 | ], 8 | num: "1", 9 | }, 10 | ) -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir_act.rustemo: -------------------------------------------------------------------------------- 1 | A: B+ Num; 2 | B: 'b'; 3 | 4 | terminals 5 | Tb: 'b'; 6 | Num: /\d+/; 7 | -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir_act_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use rustemo::Token as RustemoToken; 4 | use super::output_dir_act::{TokenKind, Context}; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = RustemoToken<'i, Input, TokenKind>; 9 | pub type Num = String; 10 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 11 | token.value.into() 12 | } 13 | #[derive(Debug, Clone)] 14 | pub struct A { 15 | pub b1: B1, 16 | pub num: Num, 17 | } 18 | pub fn a_c1(_ctx: &Ctx, b1: B1, num: Num) -> A { 19 | A { b1, num } 20 | } 21 | pub type B1 = Vec; 22 | pub fn b1_c1(_ctx: &Ctx, mut b1: B1, b: B) -> B1 { 23 | b1.push(b); 24 | b1 25 | } 26 | pub fn b1_b(_ctx: &Ctx, b: B) -> B1 { 27 | vec![b] 28 | } 29 | #[derive(Debug, Clone)] 30 | pub enum B { 31 | Tb, 32 | } 33 | pub fn b_tb(_ctx: &Ctx) -> B { 34 | B::Tb 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/output_dir/output_dir_actions.rs: -------------------------------------------------------------------------------- 1 | /// This file is maintained by rustemo but can be modified manually. 2 | /// All manual changes will be preserved except non-doc comments. 3 | use rustemo::Token as RustemoToken; 4 | use super::output_dir::{TokenKind, Context}; 5 | pub type Input = str; 6 | pub type Ctx<'i> = Context<'i, Input>; 7 | #[allow(dead_code)] 8 | pub type Token<'i> = RustemoToken<'i, Input, TokenKind>; 9 | pub type Num = String; 10 | pub fn num(_ctx: &Ctx, token: Token) -> Num { 11 | token.value.into() 12 | } 13 | #[derive(Debug, Clone)] 14 | pub struct A { 15 | pub b1: B1, 16 | pub num: Num, 17 | } 18 | pub fn a_c1(_ctx: &Ctx, b1: B1, num: Num) -> A { 19 | A { b1, num } 20 | } 21 | pub type B1 = Vec; 22 | pub fn b1_c1(_ctx: &Ctx, mut b1: B1, b: B) -> B1 { 23 | b1.push(b); 24 | b1 25 | } 26 | pub fn b1_b(_ctx: &Ctx, b: B) -> B1 { 27 | vec![b] 28 | } 29 | #[derive(Debug, Clone)] 30 | pub enum B { 31 | Tb, 32 | } 33 | pub fn b_tb(_ctx: &Ctx) -> B { 34 | B::Tb 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/partial/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | use self::partial::PartialParser; 5 | 6 | rustemo_mod!(partial, "/src/partial"); 7 | rustemo_mod!(partial_actions, "/src/partial"); 8 | 9 | #[test] 10 | fn partial_parse() { 11 | let result = PartialParser::new().parse("Numbers: 1 7 42 b b whatever .... bla bla"); 12 | output_cmp!("src/partial/partial.ast", format!("{:#?}", result)); 13 | } 14 | -------------------------------------------------------------------------------- /tests/src/partial/partial.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | "1", 4 | "7", 5 | "42", 6 | ], 7 | ) -------------------------------------------------------------------------------- /tests/src/partial/partial.rustemo: -------------------------------------------------------------------------------- 1 | S: 'Numbers:' Num+; 2 | terminals 3 | Num: /\d+/; 4 | Numbers: 'Numbers:'; 5 | -------------------------------------------------------------------------------- /tests/src/rule_patterns/one_or_more.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | "1", 4 | "2", 5 | "3", 6 | ], 7 | ) -------------------------------------------------------------------------------- /tests/src/rule_patterns/one_or_more.rustemo: -------------------------------------------------------------------------------- 1 | @vec 2 | A: A Num | Num; 3 | 4 | terminals 5 | Num: /\d+/; 6 | -------------------------------------------------------------------------------- /tests/src/rule_patterns/optional.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Some( 3 | "1", 4 | ), 5 | ) -------------------------------------------------------------------------------- /tests/src/rule_patterns/optional.rustemo: -------------------------------------------------------------------------------- 1 | A: B | EMPTY; 2 | B: Num; 3 | 4 | terminals 5 | Num: /\d+/; 6 | -------------------------------------------------------------------------------- /tests/src/rule_patterns/zero_or_more_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | "1", 4 | "2", 5 | "3", 6 | ], 7 | ) -------------------------------------------------------------------------------- /tests/src/rule_patterns/zero_or_more_1.rustemo: -------------------------------------------------------------------------------- 1 | @vec 2 | A: A Num | Num | EMPTY; 3 | 4 | terminals 5 | Num: /\d+/; 6 | -------------------------------------------------------------------------------- /tests/src/rule_patterns/zero_or_more_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Some( 3 | [ 4 | "1", 5 | "2", 6 | "3", 7 | ], 8 | ), 9 | ) -------------------------------------------------------------------------------- /tests/src/rule_patterns/zero_or_more_2.rustemo: -------------------------------------------------------------------------------- 1 | @vec 2 | A: A1 | EMPTY; 3 | @vec 4 | A1: A1 Num | Num; 5 | 6 | terminals 7 | Num: /\d+/; 8 | -------------------------------------------------------------------------------- /tests/src/special/denny_pager_menhir/denny.rustemo: -------------------------------------------------------------------------------- 1 | // Grammar from the paper (Fig 4): 2 | // Denny, Joel E., and Brian A. Malloy. "The IELR (1) algorithm for generating 3 | // minimal LR (1) parser tables for non-LR (1) grammars with conflict 4 | // resolution." Science of Computer Programming 75.11 (2010): 943-979. 5 | // 6 | // This grammar has RR conflicts in LALR(1) tables 7 | // 8 | // rcomp --table-type lalr denny.rustemo 9 | // 10 | // Also, grammar shows the problem with Pager's weak compatibility test which 11 | // doesn't work on a token level which can still case mysterious new RR 12 | // conflicts. Pager's variant implemented in Menhir and used by Rustemo doesn't 13 | // have this problem. 14 | S: "a" A "a" 15 | | "a" A "b" 16 | | "a" B "a" 17 | | "b" A "a" 18 | | "b" B "b"; 19 | A: "a"; 20 | B: "a"; 21 | 22 | terminals 23 | Ta: "a"; 24 | Tb: "b"; 25 | -------------------------------------------------------------------------------- /tests/src/special/lalr_reduce_reduce_conflict/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Naive merging of states can lead to R/R conflict as shown in this grammar 2 | // from the Dragon Book. 3 | // But the Pager's LALR state compression algorithm used in Rustemo doesn't 4 | // exibit this problem. 5 | 6 | S: 'a' A 'd' | 'b' B 'd' | 'a' B 'e' | 'b' A 'e'; 7 | A: C; 8 | B: C; 9 | C: 'c'; 10 | 11 | terminals 12 | Ta: 'a'; 13 | Tb: 'b'; 14 | Tc: 'c'; 15 | Td: 'd'; 16 | Te: 'e'; 17 | -------------------------------------------------------------------------------- /tests/src/special/lalr_reduce_reduce_conflict/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/special/lalr_reduce_reduce_conflict"); 5 | rustemo_mod!(lang_actions, "/src/special/lalr_reduce_reduce_conflict"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn special_lalr_reduce_reduce_conflict() { 10 | let result = LangParser::new().parse("a c d"); 11 | 12 | output_cmp!( 13 | "src/special/lalr_reduce_reduce_conflict/tree.ast", 14 | format!("{result:#?}") 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /tests/src/special/lalr_reduce_reduce_conflict/tree.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A1( 3 | Tc, 4 | ), 5 | ) -------------------------------------------------------------------------------- /tests/src/special/lalrpop768/lalrpop768.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | U( 3 | X1( 4 | Tx, 5 | ), 6 | ), 7 | ) -------------------------------------------------------------------------------- /tests/src/special/lalrpop768/lalrpop768.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar is from LALRPOP issue 768: https://github.com/lalrpop/lalrpop/issues/768 2 | // It is accepted by Rustemo with Pager's LALR state splitting 3 | A: T "a" | U "a"; 4 | T: "t" X1 | "t" X2 "b"; 5 | U: "u" X2 | "u" X1 "b"; 6 | X1: "x"; 7 | X2: "x"; 8 | 9 | terminals 10 | Ta: "a"; 11 | Tb: "b"; 12 | Tt: "t"; 13 | Tu: "u"; 14 | Tx: "x"; 15 | -------------------------------------------------------------------------------- /tests/src/special/lalrpop768/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lalrpop768, "/src/special/lalrpop768"); 5 | rustemo_mod!(lalrpop768_actions, "/src/special/lalrpop768"); 6 | use self::lalrpop768::Lalrpop768Parser; 7 | 8 | #[test] 9 | fn pager_g1() { 10 | let result = Lalrpop768Parser::new().parse("u x b a"); 11 | 12 | output_cmp!( 13 | "src/special/lalrpop768/lalrpop768.ast", 14 | format!("{result:#?}") 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /tests/src/special/mod.rs: -------------------------------------------------------------------------------- 1 | //! Special grammars from the literature. 2 | 3 | mod lalr_reduce_reduce_conflict; 4 | mod lalrpop768; 5 | mod nondeterministic_palindromes; 6 | mod pager_g1; 7 | -------------------------------------------------------------------------------- /tests/src/special/nondeterministic_palindromes/lang.rustemo: -------------------------------------------------------------------------------- 1 | // Language of even length palindromes. 2 | // 3 | // This is a non-deterministic grammar and the language is non-ambiguous. 4 | // 5 | // If the string is a even length palindrome parser should reduce EMPTY at he 6 | // middle of the string and start to reduce by A and B. 7 | // 8 | // LR parsing is deterministic so this grammar can't parse the input as the 9 | // EMPTY reduction will be tried only after consuming all the input by implicit 10 | // disambiguation strategy of favouring shifts over empty reductions. 11 | // 12 | // OTOH, GLR parser can handle this by forking parser at each step and trying 13 | // both empty reductions and shifts. Only the parser that has reduced empty at 14 | // the middle of the input will succeed. 15 | // 16 | // Check the same test in glr/special directory. 17 | 18 | S: A | B | EMPTY; 19 | A: '1' S '1'; 20 | B: '0' S '0'; 21 | 22 | terminals 23 | 24 | One: '1'; 25 | Zero: '0'; 26 | -------------------------------------------------------------------------------- /tests/src/special/nondeterministic_palindromes/message.err: -------------------------------------------------------------------------------- 1 | Err( 2 | Error { 3 | message: "...01100100100110-->...\nExpected one of One, Zero.", 4 | file: Some( 5 | "", 6 | ), 7 | location: Some( 8 | [1,14], 9 | ), 10 | }, 11 | ) -------------------------------------------------------------------------------- /tests/src/special/nondeterministic_palindromes/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(lang, "/src/special/nondeterministic_palindromes"); 5 | rustemo_mod!(lang_actions, "/src/special/nondeterministic_palindromes"); 6 | use self::lang::LangParser; 7 | 8 | #[test] 9 | fn special_nondeterministic_palindromes() { 10 | let result = LangParser::new().parse("01100100100110"); 11 | 12 | output_cmp!( 13 | "src/special/nondeterministic_palindromes/message.err", 14 | format!("{result:#?}") 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /tests/src/special/pager_g1/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | rustemo_mod!(pager_g1, "/src/special/pager_g1"); 5 | rustemo_mod!(pager_g1_actions, "/src/special/pager_g1"); 6 | use self::pager_g1::PagerG1Parser; 7 | 8 | #[test] 9 | fn pager_g1() { 10 | let result = PagerG1Parser::new().parse("b e e c"); 11 | 12 | output_cmp!("src/special/pager_g1/pager_g1.ast", format!("{result:#?}")); 13 | } 14 | -------------------------------------------------------------------------------- /tests/src/special/pager_g1/pager_g1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | X2( 3 | X( 4 | Te, 5 | ), 6 | ), 7 | ) -------------------------------------------------------------------------------- /tests/src/special/pager_g1/pager_g1.rustemo: -------------------------------------------------------------------------------- 1 | // This grammar is from the paper: 2 | // David Pager and Xin Chen: The Lane Table Method Of Constructing LR(1) 3 | // Parsers, APPLC’12, June 14, 2012, Beijing, China. 4 | // 5 | // This grammar is not LALR(1) which can be verified by running: 6 | // rcomp --dot --table-type lalr pager_g1.rustemo 7 | // State 4 has R/R conflicts 8 | // 9 | // Pager's state splitting removes the conflict 10 | // rcomp --dot --table-type lalr-pager pager_g1.rustemo 11 | // 12 | // Some relevant discussions from LALRPOP project can be found here: 13 | // https://github.com/lalrpop/lalrpop/tree/master/lalrpop/src/lr1/lane_table 14 | // https://github.com/lalrpop/lalrpop/issues/851 15 | // 16 | G1: "a" X "d" 17 | | "a" Y "c" 18 | | "b" X "c" 19 | | "b" Y "d"; 20 | X: "e" X 21 | | "e"; 22 | Y: "e" Y 23 | | "e"; 24 | 25 | terminals 26 | Ta: "a"; 27 | Tb: "b"; 28 | Tc: "c"; 29 | Td: "d"; 30 | Te: "e"; 31 | -------------------------------------------------------------------------------- /tests/src/sugar/mod.rs: -------------------------------------------------------------------------------- 1 | mod one_or_more; 2 | mod optional; 3 | mod zero_or_more; 4 | -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B Num+; 2 | B: 'b' | EMPTY; 3 | 4 | terminals 5 | 6 | Num: /\d+/; 7 | Tb: 'b'; 8 | Tc: 'c'; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: Some( 4 | Tb, 5 | ), 6 | num1: [ 7 | "1", 8 | "2", 9 | "3", 10 | "4", 11 | ], 12 | }, 13 | ) -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,0]: 2 | ...-->1 2 3 4... 3 | Expected Tc. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_1_sep.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: Some( 4 | Tb, 5 | ), 6 | num1: [ 7 | "1", 8 | "2", 9 | "3", 10 | "4", 11 | ], 12 | }, 13 | ) -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_1_sep.err: -------------------------------------------------------------------------------- 1 | Error at :[1,8]: 2 | ...c b 1, 2-->; 3, 4... 3 | Expected one of STOP, Comma. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: None, 4 | num1: [ 5 | "1", 6 | ], 7 | }, 8 | ) -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,4]: 2 | ...c b -->b 1 2 3 4... 3 | Expected Num. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_3.err: -------------------------------------------------------------------------------- 1 | Error at :[1,3]: 2 | ...c b-->... 3 | Expected Num. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_1_sep.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B Num+[Comma]; 2 | B: 'b' | EMPTY; 3 | 4 | terminals 5 | Num: /\d+/; 6 | Comma: ','; 7 | Tb: 'b'; 8 | Tc: 'c'; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B+ Ta; 2 | B: Num; 3 | 4 | terminals 5 | 6 | Ta: 'a'; 7 | Tc: 'c'; 8 | Num: /\d+/; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | "1", 4 | ], 5 | ) -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,7]: 2 | ...c 1 2 3-->... 3 | Expected one of Ta, Num. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | [ 3 | "1", 4 | "2", 5 | "3", 6 | "4", 7 | ], 8 | ) -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,2]: 2 | ...c -->a... 3 | Expected Num. -------------------------------------------------------------------------------- /tests/src/sugar/one_or_more/one_or_more_2_3.err: -------------------------------------------------------------------------------- 1 | Error at :[1,8]: 2 | ...c 1 2 a -->3... 3 | Expected STOP. -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c'? B Num?; 2 | B: 'b'; 3 | 4 | terminals 5 | 6 | Tb: 'b'; 7 | Tc: 'c'; 8 | Num: /\d+/; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | tc_opt: Some( 4 | Tc, 5 | ), 6 | b: Tb, 7 | num_opt: Some( 8 | "1", 9 | ), 10 | }, 11 | ) -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,2]: 2 | ...c -->1... 3 | Expected Tb. -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | tc_opt: Some( 4 | Tc, 5 | ), 6 | b: Tb, 7 | num_opt: None, 8 | }, 9 | ) -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,6]: 2 | ...c b 1 -->2... 3 | Expected STOP. -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_1_3.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | tc_opt: None, 4 | b: Tb, 5 | num_opt: Some( 6 | "1", 7 | ), 8 | }, 9 | ) -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B? Ta?; 2 | B: Num; 3 | 4 | terminals 5 | 6 | Ta: 'a'; 7 | Tc: 'c'; 8 | Num: /\d+/; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | bopt: Some( 4 | "1", 5 | ), 6 | ta_opt: None, 7 | }, 8 | ) -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,4]: 2 | ...c 1 -->2... 3 | Expected one of STOP, Ta. -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | bopt: Some( 4 | "1", 5 | ), 6 | ta_opt: Some( 7 | Ta, 8 | ), 9 | }, 10 | ) -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,4]: 2 | ...c a -->a... 3 | Expected STOP. -------------------------------------------------------------------------------- /tests/src/sugar/optional/optional_2_3.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | bopt: None, 4 | ta_opt: Some( 5 | Ta, 6 | ), 7 | }, 8 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B Ta*; 2 | B: 'b' | EMPTY; 3 | 4 | terminals 5 | 6 | Ta: 'a'; 7 | Tb: 'b'; 8 | Tc: 'c'; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: Some( 4 | Tb, 5 | ), 6 | ta0: Some( 7 | Ta1( 8 | Ta1( 9 | Ta1( 10 | Ta, 11 | ), 12 | ), 13 | ), 14 | ), 15 | }, 16 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,0]: 2 | ...-->a a a a... 3 | Expected Tc. -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_1_sep.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: Some( 4 | Tb, 5 | ), 6 | ta0: Some( 7 | Ta1( 8 | Ta1( 9 | Ta1( 10 | Ta, 11 | ), 12 | ), 13 | ), 14 | ), 15 | }, 16 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_1_sep.err: -------------------------------------------------------------------------------- 1 | Error at :[1,9]: 2 | ...c b a, a -->a, a... 3 | Expected one of STOP, Comma. -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: None, 4 | ta0: Some( 5 | Ta1( 6 | Ta1( 7 | Ta1( 8 | Ta, 9 | ), 10 | ), 11 | ), 12 | ), 13 | }, 14 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,4]: 2 | ...c b -->b a a a a... 3 | Expected one of STOP, Ta. -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_3.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | A { 3 | b: None, 4 | ta0: None, 5 | }, 6 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_1_sep.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B Ta*[Comma]; 2 | B: 'b' | EMPTY; 3 | 4 | terminals 5 | 6 | Ta: 'a'; 7 | Tb: 'b'; 8 | Tc: 'c'; 9 | Comma: ','; 10 | -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_2.rustemo: -------------------------------------------------------------------------------- 1 | A: 'c' B* Ta; 2 | B: Num; 3 | 4 | terminals 5 | 6 | Ta: 'a'; 7 | Tc: 'c'; 8 | Num: /\d+/; 9 | -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_2_1.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | Some( 3 | [ 4 | "1", 5 | "2", 6 | "3", 7 | ], 8 | ), 9 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_2_1.err: -------------------------------------------------------------------------------- 1 | Error at :[1,2]: 2 | ...c -->c a... 3 | Expected one of Ta, Num. -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_2_2.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | None, 3 | ) -------------------------------------------------------------------------------- /tests/src/sugar/zero_or_more/zero_or_more_2_2.err: -------------------------------------------------------------------------------- 1 | Error at :[1,8]: 2 | ...c 1 2 a -->a... 3 | Expected STOP. -------------------------------------------------------------------------------- /tests/src/unicode/mod.rs: -------------------------------------------------------------------------------- 1 | use rustemo::{rustemo_mod, Parser}; 2 | use rustemo_compiler::output_cmp; 3 | 4 | use self::unicode::UnicodeParser; 5 | 6 | rustemo_mod!(unicode, "/src/unicode"); 7 | rustemo_mod!(unicode_actions, "/src/unicode"); 8 | 9 | #[test] 10 | fn partial_parse() { 11 | let result = UnicodeParser::new().parse("Тестирање: čokančićem ћу те, чоканчићем ћеш ме."); 12 | output_cmp!("src/unicode/unicode.ast", format!("{:#?}", result)); 13 | } 14 | -------------------------------------------------------------------------------- /tests/src/unicode/unicode.ast: -------------------------------------------------------------------------------- 1 | Ok( 2 | File { 3 | word1_3: [ 4 | "čokančićem", 5 | "ћу", 6 | "те", 7 | ], 8 | word1_5: [ 9 | "чоканчићем", 10 | "ћеш", 11 | "ме", 12 | ], 13 | }, 14 | ) -------------------------------------------------------------------------------- /tests/src/unicode/unicode.rustemo: -------------------------------------------------------------------------------- 1 | File: Testing Colon Word+ Comma Word+ Dot; 2 | 3 | terminals 4 | 5 | Testing: 'Тестирање'; 6 | Word: /\w+/; 7 | Colon: ':'; 8 | Comma: ','; 9 | Dot: '.'; 10 | --------------------------------------------------------------------------------