├── .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 |
--------------------------------------------------------------------------------