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
data/lib/ibex/ir/serialize.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "json"
|
|
|
5
5
|
module Ibex
|
|
6
6
|
module IR
|
|
7
7
|
# Stable JSON serialization for versioned pipeline IR.
|
|
8
|
+
# rubocop:disable Metrics/ModuleLength -- explicit versioned fields keep serialization changes auditable.
|
|
8
9
|
module Serialize
|
|
9
10
|
# @rbs!
|
|
10
11
|
# private def validate_version: (untyped data) -> untyped
|
|
@@ -13,29 +14,39 @@ module Ibex
|
|
|
13
14
|
# private def self.load_grammar: (untyped data) -> untyped
|
|
14
15
|
# private def load_automaton: (untyped data) -> untyped
|
|
15
16
|
# private def self.load_automaton: (untyped data) -> untyped
|
|
17
|
+
# private def load_lexer: (untyped data) -> untyped
|
|
18
|
+
# private def self.load_lexer: (untyped data) -> untyped
|
|
16
19
|
# private def load_state: (untyped state, untyped grammar) -> untyped
|
|
17
20
|
# private def self.load_state: (untyped state, untyped grammar) -> untyped
|
|
18
21
|
# private def symbol_keyed: (untyped values, untyped grammar, ?actions: untyped) -> untyped
|
|
19
22
|
# private def self.symbol_keyed: (untyped values, untyped grammar, ?actions: untyped) -> untyped
|
|
20
23
|
# private def normalize_action: (untyped value) -> untyped
|
|
21
24
|
# private def self.normalize_action: (untyped value) -> untyped
|
|
22
|
-
# private def load_production: (untyped production) -> untyped
|
|
23
|
-
# private def self.load_production: (untyped production) -> untyped
|
|
25
|
+
# private def load_production: (untyped production, Integer schema_version) -> untyped
|
|
26
|
+
# private def self.load_production: (untyped production, Integer schema_version) -> untyped
|
|
24
27
|
# private def load_user_code_chunks: (untyped chunks) -> untyped
|
|
25
28
|
# private def self.load_user_code_chunks: (untyped chunks) -> untyped
|
|
29
|
+
# private def load_symbol_metadata: (untyped symbol, String field) -> String?
|
|
30
|
+
# private def self.load_symbol_metadata: (untyped symbol, String field) -> String?
|
|
31
|
+
# private def load_grammar_tests: (untyped tests) -> untyped
|
|
32
|
+
# private def self.load_grammar_tests: (untyped tests) -> untyped
|
|
33
|
+
# private def symbol_source_position: (untyped symbol) -> String
|
|
34
|
+
# private def self.symbol_source_position: (untyped symbol) -> String
|
|
26
35
|
# private def symbolize: (untyped value) -> untyped
|
|
27
36
|
# private def self.symbolize: (untyped value) -> untyped
|
|
28
37
|
|
|
29
|
-
# @rbs (Grammar | Automaton value) -> String
|
|
38
|
+
# @rbs (Grammar | Automaton | Lexer value) -> String
|
|
30
39
|
def dump(value)
|
|
31
40
|
"#{JSON.pretty_generate(value.to_h)}\n"
|
|
32
41
|
end
|
|
33
42
|
module_function :dump
|
|
34
43
|
|
|
35
|
-
# @rbs (String source) -> (Grammar | Automaton)
|
|
44
|
+
# @rbs (String source) -> (Grammar | Automaton | Lexer)
|
|
36
45
|
def load(source)
|
|
37
46
|
data = JSON.parse(source)
|
|
38
47
|
type = data.fetch("ibex_ir") { raise Ibex::Error, "(ir):1:1: missing ibex_ir discriminator" }
|
|
48
|
+
return load_lexer(data) if type == "lexer"
|
|
49
|
+
|
|
39
50
|
validate_version(data)
|
|
40
51
|
return load_grammar(data) if type == "grammar"
|
|
41
52
|
return load_automaton(data) if type == "automaton"
|
|
@@ -52,33 +63,74 @@ module Ibex
|
|
|
52
63
|
# @rbs skip
|
|
53
64
|
def validate_version(data)
|
|
54
65
|
version = data["schema_version"]
|
|
55
|
-
return if version
|
|
66
|
+
return if SUPPORTED_SCHEMA_VERSIONS.include?(version)
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
expected = SUPPORTED_SCHEMA_VERSIONS.join(", ")
|
|
69
|
+
raise Ibex::Error, "(ir):1:1: unsupported schema_version #{version.inspect}; expected one of #{expected}"
|
|
58
70
|
end
|
|
59
71
|
|
|
60
72
|
# @rbs skip
|
|
61
|
-
def load_grammar(data)
|
|
73
|
+
def load_grammar(data) # rubocop:disable Metrics/AbcSize -- explicit fields preserve the public IR contract.
|
|
62
74
|
empty_chunks = {} #: Hash[String, untyped]
|
|
75
|
+
empty_parameters = [] #: Array[untyped]
|
|
76
|
+
empty_printers = [] #: Array[untyped]
|
|
77
|
+
empty_tests = [] #: Array[untyped]
|
|
78
|
+
empty_recovery = { "sync_tokens" => [], "on_error_reduce" => [] } #: Hash[String, untyped]
|
|
79
|
+
schema_version = data.fetch("schema_version")
|
|
63
80
|
symbols = data.fetch("symbols").map do |symbol|
|
|
64
81
|
GrammarSymbol.new(id: symbol.fetch("id"), name: symbol.fetch("name"), kind: symbol.fetch("kind"),
|
|
65
82
|
reserved: symbol.fetch("reserved"), precedence: symbolize(symbol["prec"]),
|
|
66
|
-
location: symbolize(symbol["loc"])
|
|
83
|
+
location: symbolize(symbol["loc"]),
|
|
84
|
+
display_name: load_symbol_metadata(symbol, "display_name"),
|
|
85
|
+
semantic_type: load_symbol_metadata(symbol, "semantic_type"),
|
|
86
|
+
documentation: symbol["doc"])
|
|
67
87
|
end
|
|
68
|
-
productions = data.fetch("productions").map { |production| load_production(production) }
|
|
88
|
+
productions = data.fetch("productions").map { |production| load_production(production, schema_version) }
|
|
69
89
|
Grammar.new(class_name: data.fetch("class_name"), superclass: data["superclass"], start: data.fetch("start"),
|
|
70
90
|
expect: data.fetch("expect"), options: symbolize(data.fetch("options")), symbols: symbols,
|
|
91
|
+
mode: (data["mode"] || "default").to_sym,
|
|
92
|
+
starts: data["starts"],
|
|
93
|
+
expect_rr: data["expect_rr"],
|
|
94
|
+
parser_parameters: symbolize(data.fetch("params", empty_parameters)),
|
|
95
|
+
value_printers: symbolize(data.fetch("printers", empty_printers)),
|
|
96
|
+
grammar_tests: load_grammar_tests(data.fetch("tests", empty_tests)),
|
|
97
|
+
lexer: data["lexer"] && load_lexer(data.fetch("lexer")),
|
|
98
|
+
recovery: symbolize(data.fetch("recovery", empty_recovery)),
|
|
71
99
|
productions: productions, user_code: data.fetch("user_code"),
|
|
72
100
|
conversions: data.fetch("conversions"), warnings: symbolize(data.fetch("warnings")),
|
|
73
|
-
user_code_chunks: load_user_code_chunks(data.fetch("user_code_chunks", empty_chunks))
|
|
74
|
-
|
|
101
|
+
user_code_chunks: load_user_code_chunks(data.fetch("user_code_chunks", empty_chunks)),
|
|
102
|
+
schema_version: schema_version, source_provenance: symbolize(data["source_provenance"]),
|
|
103
|
+
migration: symbolize(data["migration"]))
|
|
104
|
+
end # rubocop:enable Metrics/AbcSize
|
|
75
105
|
|
|
76
106
|
# @rbs skip
|
|
77
107
|
def load_automaton(data)
|
|
78
108
|
grammar = load_grammar(data.fetch("grammar"))
|
|
79
109
|
states = data.fetch("states").map { |state| load_state(state, grammar) }
|
|
80
110
|
Automaton.new(grammar: grammar, states: states, conflict_summary: symbolize(data.fetch("conflict_summary")),
|
|
81
|
-
algorithm: data.fetch("algorithm"), grammar_digest: data.fetch("grammar_digest")
|
|
111
|
+
algorithm: data.fetch("algorithm"), grammar_digest: data.fetch("grammar_digest"),
|
|
112
|
+
schema_version: data.fetch("schema_version"), entry_states: data["entry_states"])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @rbs skip
|
|
116
|
+
def load_lexer(data)
|
|
117
|
+
version = data.fetch("schema_version")
|
|
118
|
+
unless SUPPORTED_LEXER_SCHEMA_VERSIONS.include?(version)
|
|
119
|
+
expected = SUPPORTED_LEXER_SCHEMA_VERSIONS.join(", ")
|
|
120
|
+
raise Ibex::Error,
|
|
121
|
+
"(ir):1:1: unsupported lexer schema_version #{version.inspect}; expected one of #{expected}"
|
|
122
|
+
end
|
|
123
|
+
rules = data.fetch("rules").map do |rule|
|
|
124
|
+
LexerRule.new(
|
|
125
|
+
id: rule.fetch("id"), state: rule.fetch("state"), kind: rule.fetch("kind").to_sym,
|
|
126
|
+
token: rule["token"], pattern: rule.fetch("pattern"), pattern_kind: rule.fetch("pattern_kind").to_sym,
|
|
127
|
+
options: rule.fetch("options"), action: rule["action"], location: symbolize(rule.fetch("loc"))
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
Lexer.new(
|
|
131
|
+
states: data.fetch("states"), rules: rules, warnings: symbolize(data.fetch("warnings")),
|
|
132
|
+
schema_version: version, source_provenance: symbolize(data["source_provenance"])
|
|
133
|
+
)
|
|
82
134
|
end
|
|
83
135
|
|
|
84
136
|
# @rbs skip
|
|
@@ -112,16 +164,19 @@ module Ibex
|
|
|
112
164
|
end
|
|
113
165
|
|
|
114
166
|
# @rbs skip
|
|
115
|
-
def load_production(production)
|
|
167
|
+
def load_production(production, schema_version)
|
|
116
168
|
action_data = production["action"]
|
|
117
169
|
action = if action_data
|
|
118
170
|
Action.new(code: action_data.fetch("code"), location: symbolize(action_data["loc"]),
|
|
119
171
|
named_refs: symbolize(action_data.fetch("named_refs")),
|
|
120
|
-
context_length: action_data.fetch("context_length")
|
|
172
|
+
context_length: action_data.fetch("context_length"),
|
|
173
|
+
composition: symbolize(action_data["composition"]))
|
|
121
174
|
end
|
|
122
175
|
Production.new(id: production.fetch("id"), lhs: production.fetch("lhs"), rhs: production.fetch("rhs"),
|
|
123
176
|
action: action, precedence_override: production["prec_override"],
|
|
124
|
-
origin: symbolize(production.fetch("origin"))
|
|
177
|
+
origin: symbolize(production.fetch("origin")), documentation: production["doc"],
|
|
178
|
+
expansion: schema_version >= 2 ? symbolize(production["expansion"]) : nil,
|
|
179
|
+
node: schema_version >= 2 ? symbolize(production["node"]) : nil)
|
|
125
180
|
end
|
|
126
181
|
|
|
127
182
|
# @rbs skip
|
|
@@ -134,6 +189,39 @@ module Ibex
|
|
|
134
189
|
end
|
|
135
190
|
end
|
|
136
191
|
|
|
192
|
+
# @rbs skip
|
|
193
|
+
def load_symbol_metadata(symbol, field)
|
|
194
|
+
value = symbol[field]
|
|
195
|
+
return nil if value.nil?
|
|
196
|
+
|
|
197
|
+
position = symbol_source_position(symbol)
|
|
198
|
+
raise Ibex::Error, "#{position}: #{field} must be a String or null" unless value.is_a?(String)
|
|
199
|
+
raise Ibex::Error, "#{position}: #{field} must not be empty" if value.strip.empty?
|
|
200
|
+
raise Ibex::Error, "#{position}: #{field} must be a single line" if value.match?(/[\r\n]/)
|
|
201
|
+
raise Ibex::Error, "#{position}: #{field} must not contain control characters" if
|
|
202
|
+
value.match?(/[[:cntrl:]]/)
|
|
203
|
+
|
|
204
|
+
value
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @rbs skip
|
|
208
|
+
def symbol_source_position(symbol)
|
|
209
|
+
location = symbol["loc"]
|
|
210
|
+
return "(ir):1:1" unless location.is_a?(Hash)
|
|
211
|
+
|
|
212
|
+
file = location["file"]
|
|
213
|
+
line = location["line"]
|
|
214
|
+
column = location["column"]
|
|
215
|
+
return "(ir):1:1" unless file.is_a?(String) && line.is_a?(Integer) && column.is_a?(Integer)
|
|
216
|
+
|
|
217
|
+
"#{file}:#{line}:#{column}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# @rbs skip
|
|
221
|
+
def load_grammar_tests(tests)
|
|
222
|
+
symbolize(tests).map { |test| test.merge(expectation: test.fetch(:expectation).to_sym) }
|
|
223
|
+
end
|
|
224
|
+
|
|
137
225
|
# @rbs skip
|
|
138
226
|
def symbolize(value)
|
|
139
227
|
case value
|
|
@@ -142,13 +230,16 @@ module Ibex
|
|
|
142
230
|
else value
|
|
143
231
|
end
|
|
144
232
|
end
|
|
145
|
-
module_function :validate_version, :load_grammar, :load_automaton, :load_state, :symbol_keyed,
|
|
146
|
-
:normalize_action, :load_production, :load_user_code_chunks, :
|
|
233
|
+
module_function :validate_version, :load_grammar, :load_automaton, :load_lexer, :load_state, :symbol_keyed,
|
|
234
|
+
:normalize_action, :load_production, :load_user_code_chunks, :load_symbol_metadata,
|
|
235
|
+
:symbol_source_position, :load_grammar_tests, :symbolize
|
|
147
236
|
|
|
148
237
|
class << self
|
|
149
|
-
private :validate_version, :load_grammar, :load_automaton, :load_state, :symbol_keyed,
|
|
150
|
-
:normalize_action, :load_production, :load_user_code_chunks, :
|
|
238
|
+
private :validate_version, :load_grammar, :load_automaton, :load_lexer, :load_state, :symbol_keyed,
|
|
239
|
+
:normalize_action, :load_production, :load_user_code_chunks, :load_symbol_metadata,
|
|
240
|
+
:symbol_source_position, :load_grammar_tests, :symbolize
|
|
151
241
|
end
|
|
152
242
|
end
|
|
243
|
+
# rubocop:enable Metrics/ModuleLength
|
|
153
244
|
end
|
|
154
245
|
end
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module IR
|
|
5
|
+
module Validator
|
|
6
|
+
# Structural and referential validation for a versioned Automaton IR JSON object.
|
|
7
|
+
# rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.
|
|
8
|
+
class AutomatonDocument < Base
|
|
9
|
+
ROOT_REQUIRED = %w[
|
|
10
|
+
ibex_ir schema_version algorithm grammar_digest grammar states conflict_summary
|
|
11
|
+
].freeze #: Array[String]
|
|
12
|
+
V2_ROOT_OPTIONAL = %w[entry_states].freeze #: Array[String]
|
|
13
|
+
STATE_REQUIRED = %w[id items transitions actions gotos default_action conflicts].freeze #: Array[String]
|
|
14
|
+
ACTION_TYPES = %w[shift reduce accept error].freeze #: Array[String]
|
|
15
|
+
RESOLUTION_KINDS = %w[definition_order default_shift precedence associativity].freeze #: Array[String]
|
|
16
|
+
|
|
17
|
+
# @rbs @data: Hash[String, untyped]
|
|
18
|
+
# @rbs @states_by_id: Hash[Integer, Hash[String, untyped]]
|
|
19
|
+
# @rbs @grammar: GrammarDocument
|
|
20
|
+
# @rbs @version: Integer
|
|
21
|
+
|
|
22
|
+
# @rbs (Hash[String, untyped] data, ?version: Integer) -> void
|
|
23
|
+
def initialize(data, version: data.fetch("schema_version"))
|
|
24
|
+
super()
|
|
25
|
+
@data = data
|
|
26
|
+
@version = version
|
|
27
|
+
@states_by_id = {} #: Hash[Integer, Hash[String, untyped]]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs () -> self
|
|
31
|
+
def validate
|
|
32
|
+
record(@data, "$", ROOT_REQUIRED, @version >= 2 ? V2_ROOT_OPTIONAL : [])
|
|
33
|
+
literal(@data["ibex_ir"], "$.ibex_ir", "automaton")
|
|
34
|
+
literal(@data["schema_version"], "$.schema_version", @version)
|
|
35
|
+
enum(@data["algorithm"], "$.algorithm", %w[slr lalr1 ielr1 lr1])
|
|
36
|
+
validate_digest
|
|
37
|
+
grammar = object(@data["grammar"], "$.grammar")
|
|
38
|
+
literal(grammar["schema_version"], "$.grammar.schema_version", @version)
|
|
39
|
+
@grammar = GrammarDocument.new(grammar, path: "$.grammar", version: @version).validate
|
|
40
|
+
validate_state_records
|
|
41
|
+
validate_entry_states
|
|
42
|
+
validate_state_contents
|
|
43
|
+
validate_conflict_summary
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# @rbs () -> void
|
|
50
|
+
def validate_entry_states
|
|
51
|
+
value = @data["entry_states"]
|
|
52
|
+
if value.nil?
|
|
53
|
+
invalid("$.entry_states", "is required for multiple start symbols") if @data.dig("grammar", "starts")
|
|
54
|
+
return
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
entries = object(value, "$.entry_states")
|
|
58
|
+
expected = @data.dig("grammar", "starts") || [@data.dig("grammar", "start")]
|
|
59
|
+
invalid("$.entry_states", "keys must equal grammar starts in order") unless entries.keys == expected
|
|
60
|
+
entries.each do |name, state|
|
|
61
|
+
nonnegative_integer(state, "$.entry_states.#{name}")
|
|
62
|
+
invalid("$.entry_states.#{name}", "references missing state #{state}") unless @states_by_id[state]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @rbs () -> void
|
|
67
|
+
def validate_digest
|
|
68
|
+
digest = string(@data["grammar_digest"], "$.grammar_digest")
|
|
69
|
+
invalid("$.grammar_digest", "must be a sha256 digest") unless digest.match?(/\Asha256:[0-9a-f]{64}\z/)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @rbs () -> void
|
|
73
|
+
def validate_state_records
|
|
74
|
+
states = array(@data["states"], "$.states")
|
|
75
|
+
invalid("$.states", "must contain at least one state") if states.empty?
|
|
76
|
+
states.each_with_index do |value, index|
|
|
77
|
+
path = "$.states[#{index}]"
|
|
78
|
+
state = record(value, path, STATE_REQUIRED)
|
|
79
|
+
id = nonnegative_integer(state["id"], "#{path}.id")
|
|
80
|
+
invalid("#{path}.id", "must equal its array index #{index}") unless id == index
|
|
81
|
+
@states_by_id[id] = state
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @rbs () -> void
|
|
86
|
+
def validate_state_contents
|
|
87
|
+
@states_by_id.each do |id, state|
|
|
88
|
+
path = "$.states[#{id}]"
|
|
89
|
+
validate_items(state["items"], "#{path}.items")
|
|
90
|
+
validate_transitions(state["transitions"], "#{path}.transitions")
|
|
91
|
+
validate_actions(state["actions"], "#{path}.actions")
|
|
92
|
+
validate_gotos(state["gotos"], "#{path}.gotos")
|
|
93
|
+
validate_parser_action(state["default_action"], "#{path}.default_action", nullable: true)
|
|
94
|
+
validate_conflicts(state["conflicts"], "#{path}.conflicts")
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs (untyped value, String path) -> void
|
|
99
|
+
def validate_items(value, path)
|
|
100
|
+
array(value, path).each_with_index do |item, index|
|
|
101
|
+
item_path = "#{path}[#{index}]"
|
|
102
|
+
item = record(item, item_path, %w[production dot lookaheads])
|
|
103
|
+
production = integer(item["production"], "#{item_path}.production")
|
|
104
|
+
validate_item_production(production, item["dot"], item_path)
|
|
105
|
+
validate_lookaheads(item["lookaheads"], "#{item_path}.lookaheads")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs (Integer production_id, untyped dot_value, String path) -> void
|
|
110
|
+
def validate_item_production(production_id, dot_value, path)
|
|
111
|
+
dot = nonnegative_integer(dot_value, "#{path}.dot")
|
|
112
|
+
if production_id == -1
|
|
113
|
+
invalid("#{path}.dot", "must not exceed 1 for the augmented production") if dot > 1
|
|
114
|
+
return
|
|
115
|
+
end
|
|
116
|
+
production = @grammar.productions_by_id[production_id]
|
|
117
|
+
invalid("#{path}.production", "references missing production id #{production_id}") unless production
|
|
118
|
+
invalid("#{path}.dot", "exceeds production #{production_id} length") if dot > production["rhs"].length
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @rbs (untyped value, String path) -> void
|
|
122
|
+
def validate_lookaheads(value, path)
|
|
123
|
+
array(value, path).each_with_index do |name, index|
|
|
124
|
+
name = string(name, "#{path}[#{index}]")
|
|
125
|
+
symbol = @grammar.symbols_by_name[name]
|
|
126
|
+
invalid("#{path}[#{index}]", "references missing symbol #{name.inspect}") unless symbol
|
|
127
|
+
invalid("#{path}[#{index}]", "must reference a terminal") unless symbol["kind"] == "terminal"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @rbs (untyped value, String path) -> void
|
|
132
|
+
def validate_transitions(value, path)
|
|
133
|
+
symbol_map(value, path) do |target, target_path, _symbol|
|
|
134
|
+
validate_state_reference(target, target_path)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# @rbs (untyped value, String path) -> void
|
|
139
|
+
def validate_actions(value, path)
|
|
140
|
+
symbol_map(value, path, kind: "terminal") do |action, action_path, _symbol|
|
|
141
|
+
validate_parser_action(action, action_path)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @rbs (untyped value, String path) -> void
|
|
146
|
+
def validate_gotos(value, path)
|
|
147
|
+
symbol_map(value, path, kind: "nonterminal") do |target, target_path, _symbol|
|
|
148
|
+
validate_state_reference(target, target_path)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @rbs (untyped value, String path, ?kind: String?) { (untyped, String, Hash[String, untyped]) -> void } -> void
|
|
153
|
+
def symbol_map(value, path, kind: nil, &block)
|
|
154
|
+
object(value, path).each do |name, item|
|
|
155
|
+
item_path = child_path(path, name)
|
|
156
|
+
symbol = @grammar.symbols_by_name[name]
|
|
157
|
+
invalid(item_path, "references missing symbol #{name.inspect}") unless symbol
|
|
158
|
+
invalid(item_path, "must reference a #{kind}") if kind && symbol["kind"] != kind
|
|
159
|
+
block.call(item, item_path, symbol)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @rbs (untyped value, String path) -> void
|
|
164
|
+
def validate_state_reference(value, path)
|
|
165
|
+
id = nonnegative_integer(value, path)
|
|
166
|
+
invalid(path, "references missing state id #{id}") unless @states_by_id.key?(id)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @rbs (untyped value, String path, ?nullable: bool) -> void
|
|
170
|
+
def validate_parser_action(value, path, nullable: false)
|
|
171
|
+
return if nullable && value.nil?
|
|
172
|
+
|
|
173
|
+
action = object(value, path)
|
|
174
|
+
type = enum(field(action, "type", path), "#{path}.type", ACTION_TYPES)
|
|
175
|
+
required = case type
|
|
176
|
+
when "shift" then %w[type state]
|
|
177
|
+
when "reduce" then %w[type production]
|
|
178
|
+
else %w[type]
|
|
179
|
+
end
|
|
180
|
+
record(action, path, required)
|
|
181
|
+
validate_state_reference(action["state"], "#{path}.state") if type == "shift"
|
|
182
|
+
validate_production_reference(action["production"], "#{path}.production") if type == "reduce"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @rbs (untyped value, String path) -> void
|
|
186
|
+
def validate_production_reference(value, path)
|
|
187
|
+
id = nonnegative_integer(value, path)
|
|
188
|
+
invalid(path, "references missing production id #{id}") unless @grammar.productions_by_id.key?(id)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# @rbs (untyped value, String path) -> void
|
|
192
|
+
def validate_conflicts(value, path)
|
|
193
|
+
array(value, path).each_with_index do |conflict, index|
|
|
194
|
+
conflict_path = "#{path}[#{index}]"
|
|
195
|
+
conflict = object(conflict, conflict_path)
|
|
196
|
+
type = enum(field(conflict, "type", conflict_path), "#{conflict_path}.type",
|
|
197
|
+
%w[shift_reduce reduce_reduce])
|
|
198
|
+
if type == "shift_reduce"
|
|
199
|
+
validate_shift_reduce(conflict, conflict_path)
|
|
200
|
+
else
|
|
201
|
+
validate_reduce_reduce(conflict, conflict_path)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# @rbs (Hash[String, untyped] conflict, String path) -> void
|
|
207
|
+
def validate_shift_reduce(conflict, path)
|
|
208
|
+
record(conflict, path, %w[type symbol shift_to reduce resolution], %w[midrule_origins entries composite])
|
|
209
|
+
validate_conflict_symbol(conflict["symbol"], "#{path}.symbol")
|
|
210
|
+
validate_state_reference(conflict["shift_to"], "#{path}.shift_to")
|
|
211
|
+
validate_production_reference(conflict["reduce"], "#{path}.reduce")
|
|
212
|
+
validate_resolution(conflict["resolution"], "#{path}.resolution")
|
|
213
|
+
validate_midrule_origins(conflict["midrule_origins"], "#{path}.midrule_origins") if
|
|
214
|
+
conflict.key?("midrule_origins")
|
|
215
|
+
validate_conflict_entries(conflict, path)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# @rbs (Hash[String, untyped] conflict, String path) -> void
|
|
219
|
+
def validate_reduce_reduce(conflict, path)
|
|
220
|
+
record(conflict, path, %w[type symbol reductions resolution], %w[midrule_origins entries composite])
|
|
221
|
+
validate_conflict_symbol(conflict["symbol"], "#{path}.symbol")
|
|
222
|
+
reductions = array(conflict["reductions"], "#{path}.reductions")
|
|
223
|
+
invalid("#{path}.reductions", "must contain at least two productions") if reductions.length < 2
|
|
224
|
+
reductions.each_with_index do |id, index|
|
|
225
|
+
validate_production_reference(id, "#{path}.reductions[#{index}]")
|
|
226
|
+
end
|
|
227
|
+
if reductions.uniq.length != reductions.length
|
|
228
|
+
invalid("#{path}.reductions", "must contain unique production ids")
|
|
229
|
+
end
|
|
230
|
+
validate_resolution(conflict["resolution"], "#{path}.resolution", reductions: reductions)
|
|
231
|
+
validate_midrule_origins(conflict["midrule_origins"], "#{path}.midrule_origins") if
|
|
232
|
+
conflict.key?("midrule_origins")
|
|
233
|
+
validate_conflict_entries(conflict, path)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# @rbs (Hash[String, untyped] conflict, String path) -> void
|
|
237
|
+
def validate_conflict_entries(conflict, path)
|
|
238
|
+
if conflict.key?("entries")
|
|
239
|
+
starts = @data.dig("grammar", "starts") || [@data.dig("grammar", "start")]
|
|
240
|
+
entries = array(conflict["entries"], "#{path}.entries")
|
|
241
|
+
invalid("#{path}.entries", "must not be empty") if entries.empty?
|
|
242
|
+
entries.each_with_index do |name, index|
|
|
243
|
+
name = nonempty_string(name, "#{path}.entries[#{index}]")
|
|
244
|
+
invalid("#{path}.entries[#{index}]", "is not a grammar start symbol") unless starts.include?(name)
|
|
245
|
+
end
|
|
246
|
+
invalid("#{path}.entries", "must be unique") unless entries.uniq.length == entries.length
|
|
247
|
+
end
|
|
248
|
+
boolean(conflict["composite"], "#{path}.composite") if conflict.key?("composite")
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# @rbs (untyped value, String path) -> void
|
|
252
|
+
def validate_midrule_origins(value, path)
|
|
253
|
+
origins = array(value, path)
|
|
254
|
+
invalid(path, "must not be empty") if origins.empty?
|
|
255
|
+
origins.each_with_index { |origin, index| location(origin, "#{path}[#{index}]") }
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @rbs (untyped value, String path) -> void
|
|
259
|
+
def validate_conflict_symbol(value, path)
|
|
260
|
+
name = string(value, path)
|
|
261
|
+
symbol = @grammar.symbols_by_name[name]
|
|
262
|
+
invalid(path, "references missing symbol #{name.inspect}") unless symbol
|
|
263
|
+
invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# @rbs (untyped value, String path, ?reductions: Array[Integer]?) -> void
|
|
267
|
+
def validate_resolution(value, path, reductions: nil)
|
|
268
|
+
resolution = record(value, path, %w[by chose], %w[associativity])
|
|
269
|
+
enum(resolution["by"], "#{path}.by", RESOLUTION_KINDS)
|
|
270
|
+
if reductions
|
|
271
|
+
chosen = resolution["chose"]
|
|
272
|
+
validate_production_reference(chosen, "#{path}.chose")
|
|
273
|
+
invalid("#{path}.chose", "must be one of the reductions") unless reductions.include?(chosen)
|
|
274
|
+
else
|
|
275
|
+
enum(resolution["chose"], "#{path}.chose", %w[shift reduce error])
|
|
276
|
+
end
|
|
277
|
+
return unless resolution.key?("associativity")
|
|
278
|
+
|
|
279
|
+
enum(resolution["associativity"], "#{path}.associativity", %w[left right nonassoc])
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# @rbs () -> void
|
|
283
|
+
def validate_conflict_summary
|
|
284
|
+
path = "$.conflict_summary"
|
|
285
|
+
summary = record(
|
|
286
|
+
@data["conflict_summary"], path, %w[sr resolved_sr rr expected_sr expectation_met],
|
|
287
|
+
%w[expected_rr rr_expectation_met]
|
|
288
|
+
)
|
|
289
|
+
%w[sr resolved_sr rr expected_sr].each do |key|
|
|
290
|
+
nonnegative_integer(summary[key], "#{path}.#{key}")
|
|
291
|
+
end
|
|
292
|
+
boolean(summary["expectation_met"], "#{path}.expectation_met")
|
|
293
|
+
if summary.key?("expected_rr") || summary.key?("rr_expectation_met")
|
|
294
|
+
nonnegative_integer(summary["expected_rr"], "#{path}.expected_rr")
|
|
295
|
+
boolean(summary["rr_expectation_met"], "#{path}.rr_expectation_met")
|
|
296
|
+
end
|
|
297
|
+
validate_conflict_counts(summary, path)
|
|
298
|
+
validate_expectation(summary, path)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# @rbs (Hash[String, untyped] summary, String path) -> void
|
|
302
|
+
def validate_conflict_counts(summary, path)
|
|
303
|
+
conflicts = @states_by_id.values.flat_map { |state| state["conflicts"] }
|
|
304
|
+
shift_reduce = conflicts.select { |conflict| conflict["type"] == "shift_reduce" }
|
|
305
|
+
counts = {
|
|
306
|
+
"sr" => shift_reduce.count { |conflict| conflict.dig("resolution", "by") == "default_shift" },
|
|
307
|
+
"resolved_sr" => shift_reduce.count { |conflict| conflict.dig("resolution", "by") != "default_shift" },
|
|
308
|
+
"rr" => conflicts.count { |conflict| conflict["type"] == "reduce_reduce" }
|
|
309
|
+
}
|
|
310
|
+
counts.each do |key, actual|
|
|
311
|
+
next if summary[key] == actual
|
|
312
|
+
|
|
313
|
+
label = key == "rr" ? "reduce/reduce" : "shift/reduce"
|
|
314
|
+
invalid("#{path}.#{key}", "must equal the #{actual} recorded #{label} conflicts")
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# @rbs (Hash[String, untyped] summary, String path) -> void
|
|
319
|
+
def validate_expectation(summary, path)
|
|
320
|
+
grammar_expect = @data.fetch("grammar").fetch("expect")
|
|
321
|
+
unless summary["expected_sr"] == grammar_expect
|
|
322
|
+
invalid("#{path}.expected_sr", "must equal embedded grammar expect #{grammar_expect}")
|
|
323
|
+
end
|
|
324
|
+
expected_met = summary["sr"] == summary["expected_sr"]
|
|
325
|
+
unless summary["expectation_met"] == expected_met
|
|
326
|
+
invalid("#{path}.expectation_met", "must be #{expected_met} for the recorded shift/reduce count")
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
grammar_expect_rr = @data.fetch("grammar")["expect_rr"]
|
|
330
|
+
return if grammar_expect_rr.nil?
|
|
331
|
+
|
|
332
|
+
unless summary["expected_rr"] == grammar_expect_rr
|
|
333
|
+
invalid("#{path}.expected_rr", "must equal embedded grammar expect_rr #{grammar_expect_rr}")
|
|
334
|
+
end
|
|
335
|
+
rr_expected_met = summary["rr"] == summary["expected_rr"]
|
|
336
|
+
return if summary["rr_expectation_met"] == rr_expected_met
|
|
337
|
+
|
|
338
|
+
invalid("#{path}.rr_expectation_met",
|
|
339
|
+
"must be #{rr_expected_met} for the recorded reduce/reduce count")
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
# rubocop:enable Metrics/ClassLength
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|