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,236 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
module Ibex
|
|
7
|
+
module LALR
|
|
8
|
+
# Builds an LR(0) collection and propagates LALR(1) lookaheads directly
|
|
9
|
+
# over item occurrences. Canonical LR(1) states are never materialized.
|
|
10
|
+
class DirectLookaheads
|
|
11
|
+
AUGMENTED_PRODUCTION = -1 #: Integer
|
|
12
|
+
EMPTY_PRODUCTIONS = Array.new(0).freeze #: Array[IR::Production]
|
|
13
|
+
EMPTY_NODES = Array.new(0).freeze #: Array[lookahead_node]
|
|
14
|
+
private_constant :EMPTY_PRODUCTIONS, :EMPTY_NODES
|
|
15
|
+
|
|
16
|
+
# @rbs @grammar: IR::Grammar
|
|
17
|
+
# @rbs @sets: Analysis::Sets
|
|
18
|
+
# @rbs @productions_by_lhs: Hash[Integer, Array[IR::Production]]
|
|
19
|
+
# @rbs @augmented_rhs: Array[Integer]
|
|
20
|
+
# @rbs @production_rhs: Array[Array[Integer]]
|
|
21
|
+
# @rbs @augmented_item_cores: Array[item_core]
|
|
22
|
+
# @rbs @production_item_cores: Array[Array[item_core]]
|
|
23
|
+
# @rbs @terminal_ids: Array[Integer]
|
|
24
|
+
# @rbs @terminal_masks: Array[Integer]
|
|
25
|
+
|
|
26
|
+
# @rbs (IR::Grammar grammar, Analysis::Sets sets) -> void
|
|
27
|
+
def initialize(grammar, sets)
|
|
28
|
+
@grammar = grammar
|
|
29
|
+
@sets = sets
|
|
30
|
+
@productions_by_lhs = grammar.productions.group_by(&:lhs)
|
|
31
|
+
start = grammar.symbol(grammar.start) || raise(Ibex::Error, "missing start symbol")
|
|
32
|
+
@augmented_rhs = [start.id].freeze
|
|
33
|
+
@production_rhs = grammar.productions.map(&:rhs).freeze
|
|
34
|
+
@augmented_item_cores = item_cores_for(AUGMENTED_PRODUCTION, @augmented_rhs.length)
|
|
35
|
+
@production_item_cores = grammar.productions.map do |production|
|
|
36
|
+
item_cores_for(production.id, production.rhs.length)
|
|
37
|
+
end.freeze
|
|
38
|
+
@terminal_ids = grammar.terminals.map(&:id).freeze
|
|
39
|
+
@terminal_masks = @terminal_ids.map { |id| 1 << id }.freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @rbs () -> [Array[packed_items], transitions]
|
|
43
|
+
def build
|
|
44
|
+
states, transitions = lr0_collection
|
|
45
|
+
lookaheads = empty_lookaheads(states)
|
|
46
|
+
propagation = propagation_graph(states, transitions, lookaheads)
|
|
47
|
+
lookaheads.fetch(0).fetch(item_core(AUGMENTED_PRODUCTION, 0)) << 0
|
|
48
|
+
propagate(lookaheads, propagation)
|
|
49
|
+
[lookaheads, transitions]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# @rbs () -> [Array[core_set], transitions]
|
|
55
|
+
def lr0_collection
|
|
56
|
+
seed = Set[item_core(AUGMENTED_PRODUCTION, 0)] #: core_set
|
|
57
|
+
states = [closure(seed)]
|
|
58
|
+
transitions = [] #: transitions
|
|
59
|
+
indexes = { item_key(states.first) => 0 }
|
|
60
|
+
cursor = 0
|
|
61
|
+
while cursor < states.length
|
|
62
|
+
transitions[cursor] = {}
|
|
63
|
+
kernels = shifted_kernels(states.fetch(cursor))
|
|
64
|
+
kernels.keys.sort.each do |symbol_id|
|
|
65
|
+
target = closure(kernels.fetch(symbol_id))
|
|
66
|
+
key = item_key(target)
|
|
67
|
+
target_id = indexes[key] ||= begin
|
|
68
|
+
states << target
|
|
69
|
+
states.length - 1
|
|
70
|
+
end
|
|
71
|
+
transitions.fetch(cursor)[symbol_id] = target_id
|
|
72
|
+
end
|
|
73
|
+
cursor += 1
|
|
74
|
+
end
|
|
75
|
+
[states, transitions]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @rbs (core_set seed) -> core_set
|
|
79
|
+
def closure(seed)
|
|
80
|
+
items = seed.dup
|
|
81
|
+
queue = seed.to_a
|
|
82
|
+
cursor = 0
|
|
83
|
+
while cursor < queue.length
|
|
84
|
+
production_id, dot = queue.fetch(cursor)
|
|
85
|
+
cursor += 1
|
|
86
|
+
symbol = @grammar.symbol_by_id(rhs_for(production_id)[dot])
|
|
87
|
+
next unless symbol&.nonterminal?
|
|
88
|
+
|
|
89
|
+
@productions_by_lhs.fetch(symbol.id, EMPTY_PRODUCTIONS).each do |production|
|
|
90
|
+
item = item_core(production.id, 0)
|
|
91
|
+
queue << item if items.add?(item)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
items
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @rbs (core_set items) -> Hash[Integer, core_set]
|
|
98
|
+
def shifted_kernels(items)
|
|
99
|
+
kernels = {} #: Hash[Integer, core_set]
|
|
100
|
+
items.each do |production_id, dot|
|
|
101
|
+
symbol_id = rhs_for(production_id)[dot]
|
|
102
|
+
next unless symbol_id
|
|
103
|
+
|
|
104
|
+
(kernels[symbol_id] ||= Set.new) << item_core(production_id, dot + 1)
|
|
105
|
+
end
|
|
106
|
+
kernels
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs (Array[core_set] states) -> Array[packed_items]
|
|
110
|
+
def empty_lookaheads(states)
|
|
111
|
+
states.map do |items|
|
|
112
|
+
items.to_h { |item| [item, Set.new] } #: packed_items
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @rbs (Array[core_set] states, transitions transitions, Array[packed_items] lookaheads) ->
|
|
117
|
+
# Hash[lookahead_node, Array[lookahead_node]]
|
|
118
|
+
def propagation_graph(states, transitions, lookaheads)
|
|
119
|
+
edges = Hash.new { |hash, key| hash[key] = [] } #: Hash[lookahead_node, Array[lookahead_node]]
|
|
120
|
+
states.each_with_index do |items, state_id|
|
|
121
|
+
items.each do |production_id, dot|
|
|
122
|
+
add_transition_edge(edges, transitions, state_id, production_id, dot)
|
|
123
|
+
add_closure_relations(edges, lookaheads, state_id, production_id, dot)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
edges.each_value(&:uniq!)
|
|
127
|
+
edges
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @rbs (Hash[lookahead_node, Array[lookahead_node]] edges, transitions transitions,
|
|
131
|
+
# Integer state_id, Integer production_id, Integer dot) -> void
|
|
132
|
+
def add_transition_edge(edges, transitions, state_id, production_id, dot)
|
|
133
|
+
symbol_id = rhs_for(production_id)[dot]
|
|
134
|
+
return unless symbol_id
|
|
135
|
+
|
|
136
|
+
target_state = transitions.fetch(state_id).fetch(symbol_id)
|
|
137
|
+
source = [state_id, production_id, dot] #: lookahead_node
|
|
138
|
+
target = [target_state, production_id, dot + 1] #: lookahead_node
|
|
139
|
+
edges[source] << target
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @rbs (Hash[lookahead_node, Array[lookahead_node]] edges, Array[packed_items] lookaheads,
|
|
143
|
+
# Integer state_id, Integer production_id, Integer dot) -> void
|
|
144
|
+
def add_closure_relations(edges, lookaheads, state_id, production_id, dot)
|
|
145
|
+
rhs = rhs_for(production_id)
|
|
146
|
+
symbol = @grammar.symbol_by_id(rhs[dot])
|
|
147
|
+
return unless symbol&.nonterminal?
|
|
148
|
+
|
|
149
|
+
suffix = rhs.drop(dot + 1)
|
|
150
|
+
spontaneous = terminal_ids(@sets.first_of_sequence(suffix))
|
|
151
|
+
source = [state_id, production_id, dot] #: lookahead_node
|
|
152
|
+
@productions_by_lhs.fetch(symbol.id, EMPTY_PRODUCTIONS).each do |production|
|
|
153
|
+
target_item = item_core(production.id, 0)
|
|
154
|
+
lookaheads.fetch(state_id).fetch(target_item).merge(spontaneous)
|
|
155
|
+
if @sets.sequence_nullable?(suffix)
|
|
156
|
+
target = [state_id, production.id, 0] #: lookahead_node
|
|
157
|
+
edges[source] << target
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @rbs (Array[packed_items] lookaheads, Hash[lookahead_node, Array[lookahead_node]] edges) -> void
|
|
163
|
+
def propagate(lookaheads, edges)
|
|
164
|
+
queue = lookaheads.each_with_index.flat_map do |items, state_id|
|
|
165
|
+
items.filter_map do |(production_id, dot), tokens|
|
|
166
|
+
next if tokens.empty?
|
|
167
|
+
|
|
168
|
+
[state_id, production_id, dot] #: lookahead_node
|
|
169
|
+
end
|
|
170
|
+
end #: Array[lookahead_node]
|
|
171
|
+
queued = queue.to_h { |node| [node, true] } #: Hash[lookahead_node, bool]
|
|
172
|
+
cursor = 0
|
|
173
|
+
while cursor < queue.length
|
|
174
|
+
source = queue.fetch(cursor)
|
|
175
|
+
cursor += 1
|
|
176
|
+
queued.delete(source)
|
|
177
|
+
source_set = node_set(lookaheads, source)
|
|
178
|
+
edges.fetch(source, EMPTY_NODES).each do |target|
|
|
179
|
+
target_set = node_set(lookaheads, target)
|
|
180
|
+
previous_size = target_set.size
|
|
181
|
+
target_set.merge(source_set)
|
|
182
|
+
next if target_set.size == previous_size || queued[target]
|
|
183
|
+
|
|
184
|
+
queue << target
|
|
185
|
+
queued[target] = true
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @rbs (Array[packed_items] lookaheads, lookahead_node node) -> Set[Integer]
|
|
191
|
+
def node_set(lookaheads, node)
|
|
192
|
+
state_id, production_id, dot = node
|
|
193
|
+
lookaheads.fetch(state_id).fetch(item_core(production_id, dot))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @rbs (Integer bits) -> Array[Integer]
|
|
197
|
+
def terminal_ids(bits)
|
|
198
|
+
selected = [] #: Array[Integer]
|
|
199
|
+
index = 0
|
|
200
|
+
while index < @terminal_ids.length
|
|
201
|
+
selected << @terminal_ids[index] if bits.anybits?(@terminal_masks[index])
|
|
202
|
+
index += 1
|
|
203
|
+
end
|
|
204
|
+
selected
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @rbs (Integer production_id) -> Array[Integer]
|
|
208
|
+
def rhs_for(production_id)
|
|
209
|
+
return @augmented_rhs if production_id == AUGMENTED_PRODUCTION
|
|
210
|
+
|
|
211
|
+
@production_rhs.fetch(production_id)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @rbs (Integer production_id, Integer rhs_length) -> Array[item_core]
|
|
215
|
+
def item_cores_for(production_id, rhs_length)
|
|
216
|
+
Array.new(rhs_length + 1) do |dot|
|
|
217
|
+
[production_id, dot].freeze #: item_core
|
|
218
|
+
end.freeze
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @rbs (Integer production_id, Integer dot) -> item_core
|
|
222
|
+
def item_core(production_id, dot)
|
|
223
|
+
raise IndexError, "invalid item dot: #{dot}" if dot.negative?
|
|
224
|
+
return @augmented_item_cores.fetch(dot) if production_id == AUGMENTED_PRODUCTION
|
|
225
|
+
raise IndexError, "invalid production id: #{production_id}" if production_id.negative?
|
|
226
|
+
|
|
227
|
+
@production_item_cores.fetch(production_id).fetch(dot)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# @rbs (core_set items) -> Array[item_core]
|
|
231
|
+
def item_key(items)
|
|
232
|
+
items.to_a.sort
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
module Ibex
|
|
7
|
+
module LALR
|
|
8
|
+
# Deterministically merges compatible canonical LR(1) states and splits
|
|
9
|
+
# partitions until their outgoing transitions are congruent.
|
|
10
|
+
class IELRPartition
|
|
11
|
+
AUGMENTED_PRODUCTION = -1 #: Integer
|
|
12
|
+
|
|
13
|
+
# @rbs @grammar: IR::Grammar
|
|
14
|
+
# @rbs @states: Array[item_set]
|
|
15
|
+
# @rbs @transitions: transitions
|
|
16
|
+
# @rbs @contributions: Array[Hash[Integer, Set[Array[untyped]]]]
|
|
17
|
+
|
|
18
|
+
# @rbs (IR::Grammar grammar, Array[item_set] states, transitions transitions) -> void
|
|
19
|
+
def initialize(grammar, states, transitions)
|
|
20
|
+
@grammar = grammar
|
|
21
|
+
@states = states
|
|
22
|
+
@transitions = transitions
|
|
23
|
+
@contributions = Array.new(states.length) { |state_id| action_contributions(state_id) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @rbs () -> [Array[packed_items], transitions]
|
|
27
|
+
def build
|
|
28
|
+
partitions = refine_transitions(initial_partitions)
|
|
29
|
+
indexes = partition_indexes(partitions)
|
|
30
|
+
items = partitions.map { |members| merge_items(members) }
|
|
31
|
+
transitions = partitions.map do |members|
|
|
32
|
+
@transitions.fetch(members.first).to_h do |symbol_id, target|
|
|
33
|
+
[symbol_id, indexes.fetch(target)]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
[items, transitions]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# @rbs () -> Array[state_partition]
|
|
42
|
+
def initial_partitions
|
|
43
|
+
cores = {} #: Hash[Array[item_core], Array[Integer]]
|
|
44
|
+
@states.each_with_index do |items, state_id|
|
|
45
|
+
cores[core_key(items)] ||= []
|
|
46
|
+
cores.fetch(core_key(items)) << state_id
|
|
47
|
+
end
|
|
48
|
+
cores.values.flat_map { |members| compatible_partitions(members) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @rbs (Array[Integer] members) -> Array[state_partition]
|
|
52
|
+
def compatible_partitions(members)
|
|
53
|
+
partitions = [] #: Array[state_partition]
|
|
54
|
+
members.each do |state_id|
|
|
55
|
+
partition = partitions.find { |candidate| compatible?(candidate + [state_id]) }
|
|
56
|
+
partition ? partition << state_id : partitions << [state_id]
|
|
57
|
+
end
|
|
58
|
+
partitions
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# A canonical member may gain an action only where it previously had no
|
|
62
|
+
# action. Every nonempty member cell must equal the merged cell.
|
|
63
|
+
# @rbs (state_partition members) -> bool
|
|
64
|
+
def compatible?(members)
|
|
65
|
+
merged = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[Array[untyped]]]
|
|
66
|
+
members.each do |state_id|
|
|
67
|
+
@contributions.fetch(state_id).each { |token_id, actions| merged[token_id].merge(actions) }
|
|
68
|
+
end
|
|
69
|
+
merged.all? do |token_id, actions|
|
|
70
|
+
members.all? do |state_id|
|
|
71
|
+
member_actions = @contributions.fetch(state_id)[token_id]
|
|
72
|
+
member_actions.nil? || member_actions.empty? || member_actions == actions
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @rbs (Array[state_partition] partitions) -> Array[state_partition]
|
|
78
|
+
def refine_transitions(partitions)
|
|
79
|
+
loop do
|
|
80
|
+
indexes = partition_indexes(partitions)
|
|
81
|
+
refined = partitions.flat_map do |members|
|
|
82
|
+
members.group_by { |state_id| transition_signature(state_id, indexes) }.values
|
|
83
|
+
end
|
|
84
|
+
return partitions if refined == partitions
|
|
85
|
+
|
|
86
|
+
partitions = refined
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @rbs (Integer state_id, Hash[Integer, Integer] indexes) -> Array[[Integer, Integer]]
|
|
91
|
+
def transition_signature(state_id, indexes)
|
|
92
|
+
@transitions.fetch(state_id).map do |symbol_id, target|
|
|
93
|
+
[symbol_id, indexes.fetch(target)] #: [Integer, Integer]
|
|
94
|
+
end.sort
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @rbs (Array[state_partition] partitions) -> Hash[Integer, Integer]
|
|
98
|
+
def partition_indexes(partitions)
|
|
99
|
+
indexes = {} #: Hash[Integer, Integer]
|
|
100
|
+
partitions.each_with_index do |members, partition_id|
|
|
101
|
+
members.each { |state_id| indexes[state_id] = partition_id }
|
|
102
|
+
end
|
|
103
|
+
indexes
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @rbs (state_partition members) -> packed_items
|
|
107
|
+
def merge_items(members)
|
|
108
|
+
merged = Hash.new { |hash, key| hash[key] = Set.new } #: packed_items
|
|
109
|
+
members.each do |state_id|
|
|
110
|
+
@states.fetch(state_id).each do |production_id, dot, lookahead|
|
|
111
|
+
merged[[production_id, dot]] << lookahead
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
merged
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs (Integer state_id) -> Hash[Integer, Set[Array[untyped]]]
|
|
118
|
+
def action_contributions(state_id)
|
|
119
|
+
actions = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[Array[untyped]]]
|
|
120
|
+
@transitions.fetch(state_id).each_key do |symbol_id|
|
|
121
|
+
symbol = @grammar.symbol_by_id(symbol_id)
|
|
122
|
+
actions[symbol_id] << [:shift] if symbol&.terminal?
|
|
123
|
+
end
|
|
124
|
+
@states.fetch(state_id).each do |production_id, dot, lookahead|
|
|
125
|
+
next unless dot == rhs_for(production_id).length
|
|
126
|
+
|
|
127
|
+
action = production_id.negative? ? [:accept] : [:reduce, production_id]
|
|
128
|
+
actions[lookahead] << action
|
|
129
|
+
end
|
|
130
|
+
actions
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @rbs (Integer production_id) -> Array[Integer]
|
|
134
|
+
def rhs_for(production_id)
|
|
135
|
+
if production_id.negative?
|
|
136
|
+
name = @grammar.starts.fetch(-production_id - 1)
|
|
137
|
+
start = @grammar.symbol(name) || raise(Ibex::Error, "missing start symbol #{name}")
|
|
138
|
+
return [start.id]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
@grammar.productions.fetch(production_id).rhs
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @rbs (item_set items) -> Array[item_core]
|
|
145
|
+
def core_key(items)
|
|
146
|
+
items.map do |production_id, dot, _lookahead|
|
|
147
|
+
[production_id, dot] #: item_core
|
|
148
|
+
end.uniq.sort
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module LALR
|
|
5
|
+
# Delays syntax-error detection by filling only otherwise erroneous table
|
|
6
|
+
# cells with one uniquely highest-priority declared reduction.
|
|
7
|
+
module OnErrorReductions
|
|
8
|
+
# @rbs (IR::Grammar grammar, Array[IR::AutomatonState] states) -> Array[IR::AutomatonState]
|
|
9
|
+
def apply(grammar, states)
|
|
10
|
+
priorities = reduction_priorities(grammar)
|
|
11
|
+
return states if priorities.empty?
|
|
12
|
+
|
|
13
|
+
terminal_ids = grammar.terminals.reject { |terminal| terminal.name == "error" }.map(&:id)
|
|
14
|
+
states.map { |state| apply_state(grammar, state, priorities, terminal_ids) }
|
|
15
|
+
end
|
|
16
|
+
module_function :apply
|
|
17
|
+
|
|
18
|
+
# @rbs (IR::Grammar grammar) -> Hash[Integer, Integer]
|
|
19
|
+
def reduction_priorities(grammar)
|
|
20
|
+
priorities = {} #: Hash[Integer, Integer]
|
|
21
|
+
grammar.recovery[:on_error_reduce].each_with_index do |names, priority|
|
|
22
|
+
names.each do |name|
|
|
23
|
+
symbol = grammar.symbol(name) || raise(Ibex::Error, "missing on-error reduction symbol #{name}")
|
|
24
|
+
priorities[symbol.id] = priority
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
priorities
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs (IR::Grammar grammar, IR::AutomatonState state, Hash[Integer, Integer] priorities,
|
|
31
|
+
# Array[Integer] terminal_ids) -> IR::AutomatonState
|
|
32
|
+
def apply_state(grammar, state, priorities, terminal_ids)
|
|
33
|
+
production_id = selected_production(grammar, state, priorities)
|
|
34
|
+
return state unless production_id
|
|
35
|
+
|
|
36
|
+
reduction = { type: :reduce, production: production_id } #: IR::reduce_action
|
|
37
|
+
actions = state.actions.dup
|
|
38
|
+
terminal_ids.each do |token_id|
|
|
39
|
+
action = actions[token_id]
|
|
40
|
+
actions[token_id] = reduction if action.nil? || action[:type] == :error
|
|
41
|
+
end
|
|
42
|
+
IR::AutomatonState.new(
|
|
43
|
+
id: state.id, items: state.items, transitions: state.transitions,
|
|
44
|
+
actions: actions, gotos: state.gotos, default_action: state.default_action,
|
|
45
|
+
conflicts: state.conflicts
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @rbs (IR::Grammar grammar, IR::AutomatonState state, Hash[Integer, Integer] priorities) -> Integer?
|
|
50
|
+
def selected_production(grammar, state, priorities)
|
|
51
|
+
candidates = state.items.filter_map do |item|
|
|
52
|
+
next if item.production.negative?
|
|
53
|
+
|
|
54
|
+
production = grammar.productions.fetch(item.production)
|
|
55
|
+
next unless item.dot == production.rhs.length
|
|
56
|
+
|
|
57
|
+
priority = priorities[production.lhs]
|
|
58
|
+
[priority, production.id] if priority
|
|
59
|
+
end.uniq
|
|
60
|
+
highest = candidates.filter_map(&:first).max
|
|
61
|
+
return unless highest
|
|
62
|
+
|
|
63
|
+
productions = candidates.filter_map { |priority, production| production if priority == highest }.uniq
|
|
64
|
+
productions.one? ? productions.fetch(0) : nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module_function :reduction_priorities, :apply_state, :selected_production
|
|
68
|
+
|
|
69
|
+
class << self
|
|
70
|
+
private :reduction_priorities, :apply_state, :selected_production
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/ibex/lalr.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "lalr/conflict"
|
|
4
|
+
require_relative "lalr/on_error_reductions"
|
|
4
5
|
require_relative "lalr/default_reductions"
|
|
6
|
+
require_relative "lalr/build_metrics"
|
|
7
|
+
require_relative "lalr/direct_lookaheads"
|
|
8
|
+
require_relative "lalr/ielr_partition"
|
|
5
9
|
require_relative "lalr/builder"
|
|
6
10
|
require_relative "lalr/conflict_search_limits"
|
|
7
11
|
require_relative "lalr/conflict_search"
|
|
@@ -12,9 +16,12 @@ module Ibex
|
|
|
12
16
|
# @rbs!
|
|
13
17
|
# type lr_item = [Integer, Integer, Integer]
|
|
14
18
|
# type item_core = [Integer, Integer]
|
|
19
|
+
# type core_set = Set[item_core]
|
|
15
20
|
# type item_set = Set[lr_item]
|
|
16
21
|
# type packed_items = Hash[item_core, Set[Integer]]
|
|
17
22
|
# type transitions = Array[Hash[Integer, Integer]]
|
|
23
|
+
# type lookahead_node = [Integer, Integer, Integer]
|
|
24
|
+
# type state_partition = Array[Integer]
|
|
18
25
|
# type derivation_node = Hash[Symbol, untyped]
|
|
19
26
|
# type search_status = :conflict | :shifted | :accepted
|
|
20
27
|
# type search_entry = [search_status, ConflictSearch::Configuration]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Ibex
|
|
5
|
+
# Immutable source range suitable for lexer tokens and parser diagnostics.
|
|
6
|
+
#
|
|
7
|
+
# Coordinates are one-based. Byte offsets are optional, zero-based, and
|
|
8
|
+
# half-open. Keeping both forms lets Unicode-aware lexers report human
|
|
9
|
+
# columns without losing an exact source slice.
|
|
10
|
+
class Location
|
|
11
|
+
attr_reader :file #: String?
|
|
12
|
+
attr_reader :line #: Integer
|
|
13
|
+
attr_reader :column #: Integer
|
|
14
|
+
attr_reader :end_line #: Integer
|
|
15
|
+
attr_reader :end_column #: Integer
|
|
16
|
+
attr_reader :start_byte #: Integer?
|
|
17
|
+
attr_reader :end_byte #: Integer?
|
|
18
|
+
attr_reader :source_line #: String?
|
|
19
|
+
|
|
20
|
+
# @rbs (?file: String?, line: Integer, column: Integer, ?end_line: Integer?,
|
|
21
|
+
# ?end_column: Integer?, ?start_byte: Integer?, ?end_byte: Integer?, ?source_line: String?) -> void
|
|
22
|
+
def initialize(line:, column:, file: nil, end_line: nil, end_column: nil,
|
|
23
|
+
start_byte: nil, end_byte: nil, source_line: nil)
|
|
24
|
+
@file = file&.dup&.freeze
|
|
25
|
+
@line = positive_coordinate(:line, line)
|
|
26
|
+
@column = positive_coordinate(:column, column)
|
|
27
|
+
@end_line = positive_coordinate(:end_line, end_line || line)
|
|
28
|
+
@end_column = positive_coordinate(:end_column, end_column || column)
|
|
29
|
+
@start_byte = optional_offset(:start_byte, start_byte)
|
|
30
|
+
@end_byte = optional_offset(:end_byte, end_byte)
|
|
31
|
+
@source_line = source_line&.dup&.freeze
|
|
32
|
+
validate_order!
|
|
33
|
+
freeze
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Return the smallest range covering both locations.
|
|
37
|
+
# @rbs (Location other) -> Location
|
|
38
|
+
def join(other)
|
|
39
|
+
raise ArgumentError, "location files differ" unless file == other.file
|
|
40
|
+
|
|
41
|
+
first = before_or_equal?(self, other) ? self : other
|
|
42
|
+
ending = before_or_equal_end?(self, other) ? other : self
|
|
43
|
+
self.class.new(
|
|
44
|
+
file: file, line: first.line, column: first.column,
|
|
45
|
+
end_line: ending.end_line, end_column: ending.end_column,
|
|
46
|
+
start_byte: joined_start_byte(other), end_byte: joined_end_byte(other),
|
|
47
|
+
source_line: first.source_line
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Fold a nonempty collection into one covering range.
|
|
52
|
+
# @rbs (Enumerable[Location] locations) -> Location
|
|
53
|
+
def self.join(locations)
|
|
54
|
+
values = locations.to_a
|
|
55
|
+
first = values.shift || raise(ArgumentError, "locations must not be empty")
|
|
56
|
+
values.reduce(first) { |combined, location| combined.join(location) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @rbs () -> bool
|
|
60
|
+
def empty?
|
|
61
|
+
@line == @end_line && @column == @end_column &&
|
|
62
|
+
(@start_byte.nil? || @end_byte.nil? || @start_byte == @end_byte)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @rbs () -> Hash[Symbol, untyped]
|
|
66
|
+
def to_h
|
|
67
|
+
{
|
|
68
|
+
file: @file, line: @line, column: @column,
|
|
69
|
+
end_line: @end_line, end_column: @end_column,
|
|
70
|
+
start_byte: @start_byte, end_byte: @end_byte
|
|
71
|
+
}.compact
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# @rbs (Symbol name, untyped value) -> Integer
|
|
77
|
+
def positive_coordinate(name, value)
|
|
78
|
+
return value if value.is_a?(Integer) && value.positive?
|
|
79
|
+
|
|
80
|
+
raise ArgumentError, "#{name} must be a positive Integer"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @rbs (Symbol name, untyped value) -> Integer?
|
|
84
|
+
def optional_offset(name, value)
|
|
85
|
+
return if value.nil?
|
|
86
|
+
return value if value.is_a?(Integer) && value >= 0
|
|
87
|
+
|
|
88
|
+
raise ArgumentError, "#{name} must be a nonnegative Integer or nil"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @rbs () -> void
|
|
92
|
+
def validate_order!
|
|
93
|
+
if @end_line < @line || (@end_line == @line && @end_column < @column)
|
|
94
|
+
raise ArgumentError, "end position precedes start position"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
start_byte = @start_byte
|
|
98
|
+
end_byte = @end_byte
|
|
99
|
+
return unless start_byte && end_byte && end_byte < start_byte
|
|
100
|
+
|
|
101
|
+
raise ArgumentError, "end_byte precedes start_byte"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @rbs (Location left, Location right) -> bool
|
|
105
|
+
def before_or_equal?(left, right)
|
|
106
|
+
left.line < right.line || (left.line == right.line && left.column <= right.column)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs (Location left, Location right) -> bool
|
|
110
|
+
def before_or_equal_end?(left, right)
|
|
111
|
+
left.end_line < right.end_line ||
|
|
112
|
+
(left.end_line == right.end_line && left.end_column <= right.end_column)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @rbs (Location other) -> Integer?
|
|
116
|
+
def joined_start_byte(other)
|
|
117
|
+
return unless @start_byte && other.start_byte
|
|
118
|
+
|
|
119
|
+
[@start_byte, other.start_byte].min
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @rbs (Location other) -> Integer?
|
|
123
|
+
def joined_end_byte(other)
|
|
124
|
+
return unless @end_byte && other.end_byte
|
|
125
|
+
|
|
126
|
+
[@end_byte, other.end_byte].max
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module LSP
|
|
5
|
+
# Applies full-text LSP document notifications to the overlay-backed store.
|
|
6
|
+
module DocumentHandlers
|
|
7
|
+
include RequestSupport
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# @rbs (untyped raw_params) -> nil
|
|
12
|
+
def did_open(raw_params)
|
|
13
|
+
require_running!
|
|
14
|
+
params = params_hash(raw_params)
|
|
15
|
+
document = hash_member(params, "textDocument")
|
|
16
|
+
publications = store.open(
|
|
17
|
+
string_member(document, "uri"), integer_member(document, "version"), string_member(document, "text")
|
|
18
|
+
)
|
|
19
|
+
publish(publications)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs (untyped raw_params) -> nil
|
|
24
|
+
def did_change(raw_params)
|
|
25
|
+
require_running!
|
|
26
|
+
params = params_hash(raw_params)
|
|
27
|
+
document = hash_member(params, "textDocument")
|
|
28
|
+
changes = params["contentChanges"]
|
|
29
|
+
unless changes.is_a?(Array) && changes.one? && changes.first.is_a?(Hash) &&
|
|
30
|
+
!changes.first.key?("range") && !changes.first.key?("rangeLength")
|
|
31
|
+
raise ProtocolError.new("full sync requires exactly one range-free content change", code: -32_602)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
source = string_member(changes.fetch(0), "text")
|
|
35
|
+
publications = store.change(
|
|
36
|
+
string_member(document, "uri"), integer_member(document, "version"), source
|
|
37
|
+
)
|
|
38
|
+
publish(publications)
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @rbs (untyped raw_params) -> nil
|
|
43
|
+
def did_save(raw_params)
|
|
44
|
+
require_running!
|
|
45
|
+
params = params_hash(raw_params)
|
|
46
|
+
document = hash_member(params, "textDocument")
|
|
47
|
+
source = params["text"]
|
|
48
|
+
unless source.nil? || source.is_a?(String)
|
|
49
|
+
raise ProtocolError.new("saved document text must be a string", code: -32_602)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
publish(store.save(string_member(document, "uri"), source: source))
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @rbs (untyped raw_params) -> nil
|
|
57
|
+
def did_close(raw_params)
|
|
58
|
+
require_running!
|
|
59
|
+
params = params_hash(raw_params)
|
|
60
|
+
document = hash_member(params, "textDocument")
|
|
61
|
+
publish(store.close(string_member(document, "uri")))
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|