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,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibex
|
|
4
|
+
module LSP
|
|
5
|
+
# Parses workspace sources, discovers safe include closures, and renders diagnostics.
|
|
6
|
+
class WorkspaceAnalyzer
|
|
7
|
+
GLOB_CHARACTERS = /[*?\[\]{}]/ #: Regexp
|
|
8
|
+
WINDOWS_ABSOLUTE = %r{\A(?:[A-Za-z]:[\\/]|\\\\)} #: Regexp
|
|
9
|
+
|
|
10
|
+
# @rbs!
|
|
11
|
+
# type lsp_diagnostic = Hash[String, untyped]
|
|
12
|
+
# type analyzed_document = {
|
|
13
|
+
# source: String,
|
|
14
|
+
# document: Frontend::SourceDocument?,
|
|
15
|
+
# diagnostics: Array[lsp_diagnostic]
|
|
16
|
+
# }
|
|
17
|
+
|
|
18
|
+
# @rbs (Workspace workspace, Frontend::SourceLoader loader) -> void
|
|
19
|
+
def initialize(workspace, loader)
|
|
20
|
+
@workspace = workspace
|
|
21
|
+
@loader = loader
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs (String path) -> analyzed_document
|
|
25
|
+
def analyze(path)
|
|
26
|
+
source = @loader.read(path)
|
|
27
|
+
return analyze_fragment(path, source) if fragment_source?(source, path)
|
|
28
|
+
|
|
29
|
+
analyze_root(path, source)
|
|
30
|
+
rescue Ibex::Error, SystemCallError => e
|
|
31
|
+
source ||= readable_source(path)
|
|
32
|
+
{ source: source, document: nil, diagnostics: [diagnostic(e.message, source, path)] }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @rbs (String root) -> [Array[String], Hash[String, analyzed_document]]
|
|
36
|
+
def closure(root)
|
|
37
|
+
files = [] #: Array[String]
|
|
38
|
+
analyzed = {} #: Hash[String, analyzed_document]
|
|
39
|
+
visiting = [root]
|
|
40
|
+
until visiting.empty?
|
|
41
|
+
path = visiting.pop
|
|
42
|
+
next unless path
|
|
43
|
+
next if analyzed.key?(path)
|
|
44
|
+
|
|
45
|
+
current = analyze(path)
|
|
46
|
+
analyzed[path] = current
|
|
47
|
+
files << path
|
|
48
|
+
dependencies(current.fetch(:document)).reverse_each do |target|
|
|
49
|
+
visiting << target unless analyzed.key?(target)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
[files, analyzed]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs (String root) -> [Frontend::Resolution?, Array[lsp_diagnostic]]
|
|
56
|
+
def resolve(root)
|
|
57
|
+
resolution = Frontend::Resolver.new(root, mode: :extended, loader: @loader).resolve
|
|
58
|
+
[resolution, []]
|
|
59
|
+
rescue Ibex::Error, SystemCallError => e
|
|
60
|
+
[nil, [diagnostic(e.message, source_for_error(e.message, root), file_for_error(e.message, root))]]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# @rbs (String path, String source) -> analyzed_document
|
|
66
|
+
def analyze_root(path, source)
|
|
67
|
+
result = Frontend::Parser.new(source, file: path, mode: :extended)
|
|
68
|
+
.parse_with_diagnostics(max_diagnostics: 20)
|
|
69
|
+
diagnostics = result.diagnostics.map { |entry| frontend_diagnostic(entry, source) }
|
|
70
|
+
{ source: source, document: result.document, diagnostics: diagnostics }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @rbs (String path, String source) -> analyzed_document
|
|
74
|
+
def analyze_fragment(path, source)
|
|
75
|
+
document = Frontend::Parser.new(source, file: path, mode: :extended).parse_source_document
|
|
76
|
+
{ source: source, document: document, diagnostics: [] }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @rbs (String source, String path) -> bool
|
|
80
|
+
def fragment_source?(source, path)
|
|
81
|
+
document, = Frontend::Lexer.new(source, file: path).tokenize_document_recovering(max_diagnostics: 1)
|
|
82
|
+
first = document.tokens.find { |token| token.type != :eof }
|
|
83
|
+
first&.value == "fragment"
|
|
84
|
+
rescue Ibex::Error
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @rbs (Frontend::SourceDocument? document) -> Array[String]
|
|
89
|
+
def dependencies(document)
|
|
90
|
+
ast = document&.ast
|
|
91
|
+
return [] unless ast
|
|
92
|
+
|
|
93
|
+
ast.declarations.filter_map do |declaration|
|
|
94
|
+
next unless declaration.is_a?(Frontend::AST::Include)
|
|
95
|
+
|
|
96
|
+
canonical_include(declaration)
|
|
97
|
+
rescue Ibex::Error, SystemCallError
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @rbs (Frontend::AST::Include include_node) -> String
|
|
103
|
+
def canonical_include(include_node)
|
|
104
|
+
path = include_node.path
|
|
105
|
+
return invalid_include(include_node) if path.empty? || path.include?("\0")
|
|
106
|
+
return invalid_include(include_node) if path.start_with?("/") || path.match?(WINDOWS_ABSOLUTE)
|
|
107
|
+
return invalid_include(include_node) if path.split(%r{[\\/]}).include?("..")
|
|
108
|
+
return invalid_include(include_node) if path.match?(GLOB_CHARACTERS)
|
|
109
|
+
|
|
110
|
+
candidate = File.expand_path(path, File.dirname(include_node.loc.file))
|
|
111
|
+
canonical = @loader.canonical_path(candidate, allow_missing: true)
|
|
112
|
+
return canonical if @workspace.root_for(canonical)
|
|
113
|
+
|
|
114
|
+
invalid_include(include_node)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs (Frontend::AST::Include include_node) -> bot
|
|
118
|
+
def invalid_include(include_node)
|
|
119
|
+
raise Ibex::Error, "#{include_node.loc}: unsafe include path"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @rbs (String message, String source, String fallback_path) -> lsp_diagnostic
|
|
123
|
+
def diagnostic(message, source, fallback_path)
|
|
124
|
+
file, line, column, text = diagnostic_parts(message, fallback_path)
|
|
125
|
+
{
|
|
126
|
+
"range" => diagnostic_range(source, line, column),
|
|
127
|
+
"severity" => 1,
|
|
128
|
+
"code" => "ibex.frontend",
|
|
129
|
+
"source" => "ibex",
|
|
130
|
+
"message" => text,
|
|
131
|
+
"data" => { "file" => file }
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @rbs (Frontend::Diagnostic diagnostic, String source) -> lsp_diagnostic
|
|
136
|
+
def frontend_diagnostic(diagnostic, source)
|
|
137
|
+
range = if diagnostic.span
|
|
138
|
+
PositionCodec.new(source).range(diagnostic.span)
|
|
139
|
+
else
|
|
140
|
+
diagnostic_range(source, diagnostic.location.line, diagnostic.location.column)
|
|
141
|
+
end
|
|
142
|
+
{
|
|
143
|
+
"range" => range,
|
|
144
|
+
"severity" => 1,
|
|
145
|
+
"code" => diagnostic.code,
|
|
146
|
+
"source" => "ibex",
|
|
147
|
+
"message" => diagnostic.message,
|
|
148
|
+
"data" => { "file" => diagnostic.location.file }
|
|
149
|
+
}
|
|
150
|
+
rescue ArgumentError, Ibex::Error
|
|
151
|
+
{
|
|
152
|
+
"range" => diagnostic_range("", 1, 1),
|
|
153
|
+
"severity" => 1,
|
|
154
|
+
"code" => diagnostic.code,
|
|
155
|
+
"source" => "ibex",
|
|
156
|
+
"message" => diagnostic.message,
|
|
157
|
+
"data" => { "file" => diagnostic.location.file }
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# @rbs (String message, String fallback_path) -> [String, Integer, Integer, String]
|
|
162
|
+
def diagnostic_parts(message, fallback_path)
|
|
163
|
+
match = message.match(/\A(.*):(\d+):(\d+):\s*(.*)\z/m)
|
|
164
|
+
return [fallback_path, 1, 1, message] unless match
|
|
165
|
+
|
|
166
|
+
[match[1] || fallback_path, Integer(match[2]), Integer(match[3]), match[4] || message]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @rbs (String source, Integer line, Integer column) -> Hash[String, Hash[String, Integer]]
|
|
170
|
+
def diagnostic_range(source, line, column)
|
|
171
|
+
line_text = source.lines.fetch(line - 1, "").delete_suffix("\n").delete_suffix("\r")
|
|
172
|
+
prefix = line_text.each_char.take([column - 1, 0].max).join
|
|
173
|
+
character = prefix.each_codepoint.sum { |codepoint| codepoint > 0xFFFF ? 2 : 1 }
|
|
174
|
+
point = { "line" => [line - 1, 0].max, "character" => character }
|
|
175
|
+
{ "start" => point, "end" => point.dup }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @rbs (String path) -> String
|
|
179
|
+
def readable_source(path)
|
|
180
|
+
@loader.read(path)
|
|
181
|
+
rescue SystemCallError
|
|
182
|
+
""
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @rbs (String message, String fallback) -> String
|
|
186
|
+
def file_for_error(message, fallback)
|
|
187
|
+
match = message.match(/\A(.*):\d+:\d+:/)
|
|
188
|
+
return fallback unless match
|
|
189
|
+
|
|
190
|
+
match[1] || fallback
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @rbs (String message, String fallback) -> String
|
|
194
|
+
def source_for_error(message, fallback)
|
|
195
|
+
readable_source(file_for_error(message, fallback))
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
data/lib/ibex/lsp.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "uri"
|
|
5
|
+
require_relative "version"
|
|
6
|
+
require_relative "error"
|
|
7
|
+
require_relative "frontend"
|
|
8
|
+
require_relative "lsp/protocol_error"
|
|
9
|
+
require_relative "lsp/transport"
|
|
10
|
+
require_relative "lsp/position_codec"
|
|
11
|
+
require_relative "lsp/workspace"
|
|
12
|
+
require_relative "lsp/workspace_analyzer"
|
|
13
|
+
require_relative "lsp/document_store_validation"
|
|
14
|
+
require_relative "lsp/document_store_diagnostics"
|
|
15
|
+
require_relative "lsp/document_store"
|
|
16
|
+
require_relative "lsp/symbol_occurrence"
|
|
17
|
+
require_relative "lsp/symbol_index_source_queries"
|
|
18
|
+
require_relative "lsp/symbol_index_precedence_references"
|
|
19
|
+
require_relative "lsp/symbol_index_builder"
|
|
20
|
+
require_relative "lsp/symbol_index"
|
|
21
|
+
require_relative "lsp/request_support"
|
|
22
|
+
require_relative "lsp/initialization_handlers"
|
|
23
|
+
require_relative "lsp/document_handlers"
|
|
24
|
+
require_relative "lsp/navigation_handlers"
|
|
25
|
+
require_relative "lsp/request_handlers"
|
|
26
|
+
require_relative "lsp/server"
|
|
27
|
+
|
|
28
|
+
module Ibex
|
|
29
|
+
# Language Server Protocol support for grammar workspaces.
|
|
30
|
+
module LSP
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -11,27 +11,160 @@ module Ibex
|
|
|
11
11
|
@declared_tokens = {} #: Hash[String, IR::location]
|
|
12
12
|
@precedence = {} #: Hash[String, IR::precedence]
|
|
13
13
|
@precedence_locations = {} #: Hash[String, IR::location]
|
|
14
|
+
@display_names = {} #: Hash[String, String]
|
|
15
|
+
@display_name_locations = {} #: Hash[String, IR::location]
|
|
16
|
+
@semantic_types = {} #: Hash[String, String]
|
|
17
|
+
@semantic_type_locations = {} #: Hash[String, IR::location]
|
|
14
18
|
@options = { result_var: true, omit_action_call: true }
|
|
19
|
+
@options[:cst] = true if @ast.cst
|
|
15
20
|
@expected_conflicts = 0
|
|
21
|
+
@expected_rr_conflicts = nil
|
|
16
22
|
@conversions = {} #: Hash[String, String]
|
|
23
|
+
@parser_parameters = [] #: Array[IR::parser_parameter]
|
|
24
|
+
@value_printers = {} #: Hash[String, IR::value_printer]
|
|
25
|
+
@recovery_sync_tokens = [] #: Array[String]
|
|
26
|
+
@recovery_sync_location = nil #: Frontend::Location?
|
|
27
|
+
@on_error_reduce_groups = [] #: Array[Array[String]]
|
|
28
|
+
@on_error_reduce_locations = {} #: Hash[String, Frontend::Location]
|
|
29
|
+
@grammar_tests = [] #: Array[IR::grammar_test]
|
|
30
|
+
@lexer_declaration = nil #: Frontend::AST::Lexer?
|
|
31
|
+
@node_shapes = {} #: Hash[String, Array[String]]
|
|
17
32
|
@ast.declarations.each { |declaration| read_declaration(declaration) }
|
|
18
33
|
end
|
|
19
34
|
|
|
20
35
|
# @rbs (Frontend::AST::declaration declaration) -> void
|
|
21
|
-
def read_declaration(declaration)
|
|
36
|
+
def read_declaration(declaration) # rubocop:disable Metrics/CyclomaticComplexity
|
|
22
37
|
# @type self: Normalizer
|
|
23
38
|
case declaration
|
|
24
|
-
when Frontend::AST::Tokens then declaration
|
|
39
|
+
when Frontend::AST::Tokens then read_tokens(declaration)
|
|
25
40
|
when Frontend::AST::Precedence then read_precedence(declaration)
|
|
26
|
-
when Frontend::AST::Options then
|
|
41
|
+
when Frontend::AST::Options then read_options(declaration)
|
|
27
42
|
when Frontend::AST::Expect then @expected_conflicts = declaration.conflicts
|
|
28
|
-
when Frontend::AST::
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
when Frontend::AST::
|
|
43
|
+
when Frontend::AST::ExpectRR then @expected_rr_conflicts = declaration.conflicts
|
|
44
|
+
when Frontend::AST::Start, Frontend::AST::Recovery, Frontend::AST::OnErrorReduce, Frontend::AST::GrammarTest
|
|
45
|
+
read_parser_control_declaration(declaration)
|
|
46
|
+
when Frontend::AST::Lexer
|
|
47
|
+
fail_at(declaration.loc, "duplicate lexer declaration") if @lexer_declaration
|
|
48
|
+
@lexer_declaration = declaration
|
|
49
|
+
when Frontend::AST::Convert then read_conversions(declaration)
|
|
50
|
+
when Frontend::AST::DisplayName, Frontend::AST::SemanticType, Frontend::AST::Parameter, Frontend::AST::Printer
|
|
51
|
+
read_extended_declaration(declaration)
|
|
52
|
+
when Frontend::AST::Include then fail_at(declaration.loc, "includes must be resolved before normalization")
|
|
32
53
|
end
|
|
33
54
|
end
|
|
34
55
|
|
|
56
|
+
# @rbs (Frontend::AST::Start declaration) -> void
|
|
57
|
+
def read_start_declaration(declaration)
|
|
58
|
+
# @type self: Normalizer
|
|
59
|
+
fail_at(declaration.loc, "duplicate start declaration") if @explicit_starts
|
|
60
|
+
if declaration.names.length > 1 && @mode != :extended
|
|
61
|
+
fail_at(declaration.loc, "multiple start symbols require extended mode")
|
|
62
|
+
end
|
|
63
|
+
fail_at(declaration.loc, "start declaration requires at least one symbol") if declaration.names.empty?
|
|
64
|
+
unless declaration.names.uniq.length == declaration.names.length
|
|
65
|
+
fail_at(declaration.loc, "start symbols must be unique")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@explicit_starts = declaration.names
|
|
69
|
+
@start_location = declaration.loc
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @rbs (Frontend::AST::symbol_metadata | Frontend::AST::Parameter | Frontend::AST::Printer declaration) -> void
|
|
73
|
+
def read_extended_declaration(declaration)
|
|
74
|
+
if declaration.is_a?(Frontend::AST::Parameter)
|
|
75
|
+
read_parser_parameter(declaration)
|
|
76
|
+
elsif declaration.is_a?(Frontend::AST::Printer)
|
|
77
|
+
read_value_printer(declaration)
|
|
78
|
+
else
|
|
79
|
+
read_symbol_metadata_declaration(declaration)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @rbs (Frontend::AST::Printer declaration) -> void
|
|
84
|
+
def read_value_printer(declaration)
|
|
85
|
+
# @type self: Normalizer
|
|
86
|
+
if @value_printers.key?(declaration.name)
|
|
87
|
+
fail_at(declaration.loc, "duplicate %printer declaration for #{declaration.name}")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
@value_printers[declaration.name] = {
|
|
91
|
+
symbol: declaration.name, code: declaration.code, loc: declaration.loc.to_h
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @rbs () -> void
|
|
96
|
+
def validate_value_printers
|
|
97
|
+
@value_printers.each_value do |printer|
|
|
98
|
+
next if @symbols_by_name.key?(printer[:symbol])
|
|
99
|
+
|
|
100
|
+
location = printer[:loc]
|
|
101
|
+
raise Ibex::Error,
|
|
102
|
+
"#{location[:file]}:#{location[:line]}:#{location[:column]}: " \
|
|
103
|
+
"%printer references missing symbol #{printer[:symbol]}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @rbs (Frontend::AST::Parameter declaration) -> void
|
|
108
|
+
def read_parser_parameter(declaration)
|
|
109
|
+
# @type self: Normalizer
|
|
110
|
+
if @parser_parameters.any? { |parameter| parameter[:name] == declaration.name }
|
|
111
|
+
fail_at(declaration.loc, "duplicate %param declaration for #{declaration.name}")
|
|
112
|
+
end
|
|
113
|
+
unless declaration.name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
|
|
114
|
+
fail_at(declaration.loc, "%param name #{declaration.name.inspect} must be a Ruby local identifier")
|
|
115
|
+
end
|
|
116
|
+
if Normalizer::RUBY_KEYWORDS.include?(declaration.name)
|
|
117
|
+
fail_at(declaration.loc, "%param name #{declaration.name.inspect} is a Ruby keyword")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
@parser_parameters << { name: declaration.name, semantic_type: declaration.semantic_type }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @rbs (Frontend::AST::Tokens declaration) -> void
|
|
124
|
+
def read_tokens(declaration)
|
|
125
|
+
# @type self: Normalizer
|
|
126
|
+
declaration.names.each { |name| @declared_tokens[name] = declaration.loc.to_h }
|
|
127
|
+
(declaration.aliases || {}).each do |name, value|
|
|
128
|
+
fail_at(declaration.loc, "duplicate display declaration for #{name}") if @display_names.key?(name)
|
|
129
|
+
|
|
130
|
+
@display_names[name] = value
|
|
131
|
+
@display_name_locations[name] = declaration.loc.to_h
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @rbs (Frontend::AST::Options declaration) -> void
|
|
136
|
+
def read_options(declaration)
|
|
137
|
+
declaration.names.each { |name| read_option(name, declaration.loc) }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @rbs (Frontend::AST::Convert declaration) -> void
|
|
141
|
+
def read_conversions(declaration)
|
|
142
|
+
# @type self: Normalizer
|
|
143
|
+
declaration.pairs.each { |pair| @conversions[pair.name] = pair.expression }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @rbs (Frontend::AST::symbol_metadata declaration) -> void
|
|
147
|
+
def read_symbol_metadata_declaration(declaration)
|
|
148
|
+
# @type self: Normalizer
|
|
149
|
+
if declaration.is_a?(Frontend::AST::DisplayName)
|
|
150
|
+
read_symbol_metadata(declaration, @display_names, @display_name_locations, "display")
|
|
151
|
+
else
|
|
152
|
+
read_symbol_metadata(declaration, @semantic_types, @semantic_type_locations, "type")
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# @rbs (Frontend::AST::symbol_metadata declaration, Hash[String, String] values,
|
|
157
|
+
# Hash[String, IR::location] locations, String label) -> void
|
|
158
|
+
def read_symbol_metadata(declaration, values, locations, label)
|
|
159
|
+
# @type self: Normalizer
|
|
160
|
+
if values.key?(declaration.name)
|
|
161
|
+
fail_at(declaration.loc, "duplicate #{label} declaration for #{declaration.name}")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
values[declaration.name] = declaration.value
|
|
165
|
+
locations[declaration.name] = declaration.loc.to_h
|
|
166
|
+
end
|
|
167
|
+
|
|
35
168
|
# @rbs (Frontend::AST::Precedence declaration) -> void
|
|
36
169
|
def read_precedence(declaration)
|
|
37
170
|
# @type self: Normalizer
|
|
@@ -10,12 +10,26 @@ module Ibex
|
|
|
10
10
|
# @rbs () -> void
|
|
11
11
|
def validate_grammar
|
|
12
12
|
# @type self: Normalizer
|
|
13
|
+
validate_symbol_metadata
|
|
13
14
|
warn_duplicate_productions
|
|
14
15
|
warn_unreachable_nonterminals
|
|
16
|
+
warn_unreachable_terminals
|
|
15
17
|
warn_unused_terminals
|
|
18
|
+
warn_unused_precedence
|
|
16
19
|
warn_empty_language
|
|
17
20
|
end
|
|
18
21
|
|
|
22
|
+
# @rbs () -> void
|
|
23
|
+
def validate_symbol_metadata
|
|
24
|
+
# @type self: Normalizer
|
|
25
|
+
{ display: @display_name_locations, type: @semantic_type_locations }.each do |label, locations|
|
|
26
|
+
locations.each do |name, location|
|
|
27
|
+
fail_hash(location, "#{label} declaration references undefined symbol #{name}") unless
|
|
28
|
+
symbol(name) || parameter_template?(name) || (label == :type && @inline_rule_names.include?(name))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
19
33
|
# @rbs () -> void
|
|
20
34
|
def warn_duplicate_productions
|
|
21
35
|
# @type self: Normalizer
|
|
@@ -45,8 +59,8 @@ module Ibex
|
|
|
45
59
|
# @rbs () -> Set[Integer]
|
|
46
60
|
def reachable_symbol_ids
|
|
47
61
|
# @type self: Normalizer
|
|
48
|
-
|
|
49
|
-
reachable = Set
|
|
62
|
+
starts = @start_names.map { |name| required_symbol(name).id }
|
|
63
|
+
reachable = Set.new(starts)
|
|
50
64
|
loop do
|
|
51
65
|
before = reachable.length
|
|
52
66
|
@productions.select { |production| reachable.include?(production.lhs) }.each do |production|
|
|
@@ -67,6 +81,35 @@ module Ibex
|
|
|
67
81
|
end
|
|
68
82
|
end
|
|
69
83
|
|
|
84
|
+
# @rbs () -> void
|
|
85
|
+
def warn_unreachable_terminals
|
|
86
|
+
# @type self: Normalizer
|
|
87
|
+
reachable = reachable_symbol_ids
|
|
88
|
+
used = @productions.flat_map(&:rhs).to_set
|
|
89
|
+
@declared_tokens.each_key do |name|
|
|
90
|
+
grammar_symbol = required_symbol(name)
|
|
91
|
+
next unless used.include?(grammar_symbol.id)
|
|
92
|
+
next if reachable.include?(grammar_symbol.id)
|
|
93
|
+
|
|
94
|
+
@warnings << { type: :unreachable_terminal, symbol: name, loc: @declared_tokens[name] }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs () -> void
|
|
99
|
+
def warn_unused_precedence
|
|
100
|
+
# @type self: Normalizer
|
|
101
|
+
referenced = @productions.each_with_object(Set.new) do |production, symbol_ids|
|
|
102
|
+
production.rhs.each { |id| symbol_ids << id }
|
|
103
|
+
symbol_ids << production.precedence_override if production.precedence_override
|
|
104
|
+
end
|
|
105
|
+
@precedence.each_key do |name|
|
|
106
|
+
grammar_symbol = required_symbol(name)
|
|
107
|
+
next if referenced.include?(grammar_symbol.id)
|
|
108
|
+
|
|
109
|
+
@warnings << { type: :unused_precedence, symbol: name, loc: @precedence_locations[name] }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
70
113
|
# @rbs () -> void
|
|
71
114
|
def warn_empty_language
|
|
72
115
|
# @type self: Normalizer
|
|
@@ -78,10 +121,12 @@ module Ibex
|
|
|
78
121
|
end
|
|
79
122
|
break if productive.length == before
|
|
80
123
|
end
|
|
81
|
-
|
|
124
|
+
@start_names.each do |name|
|
|
125
|
+
next if productive.include?(required_symbol(name).id)
|
|
82
126
|
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
start_symbol = required_symbol(name)
|
|
128
|
+
@warnings << { type: :empty_language, symbol: name, loc: start_symbol.location }
|
|
129
|
+
end
|
|
85
130
|
end
|
|
86
131
|
|
|
87
132
|
# @rbs () -> Set[Integer]
|
|
@@ -9,6 +9,9 @@ module Ibex
|
|
|
9
9
|
def normalize_user_productions
|
|
10
10
|
# @type self: Normalizer
|
|
11
11
|
@ast.rules.each do |rule|
|
|
12
|
+
next if parameterized_rule?(rule)
|
|
13
|
+
|
|
14
|
+
@current_include_chain = @resolution&.include_chain_for(rule) || []
|
|
12
15
|
rule.alternatives.each { |alternative| normalize_alternative(rule, alternative) }
|
|
13
16
|
end
|
|
14
17
|
end
|
|
@@ -18,7 +21,8 @@ module Ibex
|
|
|
18
21
|
# @type self: Normalizer
|
|
19
22
|
rhs = [] #: Array[String]
|
|
20
23
|
named_refs = [] #: Array[IR::named_ref]
|
|
21
|
-
|
|
24
|
+
items = normalized_alternative_items(alternative)
|
|
25
|
+
items.each do |item|
|
|
22
26
|
if item.is_a?(Frontend::AST::InlineAction)
|
|
23
27
|
rhs << expand_inline_action(item, rhs.length, named_refs)
|
|
24
28
|
next
|
|
@@ -28,14 +32,31 @@ module Ibex
|
|
|
28
32
|
add_named_reference(item, named_refs, rhs.length - 1)
|
|
29
33
|
end
|
|
30
34
|
action = normalize_action(alternative.action, named_refs)
|
|
35
|
+
node = normalize_node_annotation(alternative, rhs)
|
|
31
36
|
add_production(rule.lhs, rhs, action, alternative.precedence,
|
|
32
|
-
{ kind: :user, loc: alternative.loc.to_h })
|
|
37
|
+
{ kind: :user, loc: alternative.loc.to_h }, rule.documentation, node)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @rbs (Frontend::AST::Alternative alternative) -> Array[Frontend::AST::item]
|
|
41
|
+
def normalized_alternative_items(alternative)
|
|
42
|
+
# @type self: Normalizer
|
|
43
|
+
explicit = alternative.items.grep(Frontend::AST::Empty)
|
|
44
|
+
if explicit.any?
|
|
45
|
+
fail_at(explicit.first.loc, "%empty must be the only item in an alternative") unless alternative.items.one?
|
|
46
|
+
return []
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if alternative.items.empty? && (@mode.to_sym == :extended || @ast.extended)
|
|
50
|
+
@warnings << { type: :implicit_empty, loc: alternative.loc.to_h }
|
|
51
|
+
end
|
|
52
|
+
alternative.items
|
|
33
53
|
end
|
|
34
54
|
|
|
35
55
|
# @rbs (Frontend::AST::item item) -> String
|
|
36
56
|
def normalize_item(item)
|
|
37
57
|
# @type self: Normalizer
|
|
38
58
|
return symbol_for_reference(item).name if item.is_a?(Frontend::AST::SymbolReference)
|
|
59
|
+
return specialize_parameterized_reference(item) if item.is_a?(Frontend::AST::ParameterizedReference)
|
|
39
60
|
return expand_group(item) if item.is_a?(Frontend::AST::Group)
|
|
40
61
|
return expand_optional(item) if item.is_a?(Frontend::AST::Optional)
|
|
41
62
|
return expand_star(item) if item.is_a?(Frontend::AST::Star)
|
|
@@ -59,6 +80,12 @@ module Ibex
|
|
|
59
80
|
def expand_optional(item)
|
|
60
81
|
# @type self: Normalizer
|
|
61
82
|
base = normalize_item(item.item)
|
|
83
|
+
build_optional(item, base)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @rbs (Frontend::AST::Optional item, String base) -> String
|
|
87
|
+
def build_optional(item, base)
|
|
88
|
+
# @type self: Normalizer
|
|
62
89
|
helper = new_helper("optional", item.loc)
|
|
63
90
|
add_production(helper, [], synthetic_action("nil", item.loc), nil, synthetic_origin(:optional, item))
|
|
64
91
|
add_production(helper, [base], synthetic_action("val[0]", item.loc), nil, synthetic_origin(:optional, item))
|
|
@@ -69,6 +96,12 @@ module Ibex
|
|
|
69
96
|
def expand_star(item)
|
|
70
97
|
# @type self: Normalizer
|
|
71
98
|
base = normalize_item(item.item)
|
|
99
|
+
build_star(item, base)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @rbs (Frontend::AST::Star item, String base) -> String
|
|
103
|
+
def build_star(item, base)
|
|
104
|
+
# @type self: Normalizer
|
|
72
105
|
helper = new_helper("star", item.loc)
|
|
73
106
|
add_production(helper, [], synthetic_action("[]", item.loc), nil, synthetic_origin(:star, item))
|
|
74
107
|
add_production(helper, [helper, base], synthetic_action("val[0] + [val[1]]", item.loc), nil,
|
|
@@ -80,6 +113,12 @@ module Ibex
|
|
|
80
113
|
def expand_plus(item)
|
|
81
114
|
# @type self: Normalizer
|
|
82
115
|
base = normalize_item(item.item)
|
|
116
|
+
build_plus(item, base)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @rbs (Frontend::AST::Plus item, String base) -> String
|
|
120
|
+
def build_plus(item, base)
|
|
121
|
+
# @type self: Normalizer
|
|
83
122
|
helper = new_helper("plus", item.loc)
|
|
84
123
|
add_production(helper, [base], synthetic_action("[val[0]]", item.loc), nil, synthetic_origin(:plus, item))
|
|
85
124
|
add_production(helper, [helper, base], synthetic_action("val[0] + [val[1]]", item.loc), nil,
|
|
@@ -92,6 +131,12 @@ module Ibex
|
|
|
92
131
|
# @type self: Normalizer
|
|
93
132
|
base = normalize_item(item.item)
|
|
94
133
|
separator = normalize_item(item.separator)
|
|
134
|
+
build_separated_list(item, base, separator)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @rbs (Frontend::AST::SeparatedList item, String base, String separator) -> String
|
|
138
|
+
def build_separated_list(item, base, separator)
|
|
139
|
+
# @type self: Normalizer
|
|
95
140
|
helper = new_helper("separated_list", item.loc)
|
|
96
141
|
unless item.nonempty
|
|
97
142
|
add_production(helper, [], synthetic_action("[]", item.loc), nil, synthetic_origin(:separated_list, item))
|
|
@@ -110,12 +155,18 @@ module Ibex
|
|
|
110
155
|
helper = new_helper("group", item.loc)
|
|
111
156
|
item.alternatives.each do |alternative|
|
|
112
157
|
rhs = alternative.map { |child| normalize_item(child) }
|
|
113
|
-
|
|
114
|
-
add_production(helper, rhs, synthetic_action(expression, item.loc), nil, synthetic_origin(:group, item))
|
|
158
|
+
add_group_production(helper, rhs, item)
|
|
115
159
|
end
|
|
116
160
|
helper
|
|
117
161
|
end
|
|
118
162
|
|
|
163
|
+
# @rbs (String helper, Array[String] rhs, Frontend::AST::Group item) -> void
|
|
164
|
+
def add_group_production(helper, rhs, item)
|
|
165
|
+
# @type self: Normalizer
|
|
166
|
+
expression = group_value_expression(rhs.length)
|
|
167
|
+
add_production(helper, rhs, synthetic_action(expression, item.loc), nil, synthetic_origin(:group, item))
|
|
168
|
+
end
|
|
169
|
+
|
|
119
170
|
# @rbs (Integer length) -> String
|
|
120
171
|
def group_value_expression(length)
|
|
121
172
|
# @type self: Normalizer
|
|
@@ -125,28 +176,6 @@ module Ibex
|
|
|
125
176
|
"val"
|
|
126
177
|
end
|
|
127
178
|
|
|
128
|
-
# @rbs (Frontend::AST::Group group) -> void
|
|
129
|
-
def reject_group_named_references(group)
|
|
130
|
-
# @type self: Normalizer
|
|
131
|
-
reference = group.alternatives.flatten.filter_map { |item| named_reference_in(item) }.first
|
|
132
|
-
fail_at(reference.loc, "named references inside EBNF groups are not supported") if reference
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# @rbs (Frontend::AST::item item) -> Frontend::AST::SymbolReference?
|
|
136
|
-
def named_reference_in(item)
|
|
137
|
-
# @type self: Normalizer
|
|
138
|
-
return item if item.is_a?(Frontend::AST::SymbolReference) && item.named_reference
|
|
139
|
-
if item.is_a?(Frontend::AST::Group)
|
|
140
|
-
return item.alternatives.flatten.filter_map { |child| named_reference_in(child) }.first
|
|
141
|
-
end
|
|
142
|
-
if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
|
|
143
|
-
item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
|
|
144
|
-
return named_reference_in(item.item)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
nil
|
|
148
|
-
end
|
|
149
|
-
|
|
150
179
|
# @rbs (String kind, Frontend::Location location) -> String
|
|
151
180
|
def new_helper(kind, location)
|
|
152
181
|
# @type self: Normalizer
|
|
@@ -177,42 +206,17 @@ module Ibex
|
|
|
177
206
|
IR::Action.new(code: action.code, location: action.loc.to_h, named_refs: named_refs)
|
|
178
207
|
end
|
|
179
208
|
|
|
180
|
-
# @rbs (Frontend::AST::item item, Array[IR::named_ref] refs, Integer index) -> void
|
|
181
|
-
def add_named_reference(item, refs, index)
|
|
182
|
-
# @type self: Normalizer
|
|
183
|
-
reference = unwrap_reference(item)
|
|
184
|
-
return unless reference
|
|
185
|
-
|
|
186
|
-
name = reference.named_reference
|
|
187
|
-
return unless name
|
|
188
|
-
|
|
189
|
-
fail_at(reference.loc, "reserved named reference #{name}") if Normalizer::RESERVED_NAMES.include?(name)
|
|
190
|
-
fail_at(reference.loc, "duplicate named reference #{name}") if refs.any? { |entry| entry[:name] == name }
|
|
191
|
-
refs << { name: name, index: index }
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# @rbs (Frontend::AST::item item) -> Frontend::AST::SymbolReference?
|
|
195
|
-
def unwrap_reference(item)
|
|
196
|
-
# @type self: Normalizer
|
|
197
|
-
return item if item.is_a?(Frontend::AST::SymbolReference)
|
|
198
|
-
if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
|
|
199
|
-
item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
|
|
200
|
-
return unwrap_reference(item.item)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
nil
|
|
204
|
-
end
|
|
205
|
-
|
|
206
209
|
# @rbs (String lhs_name, Array[String] rhs_names, IR::Action? action, String? precedence_name,
|
|
207
|
-
# Hash[Symbol, untyped] origin) -> void
|
|
208
|
-
def add_production(lhs_name, rhs_names, action, precedence_name, origin)
|
|
210
|
+
# Hash[Symbol, untyped] origin, ?String? documentation, ?IR::node_annotation? node) -> void
|
|
211
|
+
def add_production(lhs_name, rhs_names, action, precedence_name, origin, documentation = nil, node = nil)
|
|
209
212
|
# @type self: Normalizer
|
|
210
213
|
lhs = symbol(lhs_name) || intern(lhs_name, :nonterminal, location: origin[:loc])
|
|
211
214
|
rhs = rhs_names.map { |name| symbol(name)&.id || fail_hash(origin[:loc], "undefined symbol #{name}") }
|
|
212
215
|
precedence = precedence_name && symbol(precedence_name)
|
|
213
216
|
fail_hash(origin[:loc], "undefined precedence symbol #{precedence_name}") if precedence_name && !precedence
|
|
214
217
|
@productions << IR::Production.new(id: @productions.length, lhs: lhs.id, rhs: rhs, action: action,
|
|
215
|
-
precedence_override: precedence&.id, origin: origin
|
|
218
|
+
precedence_override: precedence&.id, origin: origin,
|
|
219
|
+
documentation: documentation, expansion: resolved_expansion, node: node)
|
|
216
220
|
end
|
|
217
221
|
end
|
|
218
222
|
end
|