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,604 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module IR
|
|
5
|
+
module Validator
|
|
6
|
+
# Structural and referential validation for a versioned Grammar IR JSON object.
|
|
7
|
+
# rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.
|
|
8
|
+
class GrammarDocument < Base
|
|
9
|
+
ROOT_REQUIRED = %w[
|
|
10
|
+
ibex_ir schema_version class_name superclass start expect options symbols productions user_code
|
|
11
|
+
conversions warnings
|
|
12
|
+
].freeze #: Array[String]
|
|
13
|
+
ROOT_OPTIONAL = %w[user_code_chunks expect_rr].freeze #: Array[String]
|
|
14
|
+
V2_ROOT_REQUIRED = %w[source_provenance migration].freeze #: Array[String]
|
|
15
|
+
V2_ROOT_OPTIONAL = %w[params printers tests recovery lexer mode starts].freeze #: Array[String]
|
|
16
|
+
SYMBOL_REQUIRED = %w[id name kind reserved prec loc].freeze #: Array[String]
|
|
17
|
+
SYMBOL_OPTIONAL = %w[display_name semantic_type].freeze #: Array[String]
|
|
18
|
+
V2_SYMBOL_REQUIRED = %w[doc].freeze #: Array[String]
|
|
19
|
+
PRODUCTION_REQUIRED = %w[id lhs rhs action prec_override origin].freeze #: Array[String]
|
|
20
|
+
V2_PRODUCTION_REQUIRED = %w[doc expansion].freeze #: Array[String]
|
|
21
|
+
V2_ACTION_REQUIRED = %w[composition].freeze #: Array[String]
|
|
22
|
+
ORIGIN_KINDS = %w[
|
|
23
|
+
optional_expansion star_expansion plus_expansion separated_list_expansion group_expansion
|
|
24
|
+
].freeze #: Array[String]
|
|
25
|
+
RUBY_KEYWORDS = %w[
|
|
26
|
+
__ENCODING__ __FILE__ __LINE__ alias and begin break case class def defined do else elsif end ensure false for
|
|
27
|
+
if in module next nil not or redo rescue retry return self super then true undef unless until when while yield
|
|
28
|
+
].freeze #: Array[String]
|
|
29
|
+
|
|
30
|
+
attr_reader :symbols_by_id #: Hash[Integer, Hash[String, untyped]]
|
|
31
|
+
attr_reader :symbols_by_name #: Hash[String, Hash[String, untyped]]
|
|
32
|
+
attr_reader :productions_by_id #: Hash[Integer, Hash[String, untyped]]
|
|
33
|
+
|
|
34
|
+
# @rbs @data: Hash[String, untyped]
|
|
35
|
+
# @rbs @path: String
|
|
36
|
+
# @rbs @version: Integer
|
|
37
|
+
|
|
38
|
+
# @rbs (Hash[String, untyped] data, ?path: String, ?version: Integer) -> void
|
|
39
|
+
def initialize(data, path: "$", version: data.fetch("schema_version"))
|
|
40
|
+
super()
|
|
41
|
+
@data = data
|
|
42
|
+
@path = path
|
|
43
|
+
@version = version
|
|
44
|
+
@symbols_by_id = {}
|
|
45
|
+
@symbols_by_name = {}
|
|
46
|
+
@productions_by_id = {}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @rbs () -> self
|
|
50
|
+
def validate
|
|
51
|
+
required = ROOT_REQUIRED + (@version >= 2 ? V2_ROOT_REQUIRED : [])
|
|
52
|
+
optional = ROOT_OPTIONAL + (@version >= 2 ? V2_ROOT_OPTIONAL : [])
|
|
53
|
+
record(@data, @path, required, optional)
|
|
54
|
+
validate_envelope
|
|
55
|
+
validate_header
|
|
56
|
+
validate_options
|
|
57
|
+
validate_parser_parameters if @data.key?("params")
|
|
58
|
+
validate_symbols
|
|
59
|
+
validate_value_printers if @data.key?("printers")
|
|
60
|
+
validate_grammar_tests if @data.key?("tests")
|
|
61
|
+
validate_reserved_symbols
|
|
62
|
+
validate_start
|
|
63
|
+
validate_lexer if @data.key?("lexer")
|
|
64
|
+
validate_recovery if @data.key?("recovery")
|
|
65
|
+
validate_productions
|
|
66
|
+
validate_string_map(@data["user_code"], "#{@path}.user_code")
|
|
67
|
+
validate_string_map(@data["conversions"], "#{@path}.conversions")
|
|
68
|
+
validate_warnings
|
|
69
|
+
validate_user_code_chunks if @data.key?("user_code_chunks")
|
|
70
|
+
if @version >= 2
|
|
71
|
+
validate_source_provenance(@data["source_provenance"], "#{@path}.source_provenance")
|
|
72
|
+
validate_migration
|
|
73
|
+
end
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# @rbs () -> void
|
|
80
|
+
def validate_envelope
|
|
81
|
+
literal(@data["ibex_ir"], "#{@path}.ibex_ir", "grammar")
|
|
82
|
+
literal(@data["schema_version"], "#{@path}.schema_version", @version)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @rbs () -> void
|
|
86
|
+
def validate_header
|
|
87
|
+
nonempty_string(@data["class_name"], "#{@path}.class_name")
|
|
88
|
+
nullable_string(@data["superclass"], "#{@path}.superclass")
|
|
89
|
+
nonempty_string(@data["start"], "#{@path}.start")
|
|
90
|
+
enum(@data["mode"], "#{@path}.mode", %w[default extended]) if @data.key?("mode")
|
|
91
|
+
nonnegative_integer(@data["expect"], "#{@path}.expect")
|
|
92
|
+
nonnegative_integer(@data["expect_rr"], "#{@path}.expect_rr") if @data.key?("expect_rr")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @rbs () -> void
|
|
96
|
+
def validate_options
|
|
97
|
+
path = "#{@path}.options"
|
|
98
|
+
options = record(@data["options"], path, %w[result_var omit_action_call], %w[cst])
|
|
99
|
+
boolean(options["result_var"], "#{path}.result_var")
|
|
100
|
+
boolean(options["omit_action_call"], "#{path}.omit_action_call")
|
|
101
|
+
return unless options.key?("cst")
|
|
102
|
+
|
|
103
|
+
invalid("#{path}.cst", "requires schema_version 2") unless @version >= 2
|
|
104
|
+
literal(options["cst"], "#{path}.cst", true)
|
|
105
|
+
invalid("#{@path}.mode", "must be extended when CST construction is enabled") unless
|
|
106
|
+
@data["mode"] == "extended"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs () -> void
|
|
110
|
+
def validate_parser_parameters
|
|
111
|
+
names = {} #: Hash[String, bool]
|
|
112
|
+
array(@data["params"], "#{@path}.params").each_with_index do |value, index|
|
|
113
|
+
path = "#{@path}.params[#{index}]"
|
|
114
|
+
parameter = record(value, path, %w[name semantic_type])
|
|
115
|
+
name = nonempty_string(parameter["name"], "#{path}.name")
|
|
116
|
+
invalid("#{path}.name", "must be a Ruby local identifier") unless name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
|
|
117
|
+
invalid("#{path}.name", "must not be a Ruby keyword") if RUBY_KEYWORDS.include?(name)
|
|
118
|
+
invalid("#{path}.name", "duplicates parameter #{name.inspect}") if names.key?(name)
|
|
119
|
+
names[name] = true
|
|
120
|
+
metadata(parameter["semantic_type"], "#{path}.semantic_type")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @rbs () -> void
|
|
125
|
+
def validate_value_printers
|
|
126
|
+
names = {} #: Hash[String, bool]
|
|
127
|
+
array(@data["printers"], "#{@path}.printers").each_with_index do |value, index|
|
|
128
|
+
path = "#{@path}.printers[#{index}]"
|
|
129
|
+
printer = record(value, path, %w[symbol code loc])
|
|
130
|
+
name = nonempty_string(printer["symbol"], "#{path}.symbol")
|
|
131
|
+
invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
|
|
132
|
+
invalid("#{path}.symbol", "duplicates printer for #{name.inspect}") if names.key?(name)
|
|
133
|
+
names[name] = true
|
|
134
|
+
string(printer["code"], "#{path}.code")
|
|
135
|
+
location(printer["loc"], "#{path}.loc", nullable: false)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @rbs () -> void
|
|
140
|
+
def validate_grammar_tests
|
|
141
|
+
invalid("#{@path}.mode", "must be extended for grammar tests") unless @data["mode"] == "extended"
|
|
142
|
+
seen = {} #: Hash[[String, String], bool]
|
|
143
|
+
array(@data["tests"], "#{@path}.tests").each_with_index do |value, index|
|
|
144
|
+
path = "#{@path}.tests[#{index}]"
|
|
145
|
+
test = record(value, path, %w[expectation source loc])
|
|
146
|
+
expectation = enum(test["expectation"], "#{path}.expectation", %w[accept reject])
|
|
147
|
+
source = string(test["source"], "#{path}.source")
|
|
148
|
+
key = [expectation, source] #: [String, String]
|
|
149
|
+
invalid(path, "duplicates grammar test") if seen[key]
|
|
150
|
+
seen[key] = true
|
|
151
|
+
location(test["loc"], "#{path}.loc", nullable: false)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @rbs () -> void
|
|
156
|
+
def validate_symbols
|
|
157
|
+
array(@data["symbols"], "#{@path}.symbols").each_with_index do |value, index|
|
|
158
|
+
validate_symbol(value, index)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @rbs (untyped value, Integer index) -> void
|
|
163
|
+
def validate_symbol(value, index)
|
|
164
|
+
path = "#{@path}.symbols[#{index}]"
|
|
165
|
+
required = SYMBOL_REQUIRED + (@version >= 2 ? V2_SYMBOL_REQUIRED : [])
|
|
166
|
+
symbol = record(value, path, required, SYMBOL_OPTIONAL)
|
|
167
|
+
id = nonnegative_integer(symbol["id"], "#{path}.id")
|
|
168
|
+
invalid("#{path}.id", "must equal its array index #{index}") unless id == index
|
|
169
|
+
name = nonempty_string(symbol["name"], "#{path}.name")
|
|
170
|
+
invalid("#{path}.name", "duplicates symbol #{name.inspect}") if @symbols_by_name.key?(name)
|
|
171
|
+
@symbols_by_id[id] = symbol
|
|
172
|
+
@symbols_by_name[name] = symbol
|
|
173
|
+
enum(symbol["kind"], "#{path}.kind", %w[terminal nonterminal])
|
|
174
|
+
boolean(symbol["reserved"], "#{path}.reserved")
|
|
175
|
+
validate_precedence(symbol["prec"], "#{path}.prec")
|
|
176
|
+
location(symbol["loc"], "#{path}.loc")
|
|
177
|
+
SYMBOL_OPTIONAL.each { |key| metadata(symbol[key], "#{path}.#{key}") if symbol.key?(key) }
|
|
178
|
+
nullable_string(symbol["doc"], "#{path}.doc") if @version >= 2
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# @rbs (untyped value, String path) -> void
|
|
182
|
+
def validate_precedence(value, path)
|
|
183
|
+
return if value.nil?
|
|
184
|
+
|
|
185
|
+
precedence = record(value, path, %w[associativity level])
|
|
186
|
+
enum(precedence["associativity"], "#{path}.associativity", %w[left right nonassoc precedence])
|
|
187
|
+
positive_integer(precedence["level"], "#{path}.level")
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @rbs () -> void
|
|
191
|
+
def validate_reserved_symbols
|
|
192
|
+
validate_reserved_symbol(0, "$eof")
|
|
193
|
+
validate_reserved_symbol(1, "error")
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @rbs (Integer id, String name) -> void
|
|
197
|
+
def validate_reserved_symbol(id, name)
|
|
198
|
+
symbol = @symbols_by_id[id]
|
|
199
|
+
invalid("#{@path}.symbols", "must contain reserved symbol #{name.inspect} at id #{id}") unless symbol
|
|
200
|
+
invalid("#{@path}.symbols[#{id}]", "must be reserved terminal #{name.inspect}") unless
|
|
201
|
+
symbol["name"] == name && symbol["kind"] == "terminal" && symbol["reserved"]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @rbs () -> void
|
|
205
|
+
def validate_start
|
|
206
|
+
start = @data["start"]
|
|
207
|
+
symbol = @symbols_by_name[start]
|
|
208
|
+
invalid("#{@path}.start", "references missing symbol #{start.inspect}") unless symbol
|
|
209
|
+
invalid("#{@path}.start", "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
|
|
210
|
+
return unless @data.key?("starts")
|
|
211
|
+
|
|
212
|
+
validate_multiple_starts(start)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @rbs (String start) -> void
|
|
216
|
+
def validate_multiple_starts(start)
|
|
217
|
+
starts = array(@data["starts"], "#{@path}.starts")
|
|
218
|
+
invalid("#{@path}.starts", "must not be empty") if starts.empty?
|
|
219
|
+
invalid("#{@path}.starts[0]", "must equal start #{start.inspect}") unless starts.first == start
|
|
220
|
+
invalid("#{@path}.mode", "must be extended for multiple start symbols") unless @data["mode"] == "extended"
|
|
221
|
+
seen = {} #: Hash[String, bool]
|
|
222
|
+
starts.each_with_index do |name, index|
|
|
223
|
+
name = nonempty_string(name, "#{@path}.starts[#{index}]")
|
|
224
|
+
invalid("#{@path}.starts[#{index}]", "duplicates start symbol #{name.inspect}") if seen[name]
|
|
225
|
+
seen[name] = true
|
|
226
|
+
definition = @symbols_by_name[name]
|
|
227
|
+
invalid("#{@path}.starts[#{index}]", "references missing symbol #{name.inspect}") unless definition
|
|
228
|
+
invalid("#{@path}.starts[#{index}]", "must reference a nonterminal") unless
|
|
229
|
+
definition["kind"] == "nonterminal"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# @rbs () -> void
|
|
234
|
+
def validate_recovery
|
|
235
|
+
path = "#{@path}.recovery"
|
|
236
|
+
recovery = record(@data["recovery"], path, %w[sync_tokens on_error_reduce])
|
|
237
|
+
invalid("#{@path}.mode", "must be extended for recovery declarations") unless @data["mode"] == "extended"
|
|
238
|
+
validate_recovery_symbols(recovery["sync_tokens"], "#{path}.sync_tokens", kind: "terminal")
|
|
239
|
+
groups = array(recovery["on_error_reduce"], "#{path}.on_error_reduce")
|
|
240
|
+
seen = {} #: Hash[String, bool]
|
|
241
|
+
groups.each_with_index do |group, index|
|
|
242
|
+
group_path = "#{path}.on_error_reduce[#{index}]"
|
|
243
|
+
names = array(group, group_path)
|
|
244
|
+
invalid(group_path, "must not be empty") if names.empty?
|
|
245
|
+
names.each_with_index do |name, name_index|
|
|
246
|
+
validate_recovery_symbol(
|
|
247
|
+
name, "#{group_path}[#{name_index}]", kind: "nonterminal", seen: seen
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
return unless array(recovery["sync_tokens"], "#{path}.sync_tokens").empty? && groups.empty?
|
|
252
|
+
|
|
253
|
+
invalid(path, "must declare at least one recovery policy")
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @rbs (untyped values, String path, kind: String) -> void
|
|
257
|
+
def validate_recovery_symbols(values, path, kind:)
|
|
258
|
+
seen = {} #: Hash[String, bool]
|
|
259
|
+
array(values, path).each_with_index do |name, index|
|
|
260
|
+
validate_recovery_symbol(name, "#{path}[#{index}]", kind: kind, seen: seen)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# @rbs (untyped value, String path, kind: String, seen: Hash[String, bool]) -> void
|
|
265
|
+
def validate_recovery_symbol(value, path, kind:, seen:)
|
|
266
|
+
name = nonempty_string(value, path)
|
|
267
|
+
invalid(path, "duplicates symbol #{name.inspect}") if seen[name]
|
|
268
|
+
seen[name] = true
|
|
269
|
+
symbol = @symbols_by_name[name]
|
|
270
|
+
invalid(path, "references missing symbol #{name.inspect}") unless symbol
|
|
271
|
+
invalid(path, "must reference a #{kind}") unless symbol["kind"] == kind
|
|
272
|
+
invalid(path, "must not reference the synthetic error token") if name == "error"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# @rbs () -> void
|
|
276
|
+
def validate_productions
|
|
277
|
+
array(@data["productions"], "#{@path}.productions").each_with_index do |value, index|
|
|
278
|
+
validate_production(value, index)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# @rbs (untyped value, Integer index) -> void
|
|
283
|
+
def validate_production(value, index)
|
|
284
|
+
path = "#{@path}.productions[#{index}]"
|
|
285
|
+
required = PRODUCTION_REQUIRED + (@version >= 2 ? V2_PRODUCTION_REQUIRED : [])
|
|
286
|
+
optional = @version >= 2 ? %w[node] : Array.new(0) #: Array[String]
|
|
287
|
+
production = record(value, path, required, optional)
|
|
288
|
+
id = nonnegative_integer(production["id"], "#{path}.id")
|
|
289
|
+
invalid("#{path}.id", "must equal its array index #{index}") unless id == index
|
|
290
|
+
@productions_by_id[id] = production
|
|
291
|
+
validate_lhs(production["lhs"], "#{path}.lhs")
|
|
292
|
+
validate_rhs(production["rhs"], "#{path}.rhs")
|
|
293
|
+
validate_action(production["action"], "#{path}.action", rhs_length: production["rhs"].length)
|
|
294
|
+
validate_precedence_override(production["prec_override"], "#{path}.prec_override")
|
|
295
|
+
validate_origin(production["origin"], "#{path}.origin")
|
|
296
|
+
return unless @version >= 2
|
|
297
|
+
|
|
298
|
+
nullable_string(production["doc"], "#{path}.doc")
|
|
299
|
+
validate_expansion(production["expansion"], "#{path}.expansion")
|
|
300
|
+
validate_node_annotation(production["node"], "#{path}.node", rhs_length: production["rhs"].length)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# @rbs (untyped value, String path, rhs_length: Integer) -> void
|
|
304
|
+
def validate_node_annotation(value, path, rhs_length:)
|
|
305
|
+
return if value.nil?
|
|
306
|
+
|
|
307
|
+
node = record(value, path, %w[name fields loc])
|
|
308
|
+
name = nonempty_string(node["name"], "#{path}.name")
|
|
309
|
+
invalid("#{path}.name", "must be a Ruby constant identifier") unless name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
|
|
310
|
+
fields = array(node["fields"], "#{path}.fields")
|
|
311
|
+
invalid("#{path}.fields", "must contain #{rhs_length} entries") unless fields.length == rhs_length
|
|
312
|
+
seen = {} #: Hash[String, bool]
|
|
313
|
+
fields.each_with_index do |value, index|
|
|
314
|
+
field = nonempty_string(value, "#{path}.fields[#{index}]")
|
|
315
|
+
invalid("#{path}.fields[#{index}]", "must be a Ruby local identifier") unless
|
|
316
|
+
field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) && !RUBY_KEYWORDS.include?(field)
|
|
317
|
+
invalid("#{path}.fields[#{index}]", "duplicates field #{field.inspect}") if seen[field]
|
|
318
|
+
seen[field] = true
|
|
319
|
+
end
|
|
320
|
+
location(node["loc"], "#{path}.loc", nullable: false)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# @rbs (untyped value, String path) -> void
|
|
324
|
+
def validate_lhs(value, path)
|
|
325
|
+
id = nonnegative_integer(value, path)
|
|
326
|
+
symbol = @symbols_by_id[id]
|
|
327
|
+
invalid(path, "references missing symbol id #{id}") unless symbol
|
|
328
|
+
invalid(path, "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# @rbs (untyped value, String path) -> void
|
|
332
|
+
def validate_rhs(value, path)
|
|
333
|
+
array(value, path).each_with_index do |id, index|
|
|
334
|
+
id = nonnegative_integer(id, "#{path}[#{index}]")
|
|
335
|
+
invalid("#{path}[#{index}]", "references missing symbol id #{id}") unless @symbols_by_id.key?(id)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# @rbs (untyped value, String path, rhs_length: Integer) -> void
|
|
340
|
+
def validate_action(value, path, rhs_length:)
|
|
341
|
+
return if value.nil?
|
|
342
|
+
|
|
343
|
+
required = %w[code loc named_refs context_length] + (@version >= 2 ? V2_ACTION_REQUIRED : [])
|
|
344
|
+
action = record(value, path, required)
|
|
345
|
+
string(action["code"], "#{path}.code")
|
|
346
|
+
location(action["loc"], "#{path}.loc", nullable: false)
|
|
347
|
+
context_length = nonnegative_integer(action["context_length"], "#{path}.context_length")
|
|
348
|
+
validate_named_refs(action["named_refs"], "#{path}.named_refs", limit: [rhs_length, context_length].max)
|
|
349
|
+
return unless @version >= 2
|
|
350
|
+
|
|
351
|
+
validate_action_composition(
|
|
352
|
+
action["composition"], "#{path}.composition", rhs_length: rhs_length
|
|
353
|
+
)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# @rbs (untyped value, String path, limit: Integer) -> void
|
|
357
|
+
def validate_named_refs(value, path, limit:)
|
|
358
|
+
names = {} #: Hash[String, bool]
|
|
359
|
+
array(value, path).each_with_index do |entry, index|
|
|
360
|
+
entry_path = "#{path}[#{index}]"
|
|
361
|
+
reference = record(entry, entry_path, %w[name index])
|
|
362
|
+
name = nonempty_string(reference["name"], "#{entry_path}.name")
|
|
363
|
+
invalid("#{entry_path}.name", "duplicates named reference #{name.inspect}") if names.key?(name)
|
|
364
|
+
names[name] = true
|
|
365
|
+
reference_index = nonnegative_integer(reference["index"], "#{entry_path}.index")
|
|
366
|
+
if reference_index >= limit
|
|
367
|
+
invalid("#{entry_path}.index", "must be less than the action context length #{limit}")
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# @rbs (untyped value, String path) -> void
|
|
373
|
+
def validate_precedence_override(value, path)
|
|
374
|
+
return if value.nil?
|
|
375
|
+
|
|
376
|
+
id = nonnegative_integer(value, path)
|
|
377
|
+
symbol = @symbols_by_id[id]
|
|
378
|
+
invalid(path, "references missing symbol id #{id}") unless symbol
|
|
379
|
+
invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# @rbs (untyped value, String path) -> void
|
|
383
|
+
def validate_origin(value, path)
|
|
384
|
+
origin = object(value, path)
|
|
385
|
+
kind = string(field(origin, "kind", path), "#{path}.kind")
|
|
386
|
+
optional = ORIGIN_KINDS.include?(kind) ? %w[expression] : [] # @type var optional: Array[String]
|
|
387
|
+
record(origin, path, %w[kind loc], optional)
|
|
388
|
+
enum(kind, "#{path}.kind", %w[user inline_action] + ORIGIN_KINDS)
|
|
389
|
+
string(origin["expression"], "#{path}.expression") if origin.key?("expression")
|
|
390
|
+
location(origin["loc"], "#{path}.loc", nullable: false)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# @rbs (untyped value, String path) -> void
|
|
394
|
+
def validate_string_map(value, path)
|
|
395
|
+
object(value, path).each do |key, item|
|
|
396
|
+
string(key, path)
|
|
397
|
+
string(item, child_path(path, key))
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# @rbs () -> void
|
|
402
|
+
def validate_warnings
|
|
403
|
+
array(@data["warnings"], "#{@path}.warnings").each_with_index do |value, index|
|
|
404
|
+
validate_warning(value, "#{@path}.warnings[#{index}]")
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# @rbs (untyped value, String path) -> void
|
|
409
|
+
def validate_warning(value, path)
|
|
410
|
+
warning = record(value, path, %w[type loc], %w[symbol production original])
|
|
411
|
+
nonempty_string(warning["type"], "#{path}.type")
|
|
412
|
+
location(warning["loc"], "#{path}.loc")
|
|
413
|
+
validate_warning_symbol(warning, path) if warning.key?("symbol")
|
|
414
|
+
validate_warning_production(warning, "production", path) if warning.key?("production")
|
|
415
|
+
validate_warning_production(warning, "original", path) if warning.key?("original")
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# @rbs (Hash[String, untyped] warning, String path) -> void
|
|
419
|
+
def validate_warning_symbol(warning, path)
|
|
420
|
+
name = nonempty_string(warning["symbol"], "#{path}.symbol")
|
|
421
|
+
invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# @rbs (Hash[String, untyped] warning, String field_name, String path) -> void
|
|
425
|
+
def validate_warning_production(warning, field_name, path)
|
|
426
|
+
id = nonnegative_integer(warning[field_name], "#{path}.#{field_name}")
|
|
427
|
+
invalid("#{path}.#{field_name}", "references missing production id #{id}") unless @productions_by_id.key?(id)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# @rbs () -> void
|
|
431
|
+
def validate_lexer
|
|
432
|
+
value = @data.fetch("lexer")
|
|
433
|
+
LexerDocument.new(value, path: "#{@path}.lexer").validate
|
|
434
|
+
value.fetch("rules").each_with_index do |rule, index|
|
|
435
|
+
next unless rule["kind"] == "token"
|
|
436
|
+
|
|
437
|
+
name = rule["token"]
|
|
438
|
+
symbol = @symbols_by_name[name]
|
|
439
|
+
invalid("#{@path}.lexer.rules[#{index}].token", "references missing terminal #{name.inspect}") unless
|
|
440
|
+
symbol&.fetch("kind") == "terminal"
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# @rbs () -> void
|
|
445
|
+
def validate_user_code_chunks
|
|
446
|
+
path = "#{@path}.user_code_chunks"
|
|
447
|
+
object(@data["user_code_chunks"], path).each do |key, chunks|
|
|
448
|
+
string(key, path)
|
|
449
|
+
array(chunks, child_path(path, key)).each_with_index do |chunk, index|
|
|
450
|
+
chunk_path = "#{child_path(path, key)}[#{index}]"
|
|
451
|
+
chunk = record(chunk, chunk_path, %w[code loc])
|
|
452
|
+
string(chunk["code"], "#{chunk_path}.code")
|
|
453
|
+
location(chunk["loc"], "#{chunk_path}.loc", nullable: false)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# @rbs (untyped value, String path, ?nullable: bool) -> void
|
|
459
|
+
def validate_source_provenance(value, path, nullable: true)
|
|
460
|
+
return if nullable && value.nil?
|
|
461
|
+
|
|
462
|
+
source = record(value, path, %w[file root byte_span])
|
|
463
|
+
nullable_string(source["file"], "#{path}.file")
|
|
464
|
+
nullable_string(source["root"], "#{path}.root")
|
|
465
|
+
validate_byte_span(source["byte_span"], "#{path}.byte_span")
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# @rbs (untyped value, String path) -> void
|
|
469
|
+
def validate_byte_span(value, path)
|
|
470
|
+
return if value.nil?
|
|
471
|
+
|
|
472
|
+
span = record(value, path, %w[start end])
|
|
473
|
+
start_byte = nonnegative_integer(span["start"], "#{path}.start")
|
|
474
|
+
end_byte = nonnegative_integer(span["end"], "#{path}.end")
|
|
475
|
+
invalid("#{path}.end", "must be greater than or equal to start") if end_byte < start_byte
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# @rbs () -> void
|
|
479
|
+
def validate_migration
|
|
480
|
+
path = "#{@path}.migration"
|
|
481
|
+
value = @data["migration"]
|
|
482
|
+
return if value.nil?
|
|
483
|
+
|
|
484
|
+
migration = record(value, path, %w[from_schema_version unavailable])
|
|
485
|
+
literal(migration["from_schema_version"], "#{path}.from_schema_version", 1)
|
|
486
|
+
values = array(migration["unavailable"], "#{path}.unavailable")
|
|
487
|
+
invalid("#{path}.unavailable", "must not be empty") if values.empty?
|
|
488
|
+
values.each_with_index do |name, index|
|
|
489
|
+
enum(name, "#{path}.unavailable[#{index}]", Migration::UNAVAILABLE_V1_METADATA)
|
|
490
|
+
end
|
|
491
|
+
invalid("#{path}.unavailable", "must contain unique names") unless values.uniq.length == values.length
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# @rbs (untyped value, String path) -> void
|
|
495
|
+
def validate_expansion(value, path)
|
|
496
|
+
return if value.nil?
|
|
497
|
+
|
|
498
|
+
expansion = record(value, path, %w[parameter inline include_chain])
|
|
499
|
+
validate_parameter_expansion(expansion["parameter"], "#{path}.parameter")
|
|
500
|
+
validate_inline_expansion(expansion["inline"], "#{path}.inline")
|
|
501
|
+
array(expansion["include_chain"], "#{path}.include_chain").each_with_index do |source, index|
|
|
502
|
+
validate_source_provenance(source, "#{path}.include_chain[#{index}]", nullable: false)
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# @rbs (untyped value, String path) -> void
|
|
507
|
+
def validate_parameter_expansion(value, path)
|
|
508
|
+
return if value.nil?
|
|
509
|
+
|
|
510
|
+
parameter = record(value, path, %w[rule arguments])
|
|
511
|
+
nonempty_string(parameter["rule"], "#{path}.rule")
|
|
512
|
+
array(parameter["arguments"], "#{path}.arguments").each_with_index do |argument, index|
|
|
513
|
+
nonempty_string(argument, "#{path}.arguments[#{index}]")
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# @rbs (untyped value, String path) -> void
|
|
518
|
+
def validate_inline_expansion(value, path)
|
|
519
|
+
return if value.nil?
|
|
520
|
+
|
|
521
|
+
inline = record(value, path, %w[rule])
|
|
522
|
+
nonempty_string(inline["rule"], "#{path}.rule")
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# @rbs (untyped value, String path, rhs_length: Integer) -> void
|
|
526
|
+
def validate_action_composition(value, path, rhs_length:)
|
|
527
|
+
return if value.nil?
|
|
528
|
+
|
|
529
|
+
composition = record(value, path, %w[strategy fragments], %w[plan])
|
|
530
|
+
literal(composition["strategy"], "#{path}.strategy", "sequence")
|
|
531
|
+
fragments = array(composition["fragments"], "#{path}.fragments")
|
|
532
|
+
invalid("#{path}.fragments", "must not be empty") if fragments.empty?
|
|
533
|
+
fragments.each_with_index do |value, index|
|
|
534
|
+
fragment_path = "#{path}.fragments[#{index}]"
|
|
535
|
+
fragment = record(value, fragment_path, %w[kind source])
|
|
536
|
+
enum(fragment["kind"], "#{fragment_path}.kind", %w[rule inline])
|
|
537
|
+
validate_source_provenance(fragment["source"], "#{fragment_path}.source")
|
|
538
|
+
end
|
|
539
|
+
validate_action_composition_plan(composition["plan"], "#{path}.plan", rhs_length, fragments) if
|
|
540
|
+
composition.key?("plan")
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# @rbs (untyped value, String path, Integer rhs_length, Array[untyped] fragments) -> void
|
|
544
|
+
def validate_action_composition_plan(value, path, rhs_length, fragments)
|
|
545
|
+
plan = record(value, path, %w[version physical steps])
|
|
546
|
+
literal(plan["version"], "#{path}.version", 1)
|
|
547
|
+
physical = nonnegative_integer(plan["physical"], "#{path}.physical")
|
|
548
|
+
invalid("#{path}.physical", "must equal production RHS length #{rhs_length}") unless physical == rhs_length
|
|
549
|
+
steps = array(plan["steps"], "#{path}.steps")
|
|
550
|
+
invalid("#{path}.steps", "must not be empty") if steps.empty?
|
|
551
|
+
invalid("#{path}.steps", "must align one-to-one with fragments") unless steps.length == fragments.length
|
|
552
|
+
steps.each_with_index do |step, index|
|
|
553
|
+
validate_action_composition_step(step, "#{path}.steps[#{index}]", physical, index)
|
|
554
|
+
next if step["kind"] == fragments.fetch(index)["kind"]
|
|
555
|
+
|
|
556
|
+
invalid("#{path}.steps[#{index}].kind", "must match the corresponding fragment")
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# @rbs (untyped value, String path, Integer physical, Integer step_index) -> void
|
|
561
|
+
def validate_action_composition_step(value, path, physical, step_index)
|
|
562
|
+
step = record(
|
|
563
|
+
value, path,
|
|
564
|
+
%w[kind rule code loc named_refs context_length inputs stack_inputs lookahead result_var],
|
|
565
|
+
%w[result_type]
|
|
566
|
+
)
|
|
567
|
+
kind = enum(step["kind"], "#{path}.kind", %w[rule inline])
|
|
568
|
+
rule = nullable_string(step["rule"], "#{path}.rule")
|
|
569
|
+
invalid("#{path}.rule", "must name an inline rule") if kind == "inline" && (!rule || rule.empty?)
|
|
570
|
+
nullable_string(step["code"], "#{path}.code")
|
|
571
|
+
location(step["loc"], "#{path}.loc", nullable: false)
|
|
572
|
+
context = nonnegative_integer(step["context_length"], "#{path}.context_length")
|
|
573
|
+
inputs = array(step["inputs"], "#{path}.inputs")
|
|
574
|
+
limit = physical + step_index
|
|
575
|
+
validate_action_slots(inputs, "#{path}.inputs", limit)
|
|
576
|
+
stack_inputs = array(step["stack_inputs"], "#{path}.stack_inputs")
|
|
577
|
+
validate_action_slots(stack_inputs, "#{path}.stack_inputs", limit)
|
|
578
|
+
validate_named_refs(step["named_refs"], "#{path}.named_refs", limit: [inputs.length, context].max)
|
|
579
|
+
validate_action_lookahead(step["lookahead"], "#{path}.lookahead", physical)
|
|
580
|
+
boolean(step["result_var"], "#{path}.result_var")
|
|
581
|
+
nullable_string(step["result_type"], "#{path}.result_type")
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# @rbs (Array[untyped] inputs, String path, Integer limit) -> void
|
|
585
|
+
def validate_action_slots(inputs, path, limit)
|
|
586
|
+
inputs.each_with_index do |input, index|
|
|
587
|
+
slot_path = "#{path}[#{index}]"
|
|
588
|
+
slot = nonnegative_integer(input, slot_path)
|
|
589
|
+
invalid(slot_path, "must reference an available slot below #{limit}") if slot >= limit
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# @rbs (untyped value, String path, Integer physical) -> void
|
|
594
|
+
def validate_action_lookahead(value, path, physical)
|
|
595
|
+
return if value.nil?
|
|
596
|
+
|
|
597
|
+
boundary = nonnegative_integer(value, path)
|
|
598
|
+
invalid(path, "must reference a physical slot below #{physical}") if boundary >= physical
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
# rubocop:enable Metrics/ClassLength
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
end
|