ibex 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +247 -104
- data/docs/architecture.md +322 -28
- data/docs/cst-migration.md +107 -0
- data/docs/cst.md +213 -0
- data/docs/development.md +129 -0
- data/docs/editor-setup.md +25 -0
- data/docs/error-ux.md +60 -0
- data/docs/grammar-reference.md +458 -18
- data/docs/lexer-migration.md +51 -0
- data/docs/racc-migration.md +34 -6
- data/docs/release-readiness.md +163 -0
- data/docs/stability.md +124 -0
- data/examples/README.md +60 -0
- data/examples/calculator.y +44 -0
- data/examples/csv.y +32 -0
- data/examples/ini.y +70 -0
- data/examples/json.y +48 -0
- data/examples/tiny_language.y +75 -0
- data/lib/ibex/analysis/sets.rb +5 -4
- data/lib/ibex/artifact_set.rb +63 -0
- data/lib/ibex/cli/ambiguity.rb +66 -0
- data/lib/ibex/cli/counterexample_options.rb +6 -2
- data/lib/ibex/cli/coverage.rb +173 -0
- data/lib/ibex/cli/debug.rb +106 -0
- data/lib/ibex/cli/diagnostics.rb +116 -0
- data/lib/ibex/cli/documentation.rb +67 -0
- data/lib/ibex/cli/error_messages.rb +177 -0
- data/lib/ibex/cli/explain.rb +76 -0
- data/lib/ibex/cli/formatting.rb +386 -0
- data/lib/ibex/cli/generation_artifacts.rb +138 -0
- data/lib/ibex/cli/generation_error_messages.rb +39 -0
- data/lib/ibex/cli/grammar_tests.rb +122 -0
- data/lib/ibex/cli/ir_tools.rb +184 -0
- data/lib/ibex/cli/lsp.rb +33 -0
- data/lib/ibex/cli/outputs.rb +91 -8
- data/lib/ibex/cli/racc_migration.rb +87 -0
- data/lib/ibex/cli/samples.rb +92 -0
- data/lib/ibex/cli/watch.rb +103 -0
- data/lib/ibex/cli.rb +506 -37
- data/lib/ibex/codegen/action_locations.rb +103 -0
- data/lib/ibex/codegen/action_method_source.rb +210 -0
- data/lib/ibex/codegen/action_source.rb +118 -0
- data/lib/ibex/codegen/ambiguity.rb +173 -0
- data/lib/ibex/codegen/cst_metadata.rb +171 -0
- data/lib/ibex/codegen/documentation.rb +170 -0
- data/lib/ibex/codegen/explain.rb +312 -0
- data/lib/ibex/codegen/generated_action_abi.rb +288 -0
- data/lib/ibex/codegen/html.rb +94 -10
- data/lib/ibex/codegen/mermaid.rb +43 -0
- data/lib/ibex/codegen/railroad.rb +197 -0
- data/lib/ibex/codegen/railroad_documentation.rb +59 -0
- data/lib/ibex/codegen/rbs.rb +323 -2
- data/lib/ibex/codegen/report.rb +46 -6
- data/lib/ibex/codegen/ruby.rb +334 -59
- data/lib/ibex/codegen/ruby_actions.rb +143 -0
- data/lib/ibex/codegen/ruby_ast.rb +121 -0
- data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
- data/lib/ibex/codegen/ruby_lexer.rb +83 -0
- data/lib/ibex/codegen/ruby_syntax.rb +90 -0
- data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
- data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
- data/lib/ibex/codegen/symbol_labels.rb +1 -1
- data/lib/ibex/coverage/collector.rb +188 -0
- data/lib/ibex/coverage/event_stream.rb +97 -0
- data/lib/ibex/coverage/report.rb +259 -0
- data/lib/ibex/coverage/runtime_event_validator.rb +160 -0
- data/lib/ibex/coverage.rb +13 -0
- data/lib/ibex/error_messages/parser.rb +159 -0
- data/lib/ibex/error_messages/parser_v2.rb +198 -0
- data/lib/ibex/error_messages/renderer.rb +65 -0
- data/lib/ibex/error_messages/sentence_search.rb +196 -0
- data/lib/ibex/error_messages/update.rb +169 -0
- data/lib/ibex/error_messages.rb +165 -0
- data/lib/ibex/frontend/ast.rb +145 -6
- data/lib/ibex/frontend/bootstrap_parser.rb +47 -4
- data/lib/ibex/frontend/diagnostic.rb +81 -0
- data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
- data/lib/ibex/frontend/dsl.rb +66 -7
- data/lib/ibex/frontend/formatter.rb +407 -0
- data/lib/ibex/frontend/generated_parser.rb +614 -190
- data/lib/ibex/frontend/generated_parser_base.rb +164 -30
- data/lib/ibex/frontend/generated_parser_includes.rb +61 -0
- data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
- data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
- data/lib/ibex/frontend/generation.rb +33 -0
- data/lib/ibex/frontend/lexer.rb +162 -10
- data/lib/ibex/frontend/lexer_recovery.rb +84 -0
- data/lib/ibex/frontend/parser/declarations.rb +247 -11
- data/lib/ibex/frontend/parser/parameters.rb +82 -0
- data/lib/ibex/frontend/parser/rules.rb +43 -6
- data/lib/ibex/frontend/parser.rb +182 -4
- data/lib/ibex/frontend/regenerator.rb +26 -1
- data/lib/ibex/frontend/resolution.rb +69 -0
- data/lib/ibex/frontend/resolver.rb +217 -0
- data/lib/ibex/frontend/rule_documentation.rb +103 -0
- data/lib/ibex/frontend/source_cursor.rb +132 -8
- data/lib/ibex/frontend/source_document.rb +229 -0
- data/lib/ibex/frontend/source_loader.rb +150 -0
- data/lib/ibex/frontend/source_span.rb +81 -0
- data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
- data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
- data/lib/ibex/frontend/token_adapter/declaration_state.rb +216 -26
- data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
- data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
- data/lib/ibex/frontend/token_adapter.rb +8 -3
- data/lib/ibex/frontend.rb +13 -2
- data/lib/ibex/generation_input.rb +57 -0
- data/lib/ibex/generation_manifest.rb +200 -0
- data/lib/ibex/generation_transaction.rb +261 -0
- data/lib/ibex/generation_transaction_recovery.rb +109 -0
- data/lib/ibex/generation_transaction_validation.rb +196 -0
- data/lib/ibex/grammar_tests.rb +206 -0
- data/lib/ibex/ir/automaton_ir.rb +38 -5
- data/lib/ibex/ir/grammar_ir.rb +137 -24
- data/lib/ibex/ir/lexer_ir.rb +76 -0
- data/lib/ibex/ir/migration.rb +120 -0
- data/lib/ibex/ir/serialize.rb +110 -19
- data/lib/ibex/ir/validator/automaton.rb +345 -0
- data/lib/ibex/ir/validator/base.rb +129 -0
- data/lib/ibex/ir/validator/grammar.rb +604 -0
- data/lib/ibex/ir/validator/lexer.rb +113 -0
- data/lib/ibex/ir/validator.rb +62 -0
- data/lib/ibex/ir.rb +57 -4
- data/lib/ibex/lalr/build_metrics.rb +23 -0
- data/lib/ibex/lalr/builder.rb +379 -52
- data/lib/ibex/lalr/conflict.rb +1 -0
- data/lib/ibex/lalr/conflict_search.rb +11 -5
- data/lib/ibex/lalr/counterexample.rb +20 -5
- data/lib/ibex/lalr/direct_lookaheads.rb +236 -0
- data/lib/ibex/lalr/ielr_partition.rb +152 -0
- data/lib/ibex/lalr/on_error_reductions.rb +74 -0
- data/lib/ibex/lalr.rb +7 -0
- data/lib/ibex/location.rb +129 -0
- data/lib/ibex/lsp/document_handlers.rb +66 -0
- data/lib/ibex/lsp/document_store.rb +264 -0
- data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
- data/lib/ibex/lsp/document_store_validation.rb +61 -0
- data/lib/ibex/lsp/initialization_handlers.rb +78 -0
- data/lib/ibex/lsp/navigation_handlers.rb +56 -0
- data/lib/ibex/lsp/position_codec.rb +109 -0
- data/lib/ibex/lsp/protocol_error.rb +33 -0
- data/lib/ibex/lsp/request_handlers.rb +45 -0
- data/lib/ibex/lsp/request_support.rb +87 -0
- data/lib/ibex/lsp/server.rb +146 -0
- data/lib/ibex/lsp/symbol_index.rb +241 -0
- data/lib/ibex/lsp/symbol_index_builder.rb +267 -0
- data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
- data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
- data/lib/ibex/lsp/symbol_occurrence.rb +17 -0
- data/lib/ibex/lsp/transport.rb +118 -0
- data/lib/ibex/lsp/workspace.rb +127 -0
- data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
- data/lib/ibex/lsp.rb +32 -0
- data/lib/ibex/normalize/declarations.rb +140 -7
- data/lib/ibex/normalize/diagnostics.rb +50 -5
- data/lib/ibex/normalize/expander.rb +59 -55
- data/lib/ibex/normalize/expression.rb +60 -39
- data/lib/ibex/normalize/inline_expansion.rb +414 -0
- data/lib/ibex/normalize/inline_validation.rb +174 -0
- data/lib/ibex/normalize/lexer.rb +131 -0
- data/lib/ibex/normalize/named_references.rb +60 -0
- data/lib/ibex/normalize/nodes.rb +46 -0
- data/lib/ibex/normalize/parameter_ebnf_lowering.rb +69 -0
- data/lib/ibex/normalize/parameter_lowering.rb +126 -0
- data/lib/ibex/normalize/parameter_substitution.rb +125 -0
- data/lib/ibex/normalize/parameter_validation.rb +140 -0
- data/lib/ibex/normalize/parameters.rb +199 -0
- data/lib/ibex/normalize/recovery_declarations.rb +84 -0
- data/lib/ibex/normalize.rb +179 -14
- data/lib/ibex/racc_migration/checker.rb +122 -0
- data/lib/ibex/racc_migration/harness.rb +177 -0
- data/lib/ibex/racc_migration/report.rb +94 -0
- data/lib/ibex/racc_migration.rb +12 -0
- data/lib/ibex/rake_task.rb +116 -0
- data/lib/ibex/samples.rb +186 -0
- data/lib/ibex/table_simulation/result.rb +51 -0
- data/lib/ibex/table_simulation/simulator.rb +253 -0
- data/lib/ibex/table_simulation/step.rb +60 -0
- data/lib/ibex/table_simulation/text.rb +31 -0
- data/lib/ibex/table_simulation.rb +13 -0
- data/lib/ibex/tables.rb +9 -70
- data/lib/ibex/version.rb +1 -1
- data/lib/ibex/watch/runner.rb +172 -0
- data/lib/ibex/watch/source_snapshot.rb +93 -0
- data/lib/ibex/watch.rb +11 -0
- data/lib/ibex.rb +25 -1
- data/schema/automaton-ir-v1.schema.json +401 -0
- data/schema/automaton-ir-v2.schema.json +58 -0
- data/schema/benchmark-v1.schema.json +212 -0
- data/schema/benchmark-v2.schema.json +61 -0
- data/schema/cst-v1.json +170 -0
- data/schema/error-ux-v1.schema.json +258 -0
- data/schema/explain-v1.schema.json +433 -0
- data/schema/frontend-diagnostics-v1.schema.json +154 -0
- data/schema/generation-manifest-v1.schema.json +115 -0
- data/schema/grammar-ir-v1.schema.json +426 -0
- data/schema/grammar-ir-v2.schema.json +779 -0
- data/schema/lexer-ir-v1.schema.json +215 -0
- data/schema/migration-check-v1.schema.json +60 -0
- data/schema/performance-comparison-v1.schema.json +395 -0
- data/schema/public-performance-comparison-v1.schema.json +506 -0
- data/schema/public-performance-profile-v1.schema.json +360 -0
- data/schema/runtime-coverage-v1.schema.json +86 -0
- data/schema/runtime-event-v1.schema.json +308 -0
- data/schema/table-simulation-v1.schema.json +70 -0
- data/sig/ibex/artifact_set.rbs +37 -0
- data/sig/ibex/cli/ambiguity.rbs +22 -0
- data/sig/ibex/cli/counterexample_options.rbs +2 -0
- data/sig/ibex/cli/coverage.rbs +53 -0
- data/sig/ibex/cli/debug.rbs +28 -0
- data/sig/ibex/cli/diagnostics.rbs +38 -0
- data/sig/ibex/cli/documentation.rbs +25 -0
- data/sig/ibex/cli/error_messages.rbs +57 -0
- data/sig/ibex/cli/explain.rbs +25 -0
- data/sig/ibex/cli/formatting.rbs +103 -0
- data/sig/ibex/cli/generation_artifacts.rbs +50 -0
- data/sig/ibex/cli/generation_error_messages.rbs +19 -0
- data/sig/ibex/cli/grammar_tests.rbs +36 -0
- data/sig/ibex/cli/ir_tools.rbs +51 -0
- data/sig/ibex/cli/lsp.rbs +14 -0
- data/sig/ibex/cli/outputs.rbs +17 -0
- data/sig/ibex/cli/racc_migration.rbs +30 -0
- data/sig/ibex/cli/samples.rbs +30 -0
- data/sig/ibex/cli/watch.rbs +43 -0
- data/sig/ibex/cli.rbs +104 -5
- data/sig/ibex/codegen/action_locations.rbs +45 -0
- data/sig/ibex/codegen/action_method_source.rbs +65 -0
- data/sig/ibex/codegen/action_source.rbs +50 -0
- data/sig/ibex/codegen/ambiguity.rbs +60 -0
- data/sig/ibex/codegen/cst_metadata.rbs +59 -0
- data/sig/ibex/codegen/documentation.rbs +50 -0
- data/sig/ibex/codegen/explain.rbs +85 -0
- data/sig/ibex/codegen/generated_action_abi.rbs +101 -0
- data/sig/ibex/codegen/html.rbs +18 -2
- data/sig/ibex/codegen/mermaid.rbs +16 -0
- data/sig/ibex/codegen/railroad.rbs +82 -0
- data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
- data/sig/ibex/codegen/rbs.rbs +84 -4
- data/sig/ibex/codegen/report.rbs +8 -0
- data/sig/ibex/codegen/ruby.rbs +97 -23
- data/sig/ibex/codegen/ruby_actions.rbs +54 -0
- data/sig/ibex/codegen/ruby_ast.rbs +34 -0
- data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
- data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
- data/sig/ibex/codegen/ruby_syntax.rbs +25 -0
- data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
- data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
- data/sig/ibex/coverage/collector.rbs +76 -0
- data/sig/ibex/coverage/event_stream.rbs +42 -0
- data/sig/ibex/coverage/report.rbs +100 -0
- data/sig/ibex/coverage/runtime_event_validator.rbs +68 -0
- data/sig/ibex/coverage.rbs +7 -0
- data/sig/ibex/error_messages/parser.rbs +58 -0
- data/sig/ibex/error_messages/parser_v2.rbs +67 -0
- data/sig/ibex/error_messages/renderer.rbs +23 -0
- data/sig/ibex/error_messages/sentence_search.rbs +80 -0
- data/sig/ibex/error_messages/update.rbs +43 -0
- data/sig/ibex/error_messages.rbs +85 -0
- data/sig/ibex/frontend/ast.rbs +208 -19
- data/sig/ibex/frontend/bootstrap_parser.rbs +11 -0
- data/sig/ibex/frontend/diagnostic.rbs +53 -0
- data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
- data/sig/ibex/frontend/dsl.rbs +33 -4
- data/sig/ibex/frontend/formatter.rbs +135 -0
- data/sig/ibex/frontend/generated_parser.rbs +218 -68
- data/sig/ibex/frontend/generated_parser_base.rbs +61 -10
- data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
- data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
- data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
- data/sig/ibex/frontend/generation.rbs +6 -0
- data/sig/ibex/frontend/lexer.rbs +49 -2
- data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
- data/sig/ibex/frontend/parser/declarations.rbs +54 -0
- data/sig/ibex/frontend/parser/parameters.rbs +28 -0
- data/sig/ibex/frontend/parser/rules.rbs +3 -0
- data/sig/ibex/frontend/parser.rbs +66 -0
- data/sig/ibex/frontend/regenerator.rbs +11 -0
- data/sig/ibex/frontend/resolution.rbs +33 -0
- data/sig/ibex/frontend/resolver.rbs +91 -0
- data/sig/ibex/frontend/rule_documentation.rbs +42 -0
- data/sig/ibex/frontend/source_cursor.rbs +36 -3
- data/sig/ibex/frontend/source_document.rbs +119 -0
- data/sig/ibex/frontend/source_loader.rbs +66 -0
- data/sig/ibex/frontend/source_span.rbs +53 -0
- data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
- data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
- data/sig/ibex/frontend/token_adapter/declaration_state.rbs +73 -5
- data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
- data/sig/ibex/frontend/token_adapter.rbs +5 -2
- data/sig/ibex/frontend.rbs +1 -1
- data/sig/ibex/generation_input.rbs +37 -0
- data/sig/ibex/generation_manifest.rbs +67 -0
- data/sig/ibex/generation_transaction.rbs +82 -0
- data/sig/ibex/generation_transaction_recovery.rbs +36 -0
- data/sig/ibex/generation_transaction_validation.rbs +65 -0
- data/sig/ibex/grammar_tests.rbs +93 -0
- data/sig/ibex/ir/automaton_ir.rbs +8 -2
- data/sig/ibex/ir/grammar_ir.rbs +75 -15
- data/sig/ibex/ir/lexer_ir.rbs +57 -0
- data/sig/ibex/ir/migration.rbs +34 -0
- data/sig/ibex/ir/serialize.rbs +23 -6
- data/sig/ibex/ir/validator/automaton.rbs +109 -0
- data/sig/ibex/ir/validator/base.rbs +65 -0
- data/sig/ibex/ir/validator/grammar.rbs +184 -0
- data/sig/ibex/ir/validator/lexer.rbs +37 -0
- data/sig/ibex/ir/validator.rbs +16 -0
- data/sig/ibex/ir.rbs +38 -4
- data/sig/ibex/lalr/build_metrics.rbs +20 -0
- data/sig/ibex/lalr/builder.rbs +95 -15
- data/sig/ibex/lalr/conflict_search.rbs +7 -3
- data/sig/ibex/lalr/counterexample.rbs +5 -2
- data/sig/ibex/lalr/direct_lookaheads.rbs +86 -0
- data/sig/ibex/lalr/ielr_partition.rbs +59 -0
- data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
- data/sig/ibex/lalr.rbs +6 -0
- data/sig/ibex/location.rbs +67 -0
- data/sig/ibex/lsp/document_handlers.rbs +24 -0
- data/sig/ibex/lsp/document_store.rbs +94 -0
- data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
- data/sig/ibex/lsp/document_store_validation.rbs +26 -0
- data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
- data/sig/ibex/lsp/navigation_handlers.rbs +30 -0
- data/sig/ibex/lsp/position_codec.rbs +40 -0
- data/sig/ibex/lsp/protocol_error.rbs +32 -0
- data/sig/ibex/lsp/request_handlers.rbs +26 -0
- data/sig/ibex/lsp/request_support.rbs +40 -0
- data/sig/ibex/lsp/server.rbs +55 -0
- data/sig/ibex/lsp/symbol_index.rbs +78 -0
- data/sig/ibex/lsp/symbol_index_builder.rbs +87 -0
- data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
- data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
- data/sig/ibex/lsp/symbol_occurrence.rbs +25 -0
- data/sig/ibex/lsp/transport.rbs +35 -0
- data/sig/ibex/lsp/workspace.rbs +41 -0
- data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
- data/sig/ibex/lsp.rbs +7 -0
- data/sig/ibex/normalize/declarations.rbs +31 -0
- data/sig/ibex/normalize/diagnostics.rbs +9 -0
- data/sig/ibex/normalize/expander.rbs +20 -14
- data/sig/ibex/normalize/expression.rbs +10 -10
- data/sig/ibex/normalize/inline_expansion.rbs +120 -0
- data/sig/ibex/normalize/inline_validation.rbs +40 -0
- data/sig/ibex/normalize/lexer.rbs +37 -0
- data/sig/ibex/normalize/named_references.rbs +20 -0
- data/sig/ibex/normalize/nodes.rbs +14 -0
- data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +32 -0
- data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
- data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
- data/sig/ibex/normalize/parameter_validation.rbs +44 -0
- data/sig/ibex/normalize/parameters.rbs +46 -0
- data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
- data/sig/ibex/normalize.rbs +123 -18
- data/sig/ibex/racc_migration/checker.rbs +36 -0
- data/sig/ibex/racc_migration/harness.rbs +16 -0
- data/sig/ibex/racc_migration/report.rbs +53 -0
- data/sig/ibex/racc_migration.rbs +8 -0
- data/sig/ibex/rake_task.rbs +51 -0
- data/sig/ibex/samples.rbs +51 -0
- data/sig/ibex/table_simulation/result.rbs +32 -0
- data/sig/ibex/table_simulation/simulator.rbs +98 -0
- data/sig/ibex/table_simulation/step.rbs +40 -0
- data/sig/ibex/table_simulation/text.rbs +14 -0
- data/sig/ibex/table_simulation.rbs +7 -0
- data/sig/ibex/tables.rbs +0 -26
- data/sig/ibex/watch/runner.rbs +48 -0
- data/sig/ibex/watch/source_snapshot.rbs +42 -0
- data/sig/ibex/watch.rbs +7 -0
- data/sig/ibex.rbs +2 -0
- metadata +301 -16
- data/.rubocop.yml +0 -43
- data/CHANGELOG.md +0 -30
- data/Rakefile +0 -25
- data/Steepfile +0 -10
- data/docs/compat-notes.md +0 -37
- data/docs/lexer-coverage.md +0 -14
- data/docs/phase10-extensions.md +0 -27
- data/gemfiles/Gemfile +0 -7
- data/gemfiles/Gemfile.lock +0 -98
- data/lib/ibex/frontend/grammar.y +0 -156
- data/lib/ibex/runtime/parser.rb +0 -360
- data/lib/ibex/runtime.rb +0 -8
- data/sig/ibex/runtime/parser.rbs +0 -167
- data/sig/ibex/runtime.rbs +0 -6
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Static validation and collection for rules eliminated by inline expansion.
|
|
5
|
+
module NormalizeInlineValidation
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs () -> void
|
|
9
|
+
def gather_inline_rules
|
|
10
|
+
# @type self: Normalizer
|
|
11
|
+
@inline_rule_names = Set.new #: Set[String]
|
|
12
|
+
markings = {} #: Hash[String, bool]
|
|
13
|
+
@ast.rules.each do |rule|
|
|
14
|
+
previous = markings[rule.lhs]
|
|
15
|
+
if !previous.nil? && previous != rule.inline
|
|
16
|
+
fail_at(rule.loc, "rule #{rule.lhs} has both inline and ordinary definitions")
|
|
17
|
+
end
|
|
18
|
+
markings[rule.lhs] = rule.inline
|
|
19
|
+
@inline_rule_names << rule.lhs if rule.inline
|
|
20
|
+
end
|
|
21
|
+
validate_inline_terminal_collisions
|
|
22
|
+
validate_inline_start
|
|
23
|
+
validate_inline_cycles
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @rbs () -> void
|
|
27
|
+
def validate_inline_terminal_collisions
|
|
28
|
+
# @type self: Normalizer
|
|
29
|
+
@ast.rules.each do |rule|
|
|
30
|
+
next unless rule.inline
|
|
31
|
+
next unless @declared_tokens.key?(rule.lhs) || @precedence.key?(rule.lhs)
|
|
32
|
+
|
|
33
|
+
fail_at(rule.loc, "inline rule #{rule.lhs} collides with terminal #{rule.lhs}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs () -> void
|
|
38
|
+
def validate_inline_start
|
|
39
|
+
# @type self: Normalizer
|
|
40
|
+
inline_start = @explicit_starts&.find { |name| @inline_rule_names.include?(name) }
|
|
41
|
+
return unless inline_start
|
|
42
|
+
|
|
43
|
+
fail_at(@start_location || @ast.loc, "inline rule #{inline_start} cannot be the start symbol")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @rbs () -> void
|
|
47
|
+
def validate_inline_cycles
|
|
48
|
+
# @type self: Normalizer
|
|
49
|
+
names = @ast.rules.to_set(&:lhs)
|
|
50
|
+
liveness = parameter_formal_liveness
|
|
51
|
+
graph = Hash.new { |hash, name| hash[name] = [] } #: Hash[String, Array[[String, Frontend::Location]]]
|
|
52
|
+
@ast.rules.each do |rule|
|
|
53
|
+
rule.alternatives.each do |alternative|
|
|
54
|
+
alternative.items.each do |item|
|
|
55
|
+
collect_inline_edges(item, rule.parameters, names, graph[rule.lhs], liveness)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
states = {} #: Hash[String, Symbol]
|
|
60
|
+
names.each { |name| visit_inline_cycle(name, graph, states) unless states.key?(name) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A formal is live for cycle analysis only when substituting its actual can
|
|
64
|
+
# introduce a grammar reference. Calls propagate liveness through their
|
|
65
|
+
# callee's live positions until the finite formal set reaches a fixed point.
|
|
66
|
+
# @rbs () -> Hash[String, Set[Integer]]
|
|
67
|
+
def parameter_formal_liveness
|
|
68
|
+
# @type self: Normalizer
|
|
69
|
+
liveness = @parameter_formals.to_h { |name, _formals| [name, Set.new] } #: Hash[String, Set[Integer]]
|
|
70
|
+
loop do
|
|
71
|
+
changed = false
|
|
72
|
+
@parameter_templates.each do |name, rules|
|
|
73
|
+
formals = @parameter_formals.fetch(name)
|
|
74
|
+
found = Set.new #: Set[Integer]
|
|
75
|
+
rules.each do |rule|
|
|
76
|
+
rule.alternatives.each do |alternative|
|
|
77
|
+
alternative.items.each { |item| collect_live_formals(item, formals, liveness, found) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
additions = found - liveness.fetch(name)
|
|
81
|
+
next if additions.empty?
|
|
82
|
+
|
|
83
|
+
liveness.fetch(name).merge(additions)
|
|
84
|
+
changed = true
|
|
85
|
+
end
|
|
86
|
+
return liveness unless changed
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @rbs (Frontend::AST::item item, Array[String] formals,
|
|
91
|
+
# Hash[String, Set[Integer]] liveness, Set[Integer] found) -> void
|
|
92
|
+
def collect_live_formals(item, formals, liveness, found)
|
|
93
|
+
# @type self: Normalizer
|
|
94
|
+
case item
|
|
95
|
+
when Frontend::AST::SymbolReference
|
|
96
|
+
index = formals.index(item.name)
|
|
97
|
+
found << index if index
|
|
98
|
+
when Frontend::AST::ParameterizedReference
|
|
99
|
+
liveness.fetch(item.name, Set.new).each do |index|
|
|
100
|
+
collect_live_formals(item.arguments.fetch(index), formals, liveness, found)
|
|
101
|
+
end
|
|
102
|
+
when Frontend::AST::Group
|
|
103
|
+
item.alternatives.flatten.each { |child| collect_live_formals(child, formals, liveness, found) }
|
|
104
|
+
when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
|
|
105
|
+
collect_live_formals(item.item, formals, liveness, found)
|
|
106
|
+
when Frontend::AST::SeparatedList
|
|
107
|
+
collect_live_formals(item.item, formals, liveness, found)
|
|
108
|
+
collect_live_formals(item.separator, formals, liveness, found)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Structural AST traversal necessarily branches once for each public item shape.
|
|
113
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
114
|
+
# @rbs (Frontend::AST::item item, Array[String] formals, Set[String] names,
|
|
115
|
+
# Array[[String, Frontend::Location]] edges, Hash[String, Set[Integer]] liveness) -> void
|
|
116
|
+
def collect_inline_edges(item, formals, names, edges, liveness)
|
|
117
|
+
# @type self: Normalizer
|
|
118
|
+
case item
|
|
119
|
+
when Frontend::AST::SymbolReference
|
|
120
|
+
edges << [item.name, item.loc] if !formals.include?(item.name) && names.include?(item.name)
|
|
121
|
+
when Frontend::AST::ParameterizedReference
|
|
122
|
+
edges << [item.name, item.loc] if names.include?(item.name)
|
|
123
|
+
liveness.fetch(item.name, Set.new).each do |index|
|
|
124
|
+
collect_inline_edges(item.arguments.fetch(index), formals, names, edges, liveness)
|
|
125
|
+
end
|
|
126
|
+
when Frontend::AST::Group
|
|
127
|
+
item.alternatives.flatten.each do |child|
|
|
128
|
+
collect_inline_edges(child, formals, names, edges, liveness)
|
|
129
|
+
end
|
|
130
|
+
when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
|
|
131
|
+
collect_inline_edges(item.item, formals, names, edges, liveness)
|
|
132
|
+
when Frontend::AST::SeparatedList
|
|
133
|
+
collect_inline_edges(item.item, formals, names, edges, liveness)
|
|
134
|
+
collect_inline_edges(item.separator, formals, names, edges, liveness)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
138
|
+
|
|
139
|
+
# @rbs (String name, Hash[String, Array[[String, Frontend::Location]]] graph,
|
|
140
|
+
# Hash[String, Symbol] states) -> void
|
|
141
|
+
def visit_inline_cycle(name, graph, states)
|
|
142
|
+
# @type self: Normalizer
|
|
143
|
+
states[name] = :visiting
|
|
144
|
+
path = [name] #: Array[String]
|
|
145
|
+
positions = { name => 0 } #: Hash[String, Integer]
|
|
146
|
+
worklist = [[name, 0]] #: Array[[String, Integer]]
|
|
147
|
+
until worklist.empty?
|
|
148
|
+
current, edge_index = worklist.fetch(-1)
|
|
149
|
+
edges = graph[current]
|
|
150
|
+
if edge_index >= edges.length
|
|
151
|
+
worklist.pop
|
|
152
|
+
positions.delete(path.pop || raise(Ibex::Error, "internal inline cycle path underflow"))
|
|
153
|
+
states[current] = :visited
|
|
154
|
+
next
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
worklist[-1] = [current, edge_index + 1]
|
|
158
|
+
target, location = edges.fetch(edge_index)
|
|
159
|
+
if states[target] == :visiting
|
|
160
|
+
cycle = path.drop(positions.fetch(target))
|
|
161
|
+
next unless cycle.any? { |entry| @inline_rule_names.include?(entry) }
|
|
162
|
+
|
|
163
|
+
fail_at(location, "inline expansion cycle: #{(cycle + [target]).join(' -> ')}")
|
|
164
|
+
end
|
|
165
|
+
next if states.key?(target)
|
|
166
|
+
|
|
167
|
+
states[target] = :visiting
|
|
168
|
+
positions[target] = path.length
|
|
169
|
+
path << target
|
|
170
|
+
worklist << [target, 0]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Lexer declaration normalization and static safety checks.
|
|
5
|
+
module NormalizeLexer
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs () -> IR::Lexer?
|
|
9
|
+
def normalize_lexer
|
|
10
|
+
# @type self: Normalizer
|
|
11
|
+
declaration = @lexer_declaration
|
|
12
|
+
return unless declaration
|
|
13
|
+
|
|
14
|
+
states = ["INITIAL"] #: Array[String]
|
|
15
|
+
rules = [] #: Array[IR::LexerRule]
|
|
16
|
+
warnings = [] #: Array[Hash[Symbol, untyped]]
|
|
17
|
+
declaration.definitions.each do |definition|
|
|
18
|
+
if definition.is_a?(Frontend::AST::LexerState)
|
|
19
|
+
normalize_lexer_state(definition, states, rules, warnings)
|
|
20
|
+
else
|
|
21
|
+
rules << normalize_lexer_rule(definition, "INITIAL", rules.length, warnings)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
fail_at(declaration.loc, "lexer declaration requires at least one rule") if rules.empty?
|
|
25
|
+
IR::Lexer.new(
|
|
26
|
+
states: states, rules: rules, warnings: warnings,
|
|
27
|
+
source_provenance: { file: declaration.loc.file, root: @resolution&.root_directory, byte_span: nil }
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @rbs (Frontend::AST::LexerState definition, Array[String] states, Array[IR::LexerRule] rules,
|
|
32
|
+
# Array[Hash[Symbol, untyped]] warnings) -> void
|
|
33
|
+
def normalize_lexer_state(definition, states, rules, warnings)
|
|
34
|
+
# @type self: Normalizer
|
|
35
|
+
fail_at(definition.loc, "lexer state INITIAL is reserved") if definition.name == "INITIAL"
|
|
36
|
+
fail_at(definition.loc, "duplicate lexer state #{definition.name}") if states.include?(definition.name)
|
|
37
|
+
states << definition.name
|
|
38
|
+
definition.definitions.each do |entry|
|
|
39
|
+
fail_at(entry.loc, "nested lexer states are not supported") if entry.is_a?(Frontend::AST::LexerState)
|
|
40
|
+
|
|
41
|
+
rules << normalize_lexer_rule(entry, definition.name, rules.length, warnings)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @rbs (Frontend::AST::LexerRule definition, String state, Integer id,
|
|
46
|
+
# Array[Hash[Symbol, untyped]] warnings) -> IR::LexerRule
|
|
47
|
+
def normalize_lexer_rule(definition, state, id, warnings)
|
|
48
|
+
# @type self: Normalizer
|
|
49
|
+
validate_lexer_token(definition)
|
|
50
|
+
validate_lexer_action(definition)
|
|
51
|
+
source, options = normalize_lexer_pattern(definition)
|
|
52
|
+
validate_lexer_regexp(definition, source, options)
|
|
53
|
+
if risky_lexer_pattern?(source)
|
|
54
|
+
warning = { type: :lexer_redos, loc: definition.loc.to_h } #: IR::grammar_warning
|
|
55
|
+
warning[:symbol] = definition.token if definition.token
|
|
56
|
+
@warnings << warning
|
|
57
|
+
warnings << { type: :redos, rule: id, loc: definition.loc.to_h }
|
|
58
|
+
end
|
|
59
|
+
IR::LexerRule.new(
|
|
60
|
+
id: id, state: state, kind: definition.kind, token: definition.token,
|
|
61
|
+
pattern: source, pattern_kind: definition.pattern_kind, options: options,
|
|
62
|
+
action: definition.action, location: definition.loc.to_h
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @rbs (Frontend::AST::LexerRule definition) -> void
|
|
67
|
+
def validate_lexer_token(definition)
|
|
68
|
+
# @type self: Normalizer
|
|
69
|
+
return unless definition.kind == :token
|
|
70
|
+
return if definition.token && @declared_tokens.key?(definition.token)
|
|
71
|
+
|
|
72
|
+
fail_at(definition.loc, "lexer rule references undeclared terminal #{definition.token}")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @rbs (Frontend::AST::LexerRule definition) -> void
|
|
76
|
+
def validate_lexer_action(definition)
|
|
77
|
+
# @type self: Normalizer
|
|
78
|
+
action = definition.action
|
|
79
|
+
return unless action&.lstrip&.start_with?("|")
|
|
80
|
+
|
|
81
|
+
match = action.match(/\A\s*\|([a-z_][a-zA-Z0-9_]*)\|/m)
|
|
82
|
+
fail_at(definition.loc, "lexer action accepts exactly one local identifier") unless match
|
|
83
|
+
name = match[1]
|
|
84
|
+
fail_at(definition.loc, "lexer action parameter #{name.inspect} is a Ruby keyword") if
|
|
85
|
+
Normalizer::RUBY_KEYWORDS.include?(name)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @rbs (Frontend::AST::LexerRule definition) -> [String, String]
|
|
89
|
+
def normalize_lexer_pattern(definition)
|
|
90
|
+
# @type self: Normalizer
|
|
91
|
+
raw = definition.pattern
|
|
92
|
+
if definition.pattern_kind == :literal
|
|
93
|
+
decoded = decode_lexer_literal(raw, definition.loc)
|
|
94
|
+
fail_at(definition.loc, "lexer pattern must not be empty") if decoded.empty?
|
|
95
|
+
return [Regexp.escape(decoded), ""]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
closing = raw.rindex("/")
|
|
99
|
+
fail_at(definition.loc, "invalid lexer regular expression") unless closing&.positive?
|
|
100
|
+
[raw[1...closing] || "", raw[(closing + 1)..] || ""]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @rbs (String raw, Frontend::Location location) -> String
|
|
104
|
+
def decode_lexer_literal(raw, location)
|
|
105
|
+
# @type self: Normalizer
|
|
106
|
+
return raw.undump if raw.start_with?('"')
|
|
107
|
+
|
|
108
|
+
(raw[1...-1] || "").gsub("\\'", "'").gsub("\\\\", "\\")
|
|
109
|
+
rescue RuntimeError => e
|
|
110
|
+
fail_at(location, "invalid lexer literal: #{e.message}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @rbs (Frontend::AST::LexerRule definition, String source, String options) -> void
|
|
114
|
+
def validate_lexer_regexp(definition, source, options)
|
|
115
|
+
# @type self: Normalizer
|
|
116
|
+
flags = 0
|
|
117
|
+
flags |= Regexp::IGNORECASE if options.include?("i")
|
|
118
|
+
flags |= Regexp::MULTILINE if options.include?("m")
|
|
119
|
+
flags |= Regexp::EXTENDED if options.include?("x")
|
|
120
|
+
regexp = Regexp.new("\\A(?:#{source})", flags)
|
|
121
|
+
fail_at(definition.loc, "lexer pattern must not match an empty string") if regexp.match?("")
|
|
122
|
+
rescue RegexpError => e
|
|
123
|
+
fail_at(definition.loc, "invalid lexer regular expression: #{e.message}")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @rbs (String source) -> bool
|
|
127
|
+
def risky_lexer_pattern?(source)
|
|
128
|
+
source.match?(/\([^)]*[+*][^)]*\)[+*{]/) || source.match?(/\.\*[+*{]/)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Named-reference collection and validation shared by ordinary and parameterized items.
|
|
5
|
+
module NormalizeNamedReferences
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs (Frontend::AST::Group group) -> void
|
|
9
|
+
def reject_group_named_references(group)
|
|
10
|
+
# @type self: Normalizer
|
|
11
|
+
reference = group.alternatives.flatten.filter_map { |item| named_reference_in(item) }.first
|
|
12
|
+
fail_at(reference.loc, "named references inside EBNF groups are not supported") if reference
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @rbs (Frontend::AST::item item) -> (Frontend::AST::SymbolReference | Frontend::AST::ParameterizedReference)?
|
|
16
|
+
def named_reference_in(item)
|
|
17
|
+
# @type self: Normalizer
|
|
18
|
+
if (item.is_a?(Frontend::AST::SymbolReference) ||
|
|
19
|
+
item.is_a?(Frontend::AST::ParameterizedReference)) && item.named_reference
|
|
20
|
+
return item
|
|
21
|
+
end
|
|
22
|
+
if item.is_a?(Frontend::AST::Group)
|
|
23
|
+
return item.alternatives.flatten.filter_map { |child| named_reference_in(child) }.first
|
|
24
|
+
end
|
|
25
|
+
if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
|
|
26
|
+
item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
|
|
27
|
+
return named_reference_in(item.item)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @rbs (Frontend::AST::item item, Array[IR::named_ref] refs, Integer index) -> void
|
|
34
|
+
def add_named_reference(item, refs, index)
|
|
35
|
+
# @type self: Normalizer
|
|
36
|
+
reference = unwrap_reference(item)
|
|
37
|
+
return unless reference
|
|
38
|
+
|
|
39
|
+
name = reference.named_reference
|
|
40
|
+
return unless name
|
|
41
|
+
|
|
42
|
+
fail_at(reference.loc, "reserved named reference #{name}") if Normalizer::RESERVED_NAMES.include?(name)
|
|
43
|
+
fail_at(reference.loc, "duplicate named reference #{name}") if refs.any? { |entry| entry[:name] == name }
|
|
44
|
+
refs << { name: name, index: index }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @rbs (Frontend::AST::item item) -> (Frontend::AST::SymbolReference | Frontend::AST::ParameterizedReference)?
|
|
48
|
+
def unwrap_reference(item)
|
|
49
|
+
# @type self: Normalizer
|
|
50
|
+
return item if item.is_a?(Frontend::AST::SymbolReference) ||
|
|
51
|
+
item.is_a?(Frontend::AST::ParameterizedReference)
|
|
52
|
+
if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
|
|
53
|
+
item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
|
|
54
|
+
return unwrap_reference(item.item)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Validation and normalization for generated AST node declarations.
|
|
5
|
+
module NormalizeNodes
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs (Frontend::AST::Alternative alternative, Array[String] rhs) -> IR::node_annotation?
|
|
9
|
+
def normalize_node_annotation(alternative, rhs)
|
|
10
|
+
# @type self: Normalizer
|
|
11
|
+
annotation = alternative.node_annotation
|
|
12
|
+
return unless annotation
|
|
13
|
+
|
|
14
|
+
if alternative.action || alternative.items.any?(Frontend::AST::InlineAction)
|
|
15
|
+
fail_at(annotation.loc, "@node cannot be combined with semantic actions")
|
|
16
|
+
end
|
|
17
|
+
unless annotation.name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
|
|
18
|
+
fail_at(annotation.loc, "@node name #{annotation.name.inspect} must be a Ruby constant identifier")
|
|
19
|
+
end
|
|
20
|
+
validate_node_fields(annotation, rhs)
|
|
21
|
+
previous = @node_shapes[annotation.name]
|
|
22
|
+
if previous && previous != annotation.fields
|
|
23
|
+
fail_at(annotation.loc, "@node #{annotation.name} was already declared with fields (#{previous.join(', ')})")
|
|
24
|
+
end
|
|
25
|
+
@node_shapes[annotation.name] = annotation.fields.dup.freeze
|
|
26
|
+
{ name: annotation.name, fields: annotation.fields, loc: annotation.loc.to_h }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @rbs (Frontend::AST::NodeAnnotation annotation, Array[String] rhs) -> void
|
|
30
|
+
def validate_node_fields(annotation, rhs)
|
|
31
|
+
# @type self: Normalizer
|
|
32
|
+
unless annotation.fields.length == rhs.length
|
|
33
|
+
fail_at(
|
|
34
|
+
annotation.loc,
|
|
35
|
+
"@node #{annotation.name} declares #{annotation.fields.length} fields for #{rhs.length} RHS values"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
duplicate = annotation.fields.tally.find { |_name, count| count > 1 }&.first
|
|
39
|
+
fail_at(annotation.loc, "@node field #{duplicate.inspect} is duplicated") if duplicate
|
|
40
|
+
invalid = annotation.fields.find do |field|
|
|
41
|
+
!field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) || Normalizer::RUBY_KEYWORDS.include?(field)
|
|
42
|
+
end
|
|
43
|
+
fail_at(annotation.loc, "@node field #{invalid.inspect} must be a Ruby local identifier") if invalid
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Resumable EBNF lowering used inside specialized parameter alternatives.
|
|
5
|
+
module NormalizeParameterEbnfLowering
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs (Hash[Symbol, untyped] frame,
|
|
9
|
+
# Frontend::AST::Optional | Frontend::AST::Star | Frontend::AST::Plus item) -> void
|
|
10
|
+
def lower_parameter_suffix(frame, item)
|
|
11
|
+
frame.fetch(:operations).push([:finish_suffix, item], [:item, item.item])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @rbs (Hash[Symbol, untyped] frame,
|
|
15
|
+
# Frontend::AST::Optional | Frontend::AST::Star | Frontend::AST::Plus item) -> void
|
|
16
|
+
def finish_parameter_suffix(frame, item)
|
|
17
|
+
# @type self: Normalizer
|
|
18
|
+
base = frame.fetch(:values).pop
|
|
19
|
+
helper = case item
|
|
20
|
+
when Frontend::AST::Optional then build_optional(item, base)
|
|
21
|
+
when Frontend::AST::Star then build_star(item, base)
|
|
22
|
+
when Frontend::AST::Plus then build_plus(item, base)
|
|
23
|
+
end
|
|
24
|
+
frame.fetch(:values) << helper
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::SeparatedList item) -> void
|
|
28
|
+
def lower_parameter_separated_list(frame, item)
|
|
29
|
+
frame.fetch(:operations).push(
|
|
30
|
+
[:finish_separated, item], [:item, item.separator], [:item, item.item]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::SeparatedList item) -> void
|
|
35
|
+
def finish_parameter_separated_list(frame, item)
|
|
36
|
+
# @type self: Normalizer
|
|
37
|
+
separator = frame.fetch(:values).pop
|
|
38
|
+
base = frame.fetch(:values).pop
|
|
39
|
+
frame.fetch(:values) << build_separated_list(item, base, separator)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::Group item) -> void
|
|
43
|
+
def lower_parameter_group(frame, item)
|
|
44
|
+
# @type self: Normalizer
|
|
45
|
+
reject_group_named_references(item)
|
|
46
|
+
helper = new_helper("group", item.loc)
|
|
47
|
+
operations = frame.fetch(:operations)
|
|
48
|
+
operations << [:value, helper]
|
|
49
|
+
item.alternatives.reverse_each do |alternative|
|
|
50
|
+
operations << [:group_alternative, helper, item, alternative]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs (Hash[Symbol, untyped] frame, String helper, Frontend::AST::Group item,
|
|
55
|
+
# Array[Frontend::AST::item] alternative) -> void
|
|
56
|
+
def start_parameter_group_alternative(frame, helper, item, alternative)
|
|
57
|
+
operations = frame.fetch(:operations)
|
|
58
|
+
operations << [:finish_group_alternative, helper, item, alternative.length]
|
|
59
|
+
alternative.reverse_each { |child| operations << [:item, child] }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @rbs (Hash[Symbol, untyped] frame, String helper, Frontend::AST::Group item, Integer length) -> void
|
|
63
|
+
def finish_parameter_group_alternative(frame, helper, item, length)
|
|
64
|
+
# @type self: Normalizer
|
|
65
|
+
rhs = frame.fetch(:values).pop(length)
|
|
66
|
+
add_group_production(helper, rhs, item)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
# Resumable, source-ordered lowering for specialized parameter alternatives.
|
|
5
|
+
module NormalizeParameterLowering
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# @rbs (Hash[Symbol, untyped] frame) -> void
|
|
9
|
+
def advance_parameter_alternative(frame)
|
|
10
|
+
# @type self: Normalizer
|
|
11
|
+
with_parameter_frame_context(frame) do
|
|
12
|
+
operations = frame.fetch(:operations)
|
|
13
|
+
if operations.any?
|
|
14
|
+
process_parameter_operation(frame, operations.pop)
|
|
15
|
+
elsif parameter_items_remaining?(frame)
|
|
16
|
+
start_parameter_item(frame)
|
|
17
|
+
else
|
|
18
|
+
finish_parameter_alternative(frame)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs (Hash[Symbol, untyped] frame) { () -> void } -> void
|
|
24
|
+
def with_parameter_frame_context(frame)
|
|
25
|
+
# @type self: Normalizer
|
|
26
|
+
template, = frame.fetch(:current)
|
|
27
|
+
previous_chain = @current_include_chain
|
|
28
|
+
previous_expansion = @current_parameter_expansion
|
|
29
|
+
reference = frame.fetch(:reference)
|
|
30
|
+
@current_include_chain = @resolution&.include_chain_for(template) || []
|
|
31
|
+
@current_parameter_expansion = {
|
|
32
|
+
rule: reference.name, arguments: frame.fetch(:rendered_arguments)
|
|
33
|
+
}
|
|
34
|
+
yield
|
|
35
|
+
ensure
|
|
36
|
+
@current_include_chain = previous_chain
|
|
37
|
+
@current_parameter_expansion = previous_expansion
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @rbs (Hash[Symbol, untyped] frame) -> bool
|
|
41
|
+
def parameter_items_remaining?(frame)
|
|
42
|
+
_template, _rule, alternative = frame.fetch(:current)
|
|
43
|
+
frame.fetch(:item_index) < alternative.items.length
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @rbs (Hash[Symbol, untyped] frame) -> void
|
|
47
|
+
def start_parameter_item(frame)
|
|
48
|
+
# @type self: Normalizer
|
|
49
|
+
_template, _rule, alternative = frame.fetch(:current)
|
|
50
|
+
index = frame.fetch(:item_index)
|
|
51
|
+
item = alternative.items.fetch(index)
|
|
52
|
+
frame[:item_index] = index + 1
|
|
53
|
+
if item.is_a?(Frontend::AST::InlineAction)
|
|
54
|
+
frame.fetch(:rhs) << expand_inline_action(
|
|
55
|
+
item, frame.fetch(:rhs).length, frame.fetch(:named_refs)
|
|
56
|
+
)
|
|
57
|
+
else
|
|
58
|
+
frame.fetch(:operations).push([:finish_item, item], [:item, item])
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @rbs (Hash[Symbol, untyped] frame, Array[untyped] operation) -> void
|
|
63
|
+
def process_parameter_operation(frame, operation)
|
|
64
|
+
# @type self: Normalizer
|
|
65
|
+
kind, *arguments = operation
|
|
66
|
+
case kind
|
|
67
|
+
when :item then lower_parameter_item(frame, arguments.fetch(0))
|
|
68
|
+
when :finish_item then finish_parameter_item(frame, arguments.fetch(0))
|
|
69
|
+
when :value then frame.fetch(:values) << arguments.fetch(0)
|
|
70
|
+
when :finish_suffix then finish_parameter_suffix(frame, arguments.fetch(0))
|
|
71
|
+
when :finish_separated then finish_parameter_separated_list(frame, arguments.fetch(0))
|
|
72
|
+
when :group_alternative
|
|
73
|
+
start_parameter_group_alternative(frame, arguments.fetch(0), arguments.fetch(1), arguments.fetch(2))
|
|
74
|
+
when :finish_group_alternative
|
|
75
|
+
finish_parameter_group_alternative(frame, arguments.fetch(0), arguments.fetch(1), arguments.fetch(2))
|
|
76
|
+
else raise Ibex::Error, "internal parameter lowering operation #{kind.inspect}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::item item) -> void
|
|
81
|
+
def lower_parameter_item(frame, item)
|
|
82
|
+
# @type self: Normalizer
|
|
83
|
+
case item
|
|
84
|
+
when Frontend::AST::SymbolReference
|
|
85
|
+
frame.fetch(:values) << symbol_for_reference(item).name
|
|
86
|
+
when Frontend::AST::ParameterizedReference
|
|
87
|
+
lower_parameter_call(frame, item)
|
|
88
|
+
when Frontend::AST::Group
|
|
89
|
+
lower_parameter_group(frame, item)
|
|
90
|
+
when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
|
|
91
|
+
lower_parameter_suffix(frame, item)
|
|
92
|
+
when Frontend::AST::SeparatedList
|
|
93
|
+
lower_parameter_separated_list(frame, item)
|
|
94
|
+
else
|
|
95
|
+
fail_at(item.loc, "unsupported nested EBNF expression")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::ParameterizedReference item) -> void
|
|
100
|
+
def lower_parameter_call(frame, item)
|
|
101
|
+
# @type self: Normalizer
|
|
102
|
+
helper, = schedule_parameter_specialization(item)
|
|
103
|
+
frame.fetch(:values) << helper
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @rbs (Hash[Symbol, untyped] frame, Frontend::AST::item item) -> void
|
|
107
|
+
def finish_parameter_item(frame, item)
|
|
108
|
+
# @type self: Normalizer
|
|
109
|
+
rhs = frame.fetch(:rhs)
|
|
110
|
+
rhs << frame.fetch(:values).pop
|
|
111
|
+
add_named_reference(item, frame.fetch(:named_refs), rhs.length - 1)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @rbs (Hash[Symbol, untyped] frame) -> void
|
|
115
|
+
def finish_parameter_alternative(frame)
|
|
116
|
+
# @type self: Normalizer
|
|
117
|
+
_template, rule, alternative = frame.fetch(:current)
|
|
118
|
+
action = normalize_action(alternative.action, frame.fetch(:named_refs))
|
|
119
|
+
add_production(
|
|
120
|
+
rule.lhs, frame.fetch(:rhs), action, alternative.precedence,
|
|
121
|
+
{ kind: :user, loc: alternative.loc.to_h }, rule.documentation
|
|
122
|
+
)
|
|
123
|
+
clear_parameter_alternative(frame)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|