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/frontend/ast.rb
CHANGED
|
@@ -2,18 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
module Ibex
|
|
4
4
|
module Frontend
|
|
5
|
+
# Keeps the public Rule constructor compatible with ASTs built before parameters and inline rules existed.
|
|
6
|
+
module ASTRuleDefaults
|
|
7
|
+
# @rbs (*untyped arguments, **untyped keywords) -> void
|
|
8
|
+
def initialize(*arguments, **keywords)
|
|
9
|
+
if arguments.empty?
|
|
10
|
+
keywords = keywords.merge(parameters: keywords[:parameters] || [], inline: keywords[:inline] || false)
|
|
11
|
+
elsif arguments.one? && arguments.first.is_a?(Hash)
|
|
12
|
+
attributes = arguments.first
|
|
13
|
+
arguments = [
|
|
14
|
+
attributes.merge(parameters: attributes[:parameters] || [], inline: attributes[:inline] || false)
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
super(*arguments, **keywords) # rubocop:disable Style/SuperArguments
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
5
21
|
# Grammar frontend node types.
|
|
22
|
+
# rubocop:disable Metrics/ModuleLength -- the public node vocabulary is intentionally kept together.
|
|
6
23
|
module AST
|
|
7
24
|
# @rbs!
|
|
8
|
-
# type
|
|
9
|
-
# type
|
|
25
|
+
# type symbol_metadata = DisplayName | SemanticType
|
|
26
|
+
# type declaration = Include | Tokens | Precedence | Options | Expect | ExpectRR | Start | Recovery |
|
|
27
|
+
# OnErrorReduce | GrammarTest | Lexer | Convert | Parameter | Printer | symbol_metadata
|
|
28
|
+
# type lexer_entry = LexerRule | LexerState
|
|
29
|
+
# type item = SymbolReference | ParameterizedReference | InlineAction | Optional | Star | Plus | Group |
|
|
30
|
+
# SeparatedList | Empty
|
|
10
31
|
# type user_code = Hash[String, Array[UserCode]]
|
|
32
|
+
# class Rule < Struct[String | Array[String] | Array[Alternative] | Location | String? | bool]
|
|
33
|
+
# attr_accessor lhs: String
|
|
34
|
+
# attr_accessor parameters: Array[String]
|
|
35
|
+
# attr_accessor alternatives: Array[Alternative]
|
|
36
|
+
# attr_accessor loc: Location
|
|
37
|
+
# attr_accessor documentation: String?
|
|
38
|
+
# attr_accessor inline: bool
|
|
39
|
+
# def self.new: (?lhs: String, ?parameters: Array[String]?, ?alternatives: Array[Alternative],
|
|
40
|
+
# ?loc: Location, ?documentation: String?, ?inline: bool?) -> instance
|
|
41
|
+
# | ({ ?lhs: String, ?parameters: Array[String]?, ?alternatives: Array[Alternative],
|
|
42
|
+
# ?loc: Location, ?documentation: String?, ?inline: bool? }) -> instance
|
|
43
|
+
# end
|
|
11
44
|
|
|
12
45
|
# Adds deterministic, recursively serializable hashes to Struct nodes.
|
|
13
46
|
# @rbs module-self Struct[untyped]
|
|
14
47
|
module Node
|
|
15
48
|
def to_h
|
|
16
49
|
fields = each_pair.to_h { |name, value| [name, serialize(value)] }
|
|
50
|
+
fields.delete(:aliases) if self.class.name.end_with?("::Tokens") && !fields[:aliases]
|
|
51
|
+
if self.class.name.end_with?("::Root")
|
|
52
|
+
fields.delete(:extended) unless fields[:extended]
|
|
53
|
+
fields.delete(:cst) unless fields[:cst]
|
|
54
|
+
end
|
|
55
|
+
fields.delete(:node_annotation) if self.class.name.end_with?("::Alternative") && !fields[:node_annotation]
|
|
17
56
|
{ node: self.class.name.split("::").last }.merge(fields)
|
|
18
57
|
end
|
|
19
58
|
|
|
@@ -37,12 +76,24 @@ module Ibex
|
|
|
37
76
|
:rules, #: Array[Rule]
|
|
38
77
|
:user_code, #: user_code
|
|
39
78
|
:loc, #: Location
|
|
79
|
+
:extended, #: bool?
|
|
80
|
+
:cst, #: bool?
|
|
40
81
|
keyword_init: true
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
) { include Node }
|
|
83
|
+
Fragment = Struct.new(
|
|
84
|
+
:declarations, #: Array[declaration]
|
|
85
|
+
:rules, #: Array[Rule]
|
|
86
|
+
:loc, #: Location
|
|
87
|
+
keyword_init: true
|
|
88
|
+
) { include Node }
|
|
89
|
+
Include = Struct.new(
|
|
90
|
+
:path, #: String
|
|
91
|
+
:loc, #: Location
|
|
92
|
+
keyword_init: true
|
|
93
|
+
) { include Node }
|
|
44
94
|
Tokens = Struct.new(
|
|
45
95
|
:names, #: Array[String]
|
|
96
|
+
:aliases, #: Hash[String, String]?
|
|
46
97
|
:loc, #: Location
|
|
47
98
|
keyword_init: true
|
|
48
99
|
) { include Node }
|
|
@@ -68,8 +119,49 @@ module Ibex
|
|
|
68
119
|
:loc, #: Location
|
|
69
120
|
keyword_init: true
|
|
70
121
|
) { include Node }
|
|
122
|
+
ExpectRR = Struct.new(
|
|
123
|
+
:conflicts, #: Integer
|
|
124
|
+
:loc, #: Location
|
|
125
|
+
keyword_init: true
|
|
126
|
+
) { include Node }
|
|
71
127
|
Start = Struct.new(
|
|
128
|
+
:names, #: Array[String]
|
|
129
|
+
:loc, #: Location
|
|
130
|
+
keyword_init: true
|
|
131
|
+
) { include Node }
|
|
132
|
+
Recovery = Struct.new(
|
|
133
|
+
:sync_tokens, #: Array[String]
|
|
134
|
+
:loc, #: Location
|
|
135
|
+
keyword_init: true
|
|
136
|
+
) { include Node }
|
|
137
|
+
OnErrorReduce = Struct.new(
|
|
138
|
+
:names, #: Array[String]
|
|
139
|
+
:loc, #: Location
|
|
140
|
+
keyword_init: true
|
|
141
|
+
) { include Node }
|
|
142
|
+
GrammarTest = Struct.new(
|
|
143
|
+
:expectation, #: Symbol
|
|
144
|
+
:source, #: String
|
|
145
|
+
:loc, #: Location
|
|
146
|
+
keyword_init: true
|
|
147
|
+
) { include Node }
|
|
148
|
+
Lexer = Struct.new(
|
|
149
|
+
:definitions, #: Array[lexer_entry]
|
|
150
|
+
:loc, #: Location
|
|
151
|
+
keyword_init: true
|
|
152
|
+
) { include Node }
|
|
153
|
+
LexerRule = Struct.new(
|
|
154
|
+
:kind, #: Symbol
|
|
155
|
+
:token, #: String?
|
|
156
|
+
:pattern, #: String
|
|
157
|
+
:pattern_kind, #: Symbol
|
|
158
|
+
:action, #: String?
|
|
159
|
+
:loc, #: Location
|
|
160
|
+
keyword_init: true
|
|
161
|
+
) { include Node }
|
|
162
|
+
LexerState = Struct.new(
|
|
72
163
|
:name, #: String
|
|
164
|
+
:definitions, #: Array[lexer_entry]
|
|
73
165
|
:loc, #: Location
|
|
74
166
|
keyword_init: true
|
|
75
167
|
) { include Node }
|
|
@@ -84,16 +176,51 @@ module Ibex
|
|
|
84
176
|
:loc, #: Location
|
|
85
177
|
keyword_init: true
|
|
86
178
|
) { include Node }
|
|
179
|
+
DisplayName = Struct.new(
|
|
180
|
+
:name, #: String
|
|
181
|
+
:value, #: String
|
|
182
|
+
:loc, #: Location
|
|
183
|
+
keyword_init: true
|
|
184
|
+
) { include Node }
|
|
185
|
+
SemanticType = Struct.new(
|
|
186
|
+
:name, #: String
|
|
187
|
+
:value, #: String
|
|
188
|
+
:loc, #: Location
|
|
189
|
+
keyword_init: true
|
|
190
|
+
) { include Node }
|
|
191
|
+
Parameter = Struct.new(
|
|
192
|
+
:name, #: String
|
|
193
|
+
:semantic_type, #: String?
|
|
194
|
+
:loc, #: Location
|
|
195
|
+
keyword_init: true
|
|
196
|
+
) { include Node }
|
|
197
|
+
Printer = Struct.new(
|
|
198
|
+
:name, #: String
|
|
199
|
+
:code, #: String
|
|
200
|
+
:loc, #: Location
|
|
201
|
+
keyword_init: true
|
|
202
|
+
) { include Node }
|
|
203
|
+
# @rbs skip
|
|
87
204
|
Rule = Struct.new(
|
|
88
205
|
:lhs, #: String
|
|
206
|
+
:parameters, #: Array[String]
|
|
89
207
|
:alternatives, #: Array[Alternative]
|
|
90
208
|
:loc, #: Location
|
|
209
|
+
:documentation, #: String?
|
|
210
|
+
:inline, #: bool
|
|
91
211
|
keyword_init: true
|
|
92
|
-
) { include Node }
|
|
212
|
+
) { include Node, ASTRuleDefaults }
|
|
93
213
|
Alternative = Struct.new(
|
|
94
214
|
:items, #: Array[item]
|
|
95
215
|
:action, #: InlineAction?
|
|
96
216
|
:precedence, #: String?
|
|
217
|
+
:node_annotation, #: NodeAnnotation?
|
|
218
|
+
:loc, #: Location
|
|
219
|
+
keyword_init: true
|
|
220
|
+
) { include Node }
|
|
221
|
+
NodeAnnotation = Struct.new(
|
|
222
|
+
:name, #: String
|
|
223
|
+
:fields, #: Array[String]
|
|
97
224
|
:loc, #: Location
|
|
98
225
|
keyword_init: true
|
|
99
226
|
) { include Node }
|
|
@@ -103,11 +230,22 @@ module Ibex
|
|
|
103
230
|
:loc, #: Location
|
|
104
231
|
keyword_init: true
|
|
105
232
|
) { include Node }
|
|
233
|
+
ParameterizedReference = Struct.new(
|
|
234
|
+
:name, #: String
|
|
235
|
+
:arguments, #: Array[item]
|
|
236
|
+
:named_reference, #: String?
|
|
237
|
+
:loc, #: Location
|
|
238
|
+
keyword_init: true
|
|
239
|
+
) { include Node }
|
|
106
240
|
InlineAction = Struct.new(
|
|
107
241
|
:code, #: String
|
|
108
242
|
:loc, #: Location
|
|
109
243
|
keyword_init: true
|
|
110
244
|
) { include Node }
|
|
245
|
+
Empty = Struct.new(
|
|
246
|
+
:loc, #: Location
|
|
247
|
+
keyword_init: true
|
|
248
|
+
) { include Node }
|
|
111
249
|
Optional = Struct.new(
|
|
112
250
|
:item, #: item
|
|
113
251
|
:loc, #: Location
|
|
@@ -142,5 +280,6 @@ module Ibex
|
|
|
142
280
|
keyword_init: true
|
|
143
281
|
) { include Node }
|
|
144
282
|
end
|
|
283
|
+
# rubocop:enable Metrics/ModuleLength
|
|
145
284
|
end
|
|
146
285
|
end
|
|
@@ -6,18 +6,20 @@ module Ibex
|
|
|
6
6
|
class BootstrapParser
|
|
7
7
|
include BootstrapParserDeclarations
|
|
8
8
|
include BootstrapParserRules
|
|
9
|
+
include BootstrapParserParameters
|
|
9
10
|
|
|
10
11
|
# @rbs @tokens: Array[Token]
|
|
11
12
|
# @rbs @index: Integer
|
|
12
13
|
# @rbs @mode: Symbol
|
|
13
14
|
|
|
14
15
|
# @rbs (String | Array[Token] source, ?file: String, ?mode: Symbol) -> void
|
|
15
|
-
def initialize(source, file: "(grammar)", mode: :
|
|
16
|
-
raise ArgumentError, "mode must be :
|
|
16
|
+
def initialize(source, file: "(grammar)", mode: :default)
|
|
17
|
+
raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
|
|
17
18
|
|
|
18
19
|
@tokens = source.is_a?(Array) ? source : Lexer.new(source, file: file).tokenize
|
|
19
20
|
@index = 0
|
|
20
21
|
@mode = mode
|
|
22
|
+
@pragmas = {} #: Hash[String, bool]
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
# @rbs () -> AST::Root
|
|
@@ -29,15 +31,56 @@ module Ibex
|
|
|
29
31
|
declarations = parse_declarations
|
|
30
32
|
expect_keyword("rule")
|
|
31
33
|
rules = parse_rules
|
|
32
|
-
expect_keyword("end")
|
|
34
|
+
expect_keyword("end") unless current.type == :user_code
|
|
33
35
|
user_code = parse_user_code
|
|
34
36
|
expect(:eof)
|
|
35
37
|
AST::Root.new(class_name: class_name, superclass: superclass, declarations: declarations,
|
|
36
|
-
rules: rules, user_code: user_code, loc: location
|
|
38
|
+
rules: rules, user_code: user_code, loc: location,
|
|
39
|
+
extended: @mode == :extended || @pragmas.key?("extended") || @pragmas.key?("cst"),
|
|
40
|
+
cst: @pragmas.key?("cst"))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @rbs () -> AST::Fragment
|
|
44
|
+
def parse_fragment
|
|
45
|
+
location = expect_keyword("fragment").location
|
|
46
|
+
extended_only!(location, "fragments")
|
|
47
|
+
declarations = parse_declarations
|
|
48
|
+
reject_fragment_root_declarations(declarations)
|
|
49
|
+
expect_keyword("rule")
|
|
50
|
+
rules = keyword?("end") ? Array.new(0) : parse_rules #: Array[AST::Rule]
|
|
51
|
+
expect_keyword("end")
|
|
52
|
+
user_code = parse_user_code
|
|
53
|
+
fail_at(user_code.values.flatten.first.loc, "user code is not allowed in fragments") unless user_code.empty?
|
|
54
|
+
expect(:eof)
|
|
55
|
+
AST::Fragment.new(declarations: declarations, rules: rules, loc: location)
|
|
37
56
|
end
|
|
38
57
|
|
|
39
58
|
private
|
|
40
59
|
|
|
60
|
+
# @rbs (Array[AST::declaration] declarations) -> void
|
|
61
|
+
def reject_fragment_root_declarations(declarations)
|
|
62
|
+
root_only = declarations.find { |declaration| fragment_root_declaration_name(declaration) }
|
|
63
|
+
return unless root_only
|
|
64
|
+
|
|
65
|
+
name = fragment_root_declaration_name(root_only)
|
|
66
|
+
fail_at(root_only.loc, "#{name} declarations are not allowed in fragments")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @rbs (AST::declaration declaration) -> String?
|
|
70
|
+
def fragment_root_declaration_name(declaration)
|
|
71
|
+
return "options" if declaration.is_a?(AST::Options)
|
|
72
|
+
return "expect" if declaration.is_a?(AST::Expect)
|
|
73
|
+
return "%expect-rr" if declaration.is_a?(AST::ExpectRR)
|
|
74
|
+
return "%param" if declaration.is_a?(AST::Parameter)
|
|
75
|
+
return "start" if declaration.is_a?(AST::Start)
|
|
76
|
+
return "%recover" if declaration.is_a?(AST::Recovery)
|
|
77
|
+
return "%on_error_reduce" if declaration.is_a?(AST::OnErrorReduce)
|
|
78
|
+
return "%test" if declaration.is_a?(AST::GrammarTest)
|
|
79
|
+
return "lexer" if declaration.is_a?(AST::Lexer)
|
|
80
|
+
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
41
84
|
# @rbs () -> String
|
|
42
85
|
def parse_constant_path
|
|
43
86
|
parts = [token_string(expect(:identifier))]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module Frontend
|
|
5
|
+
# An immutable machine-readable frontend error.
|
|
6
|
+
class Diagnostic
|
|
7
|
+
attr_reader :code #: String
|
|
8
|
+
attr_reader :phase #: Symbol
|
|
9
|
+
attr_reader :message #: String
|
|
10
|
+
attr_reader :location #: Location
|
|
11
|
+
attr_reader :span #: SourceSpan?
|
|
12
|
+
attr_reader :expected #: Array[String]
|
|
13
|
+
attr_reader :received #: String?
|
|
14
|
+
|
|
15
|
+
# @rbs (code: String, phase: Symbol, message: String, location: Location, ?span: SourceSpan?,
|
|
16
|
+
# ?expected: Array[String], ?received: String?, ?rendered: String?) -> void
|
|
17
|
+
def initialize(code:, phase:, message:, location:, span: nil, expected: [], received: nil, rendered: nil)
|
|
18
|
+
@code = code.dup.freeze
|
|
19
|
+
@phase = phase
|
|
20
|
+
@message = message.dup.freeze
|
|
21
|
+
@location = Location.new(
|
|
22
|
+
file: location.file.dup.freeze, line: location.line, column: location.column
|
|
23
|
+
).freeze
|
|
24
|
+
@span = span
|
|
25
|
+
@expected = expected.map { |value| value.dup.freeze }.freeze
|
|
26
|
+
@received = received&.dup&.freeze
|
|
27
|
+
@rendered = (rendered || "#{location}: #{message}").dup.freeze
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @rbs () -> String
|
|
32
|
+
def severity
|
|
33
|
+
"error"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @rbs () -> String
|
|
37
|
+
def to_s
|
|
38
|
+
@rendered
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @rbs () -> Hash[Symbol, untyped]
|
|
42
|
+
def to_h
|
|
43
|
+
{
|
|
44
|
+
code: code,
|
|
45
|
+
severity: severity,
|
|
46
|
+
phase: phase.to_s,
|
|
47
|
+
message: message,
|
|
48
|
+
location: location.to_h,
|
|
49
|
+
span: span&.to_h,
|
|
50
|
+
expected: expected,
|
|
51
|
+
received: received
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Immutable output from a diagnostic parse.
|
|
57
|
+
class ParseResult
|
|
58
|
+
attr_reader :diagnostics #: Array[Diagnostic]
|
|
59
|
+
attr_reader :ast #: AST::Root?
|
|
60
|
+
attr_reader :document #: SourceDocument?
|
|
61
|
+
|
|
62
|
+
# @rbs (diagnostics: Array[Diagnostic], ast: AST::Root?, document: SourceDocument?) -> void
|
|
63
|
+
def initialize(diagnostics:, ast:, document:)
|
|
64
|
+
@diagnostics = diagnostics.dup.freeze
|
|
65
|
+
@ast = ast
|
|
66
|
+
@document = document
|
|
67
|
+
freeze
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @rbs () -> bool
|
|
71
|
+
def success?
|
|
72
|
+
diagnostics.empty? && !ast.nil?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @rbs () -> bool
|
|
76
|
+
def partial?
|
|
77
|
+
diagnostics.any? && !ast.nil?
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module Frontend
|
|
5
|
+
# Re-runs the generated parser after suppressing only conservative source regions.
|
|
6
|
+
class DiagnosticRecovery
|
|
7
|
+
DECLARATION_STARTS = %w[
|
|
8
|
+
pragma include import token prechigh preclow options expect expect_rr start recover on_error_reduce test lexer
|
|
9
|
+
convert display type param printer
|
|
10
|
+
].freeze #: Array[String]
|
|
11
|
+
|
|
12
|
+
# @rbs @tokens: Array[Token]
|
|
13
|
+
# @rbs @mode: Symbol
|
|
14
|
+
# @rbs @max_diagnostics: Integer
|
|
15
|
+
# @rbs @suppressed: Hash[Integer, bool]
|
|
16
|
+
|
|
17
|
+
# @rbs (Array[Token] tokens, mode: Symbol, max_diagnostics: Integer) -> void
|
|
18
|
+
def initialize(tokens, mode:, max_diagnostics:)
|
|
19
|
+
@tokens = tokens
|
|
20
|
+
@mode = mode
|
|
21
|
+
@max_diagnostics = max_diagnostics
|
|
22
|
+
@diagnostics = [] #: Array[Diagnostic]
|
|
23
|
+
@suppressed = {} #: Hash[Integer, bool]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @rbs () -> [AST::Root | AST::Fragment | nil, Array[Diagnostic]]
|
|
27
|
+
def parse
|
|
28
|
+
ast = parse_until_stable
|
|
29
|
+
[ast, sorted_diagnostics]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# @rbs () -> (AST::Root | AST::Fragment | nil)
|
|
35
|
+
def parse_until_stable
|
|
36
|
+
attempts = 0
|
|
37
|
+
loop do
|
|
38
|
+
parser = GeneratedParser.new(working_tokens, mode: @mode)
|
|
39
|
+
begin
|
|
40
|
+
return parser.parse
|
|
41
|
+
rescue Ibex::Error => e
|
|
42
|
+
return if @diagnostics.length >= @max_diagnostics
|
|
43
|
+
|
|
44
|
+
diagnostic = diagnostic_from(e, parser.diagnostic_token, phase: :syntax)
|
|
45
|
+
append_diagnostic(diagnostic)
|
|
46
|
+
range = suppression_range(parser.diagnostic_token, e)
|
|
47
|
+
return unless range && suppress(range)
|
|
48
|
+
|
|
49
|
+
attempts += 1
|
|
50
|
+
return if attempts >= @tokens.length
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs () -> Array[Token]
|
|
56
|
+
def working_tokens
|
|
57
|
+
@tokens.each_with_index.filter_map { |token, index| token unless @suppressed[index] }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @rbs (Diagnostic diagnostic) -> void
|
|
61
|
+
def append_diagnostic(diagnostic)
|
|
62
|
+
key = diagnostic_key(diagnostic)
|
|
63
|
+
return if @diagnostics.any? { |existing| diagnostic_key(existing) == key }
|
|
64
|
+
|
|
65
|
+
@diagnostics << diagnostic
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @rbs (Diagnostic diagnostic) -> [String, String, Integer, Integer, String]
|
|
69
|
+
def diagnostic_key(diagnostic)
|
|
70
|
+
location = diagnostic.location
|
|
71
|
+
[diagnostic.phase.to_s, location.file, location.line, location.column, diagnostic.message]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @rbs () -> Array[Diagnostic]
|
|
75
|
+
def sorted_diagnostics
|
|
76
|
+
@diagnostics.uniq { |diagnostic| diagnostic_key(diagnostic) }
|
|
77
|
+
.sort_by { |diagnostic| diagnostic_sort_key(diagnostic) }
|
|
78
|
+
.first(@max_diagnostics)
|
|
79
|
+
.freeze
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @rbs (Diagnostic diagnostic) -> [String, Integer, Integer, String, String]
|
|
83
|
+
def diagnostic_sort_key(diagnostic)
|
|
84
|
+
location = diagnostic.location
|
|
85
|
+
[location.file, location.line, location.column, diagnostic.phase.to_s, diagnostic.code]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @rbs (Token? token, Exception error) -> Range[Integer]?
|
|
89
|
+
def suppression_range(token, error)
|
|
90
|
+
index = token_index(token) || token_index_for_message(error.message)
|
|
91
|
+
return unless index
|
|
92
|
+
|
|
93
|
+
rule_marker = rule_marker_index
|
|
94
|
+
return declaration_range(index, rule_marker) if rule_marker && index < rule_marker
|
|
95
|
+
return rule_range(index, rule_marker) if rule_marker
|
|
96
|
+
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @rbs (Token? token) -> Integer?
|
|
101
|
+
def token_index(token)
|
|
102
|
+
return unless token
|
|
103
|
+
|
|
104
|
+
@tokens.index { |candidate| candidate.equal?(token) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @rbs (String message) -> Integer?
|
|
108
|
+
def token_index_for_message(message)
|
|
109
|
+
@tokens.index { |token| message.start_with?("#{token.location}:") }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @rbs () -> Integer?
|
|
113
|
+
def rule_marker_index
|
|
114
|
+
@tokens.index { |token| token.type == :identifier && token.value == "rule" }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs (Integer failure, Integer rule_marker) -> Range[Integer]?
|
|
118
|
+
def declaration_range(failure, rule_marker)
|
|
119
|
+
starts = (0...rule_marker).select { |index| declaration_start?(index) }
|
|
120
|
+
start = starts.reverse.find { |index| index <= failure }
|
|
121
|
+
return unless start
|
|
122
|
+
|
|
123
|
+
finish = declaration_finish(start, rule_marker, starts)
|
|
124
|
+
start...finish
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @rbs (Integer index) -> bool
|
|
128
|
+
def declaration_start?(index)
|
|
129
|
+
token = @tokens[index]
|
|
130
|
+
token&.type == :identifier && DECLARATION_STARTS.include?(token.value)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @rbs (Integer start, Integer rule_marker, Array[Integer] starts) -> Integer
|
|
134
|
+
def declaration_finish(start, rule_marker, starts)
|
|
135
|
+
value = @tokens.fetch(start).value
|
|
136
|
+
return matching_declaration_end(start, rule_marker, value == "prechigh" ? "preclow" : "prechigh") if
|
|
137
|
+
%w[prechigh preclow].include?(value)
|
|
138
|
+
return matching_declaration_end(start, rule_marker, "end") if %w[convert lexer].include?(value)
|
|
139
|
+
|
|
140
|
+
starts.find { |index| index > start } || rule_marker
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @rbs (Integer start, Integer fallback, String closer) -> Integer
|
|
144
|
+
def matching_declaration_end(start, fallback, closer)
|
|
145
|
+
index = ((start + 1)...fallback).find { |candidate| @tokens[candidate]&.value == closer }
|
|
146
|
+
index ? index + 1 : fallback
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @rbs (Integer failure, Integer rule_marker) -> Range[Integer]?
|
|
150
|
+
def rule_range(failure, rule_marker)
|
|
151
|
+
grammar_end = grammar_end_index(rule_marker)
|
|
152
|
+
definitions = rule_definition_starts(rule_marker, grammar_end)
|
|
153
|
+
start = definitions.reverse.find { |index| index <= failure }
|
|
154
|
+
return unless start
|
|
155
|
+
|
|
156
|
+
finish = definitions.find { |index| index > start } || grammar_end
|
|
157
|
+
alternative_range(failure, start, finish) || (start...finish)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @rbs (Integer rule_marker) -> Integer
|
|
161
|
+
def grammar_end_index(rule_marker)
|
|
162
|
+
depth = 0
|
|
163
|
+
((rule_marker + 1)...@tokens.length).each do |index|
|
|
164
|
+
token = @tokens.fetch(index)
|
|
165
|
+
depth += 1 if token.type == :"("
|
|
166
|
+
depth -= 1 if token.type == :")" && depth.positive?
|
|
167
|
+
return index if depth.zero? && token.type == :identifier && token.value == "end"
|
|
168
|
+
end
|
|
169
|
+
@tokens.length - 1
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# @rbs (Integer rule_marker, Integer grammar_end) -> Array[Integer]
|
|
173
|
+
def rule_definition_starts(rule_marker, grammar_end)
|
|
174
|
+
starts = [] #: Array[Integer]
|
|
175
|
+
depth = 0
|
|
176
|
+
lhs_column = nil #: Integer?
|
|
177
|
+
((rule_marker + 1)...grammar_end).each do |index|
|
|
178
|
+
token = @tokens.fetch(index)
|
|
179
|
+
depth += 1 if token.type == :"("
|
|
180
|
+
depth -= 1 if token.type == :")" && depth.positive?
|
|
181
|
+
next unless depth.zero? && lhs_candidate?(index, lhs_column)
|
|
182
|
+
|
|
183
|
+
lhs_column ||= token.location.column
|
|
184
|
+
starts << index
|
|
185
|
+
end
|
|
186
|
+
starts
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @rbs (Integer index, Integer? lhs_column) -> bool
|
|
190
|
+
def lhs_candidate?(index, lhs_column)
|
|
191
|
+
token = @tokens[index]
|
|
192
|
+
following = @tokens[index + 1]
|
|
193
|
+
return false unless token&.type == :identifier && following&.type == :":"
|
|
194
|
+
|
|
195
|
+
lhs_column.nil? || token.location.column <= lhs_column
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @rbs (Integer failure, Integer start, Integer finish) -> Range[Integer]?
|
|
199
|
+
def alternative_range(failure, start, finish)
|
|
200
|
+
colon = ((start + 1)...finish).find { |index| @tokens[index]&.type == :":" }
|
|
201
|
+
return unless colon
|
|
202
|
+
|
|
203
|
+
pipes = top_level_pipes(colon + 1, finish)
|
|
204
|
+
return if pipes.empty?
|
|
205
|
+
|
|
206
|
+
previous = pipes.reverse.find { |index| index < failure }
|
|
207
|
+
following = pipes.find { |index| index >= failure }
|
|
208
|
+
alternative_slice(previous, following, colon, finish)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# @rbs (Integer? previous, Integer? following, Integer colon, Integer finish) -> Range[Integer]?
|
|
212
|
+
def alternative_slice(previous, following, colon, finish)
|
|
213
|
+
if previous
|
|
214
|
+
following ? (previous...following) : (previous...finish)
|
|
215
|
+
elsif following
|
|
216
|
+
(colon + 1)..following
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# @rbs (Integer start, Integer finish) -> Array[Integer]
|
|
221
|
+
def top_level_pipes(start, finish)
|
|
222
|
+
depth = 0
|
|
223
|
+
(start...finish).filter_map do |index|
|
|
224
|
+
token = @tokens.fetch(index)
|
|
225
|
+
depth += 1 if token.type == :"("
|
|
226
|
+
depth -= 1 if token.type == :")" && depth.positive?
|
|
227
|
+
index if depth.zero? && token.type == :|
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# @rbs (Range[Integer] range) -> bool
|
|
232
|
+
def suppress(range)
|
|
233
|
+
changed = false
|
|
234
|
+
range.each do |index|
|
|
235
|
+
next if index.negative? || index >= @tokens.length || @tokens[index]&.type == :eof
|
|
236
|
+
next if @suppressed[index]
|
|
237
|
+
|
|
238
|
+
@suppressed[index] = true
|
|
239
|
+
changed = true
|
|
240
|
+
end
|
|
241
|
+
changed
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @rbs (Exception error, Token? token, phase: Symbol) -> Diagnostic
|
|
245
|
+
def diagnostic_from(error, token, phase:)
|
|
246
|
+
matching = @tokens.find { |candidate| error.message.start_with?("#{candidate.location}:") }
|
|
247
|
+
token = matching || token || @tokens.last
|
|
248
|
+
location = token&.location || Location.new(file: "(grammar)", line: 1, column: 1)
|
|
249
|
+
prefix = "#{location}: "
|
|
250
|
+
message = error.message.delete_prefix(prefix)
|
|
251
|
+
expected, received = expected_and_received(message)
|
|
252
|
+
Diagnostic.new(code: "frontend.#{phase}_error", phase: phase, message: message,
|
|
253
|
+
location: location, span: token&.span, expected: expected,
|
|
254
|
+
received: received, rendered: error.message)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# @rbs (String message) -> [Array[String], String?]
|
|
258
|
+
def expected_and_received(message)
|
|
259
|
+
match = message.match(/\Aexpected (.+), got (.+)\z/)
|
|
260
|
+
return [[], nil] unless match
|
|
261
|
+
|
|
262
|
+
expected = match[1]
|
|
263
|
+
received = match[2]
|
|
264
|
+
[expected ? [expected] : [], received]
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|