ibex 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +247 -104
- data/docs/architecture.md +322 -28
- data/docs/cst-migration.md +107 -0
- data/docs/cst.md +213 -0
- data/docs/development.md +129 -0
- data/docs/editor-setup.md +25 -0
- data/docs/error-ux.md +60 -0
- data/docs/grammar-reference.md +458 -18
- data/docs/lexer-migration.md +51 -0
- data/docs/racc-migration.md +34 -6
- data/docs/release-readiness.md +163 -0
- data/docs/stability.md +124 -0
- data/examples/README.md +60 -0
- data/examples/calculator.y +44 -0
- data/examples/csv.y +32 -0
- data/examples/ini.y +70 -0
- data/examples/json.y +48 -0
- data/examples/tiny_language.y +75 -0
- data/lib/ibex/analysis/sets.rb +5 -4
- data/lib/ibex/artifact_set.rb +63 -0
- data/lib/ibex/cli/ambiguity.rb +66 -0
- data/lib/ibex/cli/counterexample_options.rb +6 -2
- data/lib/ibex/cli/coverage.rb +173 -0
- data/lib/ibex/cli/debug.rb +106 -0
- data/lib/ibex/cli/diagnostics.rb +116 -0
- data/lib/ibex/cli/documentation.rb +67 -0
- data/lib/ibex/cli/error_messages.rb +177 -0
- data/lib/ibex/cli/explain.rb +76 -0
- data/lib/ibex/cli/formatting.rb +386 -0
- data/lib/ibex/cli/generation_artifacts.rb +138 -0
- data/lib/ibex/cli/generation_error_messages.rb +39 -0
- data/lib/ibex/cli/grammar_tests.rb +122 -0
- data/lib/ibex/cli/ir_tools.rb +184 -0
- data/lib/ibex/cli/lsp.rb +33 -0
- data/lib/ibex/cli/outputs.rb +91 -8
- data/lib/ibex/cli/racc_migration.rb +87 -0
- data/lib/ibex/cli/samples.rb +92 -0
- data/lib/ibex/cli/watch.rb +103 -0
- data/lib/ibex/cli.rb +506 -37
- data/lib/ibex/codegen/action_locations.rb +103 -0
- data/lib/ibex/codegen/action_method_source.rb +210 -0
- data/lib/ibex/codegen/action_source.rb +118 -0
- data/lib/ibex/codegen/ambiguity.rb +173 -0
- data/lib/ibex/codegen/cst_metadata.rb +171 -0
- data/lib/ibex/codegen/documentation.rb +170 -0
- data/lib/ibex/codegen/explain.rb +312 -0
- data/lib/ibex/codegen/generated_action_abi.rb +288 -0
- data/lib/ibex/codegen/html.rb +94 -10
- data/lib/ibex/codegen/mermaid.rb +43 -0
- data/lib/ibex/codegen/railroad.rb +197 -0
- data/lib/ibex/codegen/railroad_documentation.rb +59 -0
- data/lib/ibex/codegen/rbs.rb +323 -2
- data/lib/ibex/codegen/report.rb +46 -6
- data/lib/ibex/codegen/ruby.rb +334 -59
- data/lib/ibex/codegen/ruby_actions.rb +143 -0
- data/lib/ibex/codegen/ruby_ast.rb +121 -0
- data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
- data/lib/ibex/codegen/ruby_lexer.rb +83 -0
- data/lib/ibex/codegen/ruby_syntax.rb +90 -0
- data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
- data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
- data/lib/ibex/codegen/symbol_labels.rb +1 -1
- data/lib/ibex/coverage/collector.rb +188 -0
- data/lib/ibex/coverage/event_stream.rb +97 -0
- data/lib/ibex/coverage/report.rb +259 -0
- data/lib/ibex/coverage/runtime_event_validator.rb +160 -0
- data/lib/ibex/coverage.rb +13 -0
- data/lib/ibex/error_messages/parser.rb +159 -0
- data/lib/ibex/error_messages/parser_v2.rb +198 -0
- data/lib/ibex/error_messages/renderer.rb +65 -0
- data/lib/ibex/error_messages/sentence_search.rb +196 -0
- data/lib/ibex/error_messages/update.rb +169 -0
- data/lib/ibex/error_messages.rb +165 -0
- data/lib/ibex/frontend/ast.rb +145 -6
- data/lib/ibex/frontend/bootstrap_parser.rb +47 -4
- data/lib/ibex/frontend/diagnostic.rb +81 -0
- data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
- data/lib/ibex/frontend/dsl.rb +66 -7
- data/lib/ibex/frontend/formatter.rb +407 -0
- data/lib/ibex/frontend/generated_parser.rb +614 -190
- data/lib/ibex/frontend/generated_parser_base.rb +164 -30
- data/lib/ibex/frontend/generated_parser_includes.rb +61 -0
- data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
- data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
- data/lib/ibex/frontend/generation.rb +33 -0
- data/lib/ibex/frontend/lexer.rb +162 -10
- data/lib/ibex/frontend/lexer_recovery.rb +84 -0
- data/lib/ibex/frontend/parser/declarations.rb +247 -11
- data/lib/ibex/frontend/parser/parameters.rb +82 -0
- data/lib/ibex/frontend/parser/rules.rb +43 -6
- data/lib/ibex/frontend/parser.rb +182 -4
- data/lib/ibex/frontend/regenerator.rb +26 -1
- data/lib/ibex/frontend/resolution.rb +69 -0
- data/lib/ibex/frontend/resolver.rb +217 -0
- data/lib/ibex/frontend/rule_documentation.rb +103 -0
- data/lib/ibex/frontend/source_cursor.rb +132 -8
- data/lib/ibex/frontend/source_document.rb +229 -0
- data/lib/ibex/frontend/source_loader.rb +150 -0
- data/lib/ibex/frontend/source_span.rb +81 -0
- data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
- data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
- data/lib/ibex/frontend/token_adapter/declaration_state.rb +216 -26
- data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
- data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
- data/lib/ibex/frontend/token_adapter.rb +8 -3
- data/lib/ibex/frontend.rb +13 -2
- data/lib/ibex/generation_input.rb +57 -0
- data/lib/ibex/generation_manifest.rb +200 -0
- data/lib/ibex/generation_transaction.rb +261 -0
- data/lib/ibex/generation_transaction_recovery.rb +109 -0
- data/lib/ibex/generation_transaction_validation.rb +196 -0
- data/lib/ibex/grammar_tests.rb +206 -0
- data/lib/ibex/ir/automaton_ir.rb +38 -5
- data/lib/ibex/ir/grammar_ir.rb +137 -24
- data/lib/ibex/ir/lexer_ir.rb +76 -0
- data/lib/ibex/ir/migration.rb +120 -0
- data/lib/ibex/ir/serialize.rb +110 -19
- data/lib/ibex/ir/validator/automaton.rb +345 -0
- data/lib/ibex/ir/validator/base.rb +129 -0
- data/lib/ibex/ir/validator/grammar.rb +604 -0
- data/lib/ibex/ir/validator/lexer.rb +113 -0
- data/lib/ibex/ir/validator.rb +62 -0
- data/lib/ibex/ir.rb +57 -4
- data/lib/ibex/lalr/build_metrics.rb +23 -0
- data/lib/ibex/lalr/builder.rb +379 -52
- data/lib/ibex/lalr/conflict.rb +1 -0
- data/lib/ibex/lalr/conflict_search.rb +11 -5
- data/lib/ibex/lalr/counterexample.rb +20 -5
- data/lib/ibex/lalr/direct_lookaheads.rb +236 -0
- data/lib/ibex/lalr/ielr_partition.rb +152 -0
- data/lib/ibex/lalr/on_error_reductions.rb +74 -0
- data/lib/ibex/lalr.rb +7 -0
- data/lib/ibex/location.rb +129 -0
- data/lib/ibex/lsp/document_handlers.rb +66 -0
- data/lib/ibex/lsp/document_store.rb +264 -0
- data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
- data/lib/ibex/lsp/document_store_validation.rb +61 -0
- data/lib/ibex/lsp/initialization_handlers.rb +78 -0
- data/lib/ibex/lsp/navigation_handlers.rb +56 -0
- data/lib/ibex/lsp/position_codec.rb +109 -0
- data/lib/ibex/lsp/protocol_error.rb +33 -0
- data/lib/ibex/lsp/request_handlers.rb +45 -0
- data/lib/ibex/lsp/request_support.rb +87 -0
- data/lib/ibex/lsp/server.rb +146 -0
- data/lib/ibex/lsp/symbol_index.rb +241 -0
- data/lib/ibex/lsp/symbol_index_builder.rb +267 -0
- data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
- data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
- data/lib/ibex/lsp/symbol_occurrence.rb +17 -0
- data/lib/ibex/lsp/transport.rb +118 -0
- data/lib/ibex/lsp/workspace.rb +127 -0
- data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
- data/lib/ibex/lsp.rb +32 -0
- data/lib/ibex/normalize/declarations.rb +140 -7
- data/lib/ibex/normalize/diagnostics.rb +50 -5
- data/lib/ibex/normalize/expander.rb +59 -55
- data/lib/ibex/normalize/expression.rb +60 -39
- data/lib/ibex/normalize/inline_expansion.rb +414 -0
- data/lib/ibex/normalize/inline_validation.rb +174 -0
- data/lib/ibex/normalize/lexer.rb +131 -0
- data/lib/ibex/normalize/named_references.rb +60 -0
- data/lib/ibex/normalize/nodes.rb +46 -0
- data/lib/ibex/normalize/parameter_ebnf_lowering.rb +69 -0
- data/lib/ibex/normalize/parameter_lowering.rb +126 -0
- data/lib/ibex/normalize/parameter_substitution.rb +125 -0
- data/lib/ibex/normalize/parameter_validation.rb +140 -0
- data/lib/ibex/normalize/parameters.rb +199 -0
- data/lib/ibex/normalize/recovery_declarations.rb +84 -0
- data/lib/ibex/normalize.rb +179 -14
- data/lib/ibex/racc_migration/checker.rb +122 -0
- data/lib/ibex/racc_migration/harness.rb +177 -0
- data/lib/ibex/racc_migration/report.rb +94 -0
- data/lib/ibex/racc_migration.rb +12 -0
- data/lib/ibex/rake_task.rb +116 -0
- data/lib/ibex/samples.rb +186 -0
- data/lib/ibex/table_simulation/result.rb +51 -0
- data/lib/ibex/table_simulation/simulator.rb +253 -0
- data/lib/ibex/table_simulation/step.rb +60 -0
- data/lib/ibex/table_simulation/text.rb +31 -0
- data/lib/ibex/table_simulation.rb +13 -0
- data/lib/ibex/tables.rb +9 -70
- data/lib/ibex/version.rb +1 -1
- data/lib/ibex/watch/runner.rb +172 -0
- data/lib/ibex/watch/source_snapshot.rb +93 -0
- data/lib/ibex/watch.rb +11 -0
- data/lib/ibex.rb +25 -1
- data/schema/automaton-ir-v1.schema.json +401 -0
- data/schema/automaton-ir-v2.schema.json +58 -0
- data/schema/benchmark-v1.schema.json +212 -0
- data/schema/benchmark-v2.schema.json +61 -0
- data/schema/cst-v1.json +170 -0
- data/schema/error-ux-v1.schema.json +258 -0
- data/schema/explain-v1.schema.json +433 -0
- data/schema/frontend-diagnostics-v1.schema.json +154 -0
- data/schema/generation-manifest-v1.schema.json +115 -0
- data/schema/grammar-ir-v1.schema.json +426 -0
- data/schema/grammar-ir-v2.schema.json +779 -0
- data/schema/lexer-ir-v1.schema.json +215 -0
- data/schema/migration-check-v1.schema.json +60 -0
- data/schema/performance-comparison-v1.schema.json +395 -0
- data/schema/public-performance-comparison-v1.schema.json +506 -0
- data/schema/public-performance-profile-v1.schema.json +360 -0
- data/schema/runtime-coverage-v1.schema.json +86 -0
- data/schema/runtime-event-v1.schema.json +308 -0
- data/schema/table-simulation-v1.schema.json +70 -0
- data/sig/ibex/artifact_set.rbs +37 -0
- data/sig/ibex/cli/ambiguity.rbs +22 -0
- data/sig/ibex/cli/counterexample_options.rbs +2 -0
- data/sig/ibex/cli/coverage.rbs +53 -0
- data/sig/ibex/cli/debug.rbs +28 -0
- data/sig/ibex/cli/diagnostics.rbs +38 -0
- data/sig/ibex/cli/documentation.rbs +25 -0
- data/sig/ibex/cli/error_messages.rbs +57 -0
- data/sig/ibex/cli/explain.rbs +25 -0
- data/sig/ibex/cli/formatting.rbs +103 -0
- data/sig/ibex/cli/generation_artifacts.rbs +50 -0
- data/sig/ibex/cli/generation_error_messages.rbs +19 -0
- data/sig/ibex/cli/grammar_tests.rbs +36 -0
- data/sig/ibex/cli/ir_tools.rbs +51 -0
- data/sig/ibex/cli/lsp.rbs +14 -0
- data/sig/ibex/cli/outputs.rbs +17 -0
- data/sig/ibex/cli/racc_migration.rbs +30 -0
- data/sig/ibex/cli/samples.rbs +30 -0
- data/sig/ibex/cli/watch.rbs +43 -0
- data/sig/ibex/cli.rbs +104 -5
- data/sig/ibex/codegen/action_locations.rbs +45 -0
- data/sig/ibex/codegen/action_method_source.rbs +65 -0
- data/sig/ibex/codegen/action_source.rbs +50 -0
- data/sig/ibex/codegen/ambiguity.rbs +60 -0
- data/sig/ibex/codegen/cst_metadata.rbs +59 -0
- data/sig/ibex/codegen/documentation.rbs +50 -0
- data/sig/ibex/codegen/explain.rbs +85 -0
- data/sig/ibex/codegen/generated_action_abi.rbs +101 -0
- data/sig/ibex/codegen/html.rbs +18 -2
- data/sig/ibex/codegen/mermaid.rbs +16 -0
- data/sig/ibex/codegen/railroad.rbs +82 -0
- data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
- data/sig/ibex/codegen/rbs.rbs +84 -4
- data/sig/ibex/codegen/report.rbs +8 -0
- data/sig/ibex/codegen/ruby.rbs +97 -23
- data/sig/ibex/codegen/ruby_actions.rbs +54 -0
- data/sig/ibex/codegen/ruby_ast.rbs +34 -0
- data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
- data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
- data/sig/ibex/codegen/ruby_syntax.rbs +25 -0
- data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
- data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
- data/sig/ibex/coverage/collector.rbs +76 -0
- data/sig/ibex/coverage/event_stream.rbs +42 -0
- data/sig/ibex/coverage/report.rbs +100 -0
- data/sig/ibex/coverage/runtime_event_validator.rbs +68 -0
- data/sig/ibex/coverage.rbs +7 -0
- data/sig/ibex/error_messages/parser.rbs +58 -0
- data/sig/ibex/error_messages/parser_v2.rbs +67 -0
- data/sig/ibex/error_messages/renderer.rbs +23 -0
- data/sig/ibex/error_messages/sentence_search.rbs +80 -0
- data/sig/ibex/error_messages/update.rbs +43 -0
- data/sig/ibex/error_messages.rbs +85 -0
- data/sig/ibex/frontend/ast.rbs +208 -19
- data/sig/ibex/frontend/bootstrap_parser.rbs +11 -0
- data/sig/ibex/frontend/diagnostic.rbs +53 -0
- data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
- data/sig/ibex/frontend/dsl.rbs +33 -4
- data/sig/ibex/frontend/formatter.rbs +135 -0
- data/sig/ibex/frontend/generated_parser.rbs +218 -68
- data/sig/ibex/frontend/generated_parser_base.rbs +61 -10
- data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
- data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
- data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
- data/sig/ibex/frontend/generation.rbs +6 -0
- data/sig/ibex/frontend/lexer.rbs +49 -2
- data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
- data/sig/ibex/frontend/parser/declarations.rbs +54 -0
- data/sig/ibex/frontend/parser/parameters.rbs +28 -0
- data/sig/ibex/frontend/parser/rules.rbs +3 -0
- data/sig/ibex/frontend/parser.rbs +66 -0
- data/sig/ibex/frontend/regenerator.rbs +11 -0
- data/sig/ibex/frontend/resolution.rbs +33 -0
- data/sig/ibex/frontend/resolver.rbs +91 -0
- data/sig/ibex/frontend/rule_documentation.rbs +42 -0
- data/sig/ibex/frontend/source_cursor.rbs +36 -3
- data/sig/ibex/frontend/source_document.rbs +119 -0
- data/sig/ibex/frontend/source_loader.rbs +66 -0
- data/sig/ibex/frontend/source_span.rbs +53 -0
- data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
- data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
- data/sig/ibex/frontend/token_adapter/declaration_state.rbs +73 -5
- data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
- data/sig/ibex/frontend/token_adapter.rbs +5 -2
- data/sig/ibex/frontend.rbs +1 -1
- data/sig/ibex/generation_input.rbs +37 -0
- data/sig/ibex/generation_manifest.rbs +67 -0
- data/sig/ibex/generation_transaction.rbs +82 -0
- data/sig/ibex/generation_transaction_recovery.rbs +36 -0
- data/sig/ibex/generation_transaction_validation.rbs +65 -0
- data/sig/ibex/grammar_tests.rbs +93 -0
- data/sig/ibex/ir/automaton_ir.rbs +8 -2
- data/sig/ibex/ir/grammar_ir.rbs +75 -15
- data/sig/ibex/ir/lexer_ir.rbs +57 -0
- data/sig/ibex/ir/migration.rbs +34 -0
- data/sig/ibex/ir/serialize.rbs +23 -6
- data/sig/ibex/ir/validator/automaton.rbs +109 -0
- data/sig/ibex/ir/validator/base.rbs +65 -0
- data/sig/ibex/ir/validator/grammar.rbs +184 -0
- data/sig/ibex/ir/validator/lexer.rbs +37 -0
- data/sig/ibex/ir/validator.rbs +16 -0
- data/sig/ibex/ir.rbs +38 -4
- data/sig/ibex/lalr/build_metrics.rbs +20 -0
- data/sig/ibex/lalr/builder.rbs +95 -15
- data/sig/ibex/lalr/conflict_search.rbs +7 -3
- data/sig/ibex/lalr/counterexample.rbs +5 -2
- data/sig/ibex/lalr/direct_lookaheads.rbs +86 -0
- data/sig/ibex/lalr/ielr_partition.rbs +59 -0
- data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
- data/sig/ibex/lalr.rbs +6 -0
- data/sig/ibex/location.rbs +67 -0
- data/sig/ibex/lsp/document_handlers.rbs +24 -0
- data/sig/ibex/lsp/document_store.rbs +94 -0
- data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
- data/sig/ibex/lsp/document_store_validation.rbs +26 -0
- data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
- data/sig/ibex/lsp/navigation_handlers.rbs +30 -0
- data/sig/ibex/lsp/position_codec.rbs +40 -0
- data/sig/ibex/lsp/protocol_error.rbs +32 -0
- data/sig/ibex/lsp/request_handlers.rbs +26 -0
- data/sig/ibex/lsp/request_support.rbs +40 -0
- data/sig/ibex/lsp/server.rbs +55 -0
- data/sig/ibex/lsp/symbol_index.rbs +78 -0
- data/sig/ibex/lsp/symbol_index_builder.rbs +87 -0
- data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
- data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
- data/sig/ibex/lsp/symbol_occurrence.rbs +25 -0
- data/sig/ibex/lsp/transport.rbs +35 -0
- data/sig/ibex/lsp/workspace.rbs +41 -0
- data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
- data/sig/ibex/lsp.rbs +7 -0
- data/sig/ibex/normalize/declarations.rbs +31 -0
- data/sig/ibex/normalize/diagnostics.rbs +9 -0
- data/sig/ibex/normalize/expander.rbs +20 -14
- data/sig/ibex/normalize/expression.rbs +10 -10
- data/sig/ibex/normalize/inline_expansion.rbs +120 -0
- data/sig/ibex/normalize/inline_validation.rbs +40 -0
- data/sig/ibex/normalize/lexer.rbs +37 -0
- data/sig/ibex/normalize/named_references.rbs +20 -0
- data/sig/ibex/normalize/nodes.rbs +14 -0
- data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +32 -0
- data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
- data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
- data/sig/ibex/normalize/parameter_validation.rbs +44 -0
- data/sig/ibex/normalize/parameters.rbs +46 -0
- data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
- data/sig/ibex/normalize.rbs +123 -18
- data/sig/ibex/racc_migration/checker.rbs +36 -0
- data/sig/ibex/racc_migration/harness.rbs +16 -0
- data/sig/ibex/racc_migration/report.rbs +53 -0
- data/sig/ibex/racc_migration.rbs +8 -0
- data/sig/ibex/rake_task.rbs +51 -0
- data/sig/ibex/samples.rbs +51 -0
- data/sig/ibex/table_simulation/result.rbs +32 -0
- data/sig/ibex/table_simulation/simulator.rbs +98 -0
- data/sig/ibex/table_simulation/step.rbs +40 -0
- data/sig/ibex/table_simulation/text.rbs +14 -0
- data/sig/ibex/table_simulation.rbs +7 -0
- data/sig/ibex/tables.rbs +0 -26
- data/sig/ibex/watch/runner.rbs +48 -0
- data/sig/ibex/watch/source_snapshot.rbs +42 -0
- data/sig/ibex/watch.rbs +7 -0
- data/sig/ibex.rbs +2 -0
- metadata +301 -16
- data/.rubocop.yml +0 -43
- data/CHANGELOG.md +0 -30
- data/Rakefile +0 -25
- data/Steepfile +0 -10
- data/docs/compat-notes.md +0 -37
- data/docs/lexer-coverage.md +0 -14
- data/docs/phase10-extensions.md +0 -27
- data/gemfiles/Gemfile +0 -7
- data/gemfiles/Gemfile.lock +0 -98
- data/lib/ibex/frontend/grammar.y +0 -156
- data/lib/ibex/runtime/parser.rb +0 -360
- data/lib/ibex/runtime.rb +0 -8
- data/sig/ibex/runtime/parser.rbs +0 -167
- data/sig/ibex/runtime.rbs +0 -6
data/lib/ibex/cli.rb
CHANGED
|
@@ -1,39 +1,98 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
3
4
|
require "optparse"
|
|
4
|
-
require_relative "
|
|
5
|
+
require_relative "version"
|
|
6
|
+
require_relative "error"
|
|
7
|
+
require_relative "artifact_set"
|
|
8
|
+
require_relative "generation_input"
|
|
9
|
+
require_relative "generation_transaction"
|
|
10
|
+
require_relative "frontend/generation"
|
|
11
|
+
require_relative "ir/grammar_ir"
|
|
12
|
+
require_relative "ir/lexer_ir"
|
|
13
|
+
require_relative "ir/serialize"
|
|
14
|
+
require_relative "ir/automaton_ir"
|
|
15
|
+
require_relative "normalize"
|
|
16
|
+
require_relative "analysis"
|
|
17
|
+
require_relative "lalr/conflict"
|
|
18
|
+
require_relative "lalr/on_error_reductions"
|
|
19
|
+
require_relative "lalr/default_reductions"
|
|
20
|
+
require_relative "lalr/build_metrics"
|
|
21
|
+
require_relative "lalr/direct_lookaheads"
|
|
22
|
+
require_relative "lalr/ielr_partition"
|
|
23
|
+
require_relative "lalr/builder"
|
|
24
|
+
require_relative "codegen/ruby"
|
|
5
25
|
require_relative "cli/counterexample_options"
|
|
26
|
+
require_relative "cli/generation_error_messages"
|
|
27
|
+
require_relative "cli/generation_artifacts"
|
|
6
28
|
require_relative "cli/outputs"
|
|
7
29
|
|
|
8
30
|
module Ibex
|
|
31
|
+
CLI_FEATURE_ROOT = File.expand_path("cli", __dir__ || raise("CLI source directory is unavailable")) #: String
|
|
32
|
+
autoload :CLIAmbiguity, File.join(CLI_FEATURE_ROOT, "ambiguity")
|
|
33
|
+
autoload :CLICoverage, File.join(CLI_FEATURE_ROOT, "coverage")
|
|
34
|
+
autoload :CLIDebug, File.join(CLI_FEATURE_ROOT, "debug")
|
|
35
|
+
autoload :CLIDiagnostics, File.join(CLI_FEATURE_ROOT, "diagnostics")
|
|
36
|
+
autoload :CLIDocumentation, File.join(CLI_FEATURE_ROOT, "documentation")
|
|
37
|
+
autoload :CLIErrorMessages, File.join(CLI_FEATURE_ROOT, "error_messages")
|
|
38
|
+
autoload :CLIExplain, File.join(CLI_FEATURE_ROOT, "explain")
|
|
39
|
+
autoload :CLIFormatting, File.join(CLI_FEATURE_ROOT, "formatting")
|
|
40
|
+
autoload :CLIGrammarTests, File.join(CLI_FEATURE_ROOT, "grammar_tests")
|
|
41
|
+
autoload :CLIIRTools, File.join(CLI_FEATURE_ROOT, "ir_tools")
|
|
42
|
+
autoload :CLILSP, File.join(CLI_FEATURE_ROOT, "lsp")
|
|
43
|
+
autoload :CLIRaccMigration, File.join(CLI_FEATURE_ROOT, "racc_migration")
|
|
44
|
+
autoload :CLISamples, File.join(CLI_FEATURE_ROOT, "samples")
|
|
45
|
+
autoload :CLIWatch, File.join(CLI_FEATURE_ROOT, "watch")
|
|
46
|
+
|
|
9
47
|
# @rbs!
|
|
10
48
|
# interface _CLIOutput
|
|
11
49
|
# def puts: (*untyped) -> untyped
|
|
12
50
|
# def write: (String) -> untyped
|
|
13
51
|
# end
|
|
52
|
+
# interface _CLIInput
|
|
53
|
+
# def read: () -> String
|
|
54
|
+
# def gets: () -> String?
|
|
55
|
+
# end
|
|
14
56
|
# type cli_options = {
|
|
15
57
|
# emit: String,
|
|
16
58
|
# mode: Symbol,
|
|
17
59
|
# table: Symbol,
|
|
60
|
+
# ?cst_trivia: Symbol,
|
|
18
61
|
# line_convert: bool,
|
|
19
62
|
# ?line_convert_all: bool,
|
|
20
63
|
# counterexample_max_tokens: Integer,
|
|
21
64
|
# counterexample_max_configurations: Integer,
|
|
22
65
|
# ?from: String,
|
|
23
66
|
# ?algorithm: Symbol,
|
|
67
|
+
# ?suggest_ielr: bool,
|
|
68
|
+
# ?entry_isolation: bool,
|
|
24
69
|
# ?warnings: Array[Symbol],
|
|
25
70
|
# ?output: String,
|
|
26
71
|
# ?embedded: bool,
|
|
27
72
|
# ?debug: bool,
|
|
28
73
|
# ?verbose: bool,
|
|
29
74
|
# ?rbs: String | true,
|
|
75
|
+
# ?action_source: String | true,
|
|
76
|
+
# ?manifest: String | true,
|
|
77
|
+
# ?watch: bool,
|
|
30
78
|
# ?dot: String,
|
|
79
|
+
# ?mermaid: String,
|
|
31
80
|
# ?html: String,
|
|
81
|
+
# ?railroad: String,
|
|
82
|
+
# ?messages: String,
|
|
83
|
+
# ?messages_update: String | true,
|
|
84
|
+
# ?messages_algorithm_explicit: bool,
|
|
85
|
+
# ?sample_count: Integer,
|
|
86
|
+
# ?sample_seed: Integer,
|
|
87
|
+
# ?sample_max_tokens: Integer,
|
|
88
|
+
# ?sample_max_depth: Integer,
|
|
89
|
+
# ?sample_max_expansions: Integer,
|
|
32
90
|
# ?log_file: String,
|
|
33
91
|
# ?executable: String,
|
|
34
92
|
# ?frozen: bool,
|
|
35
93
|
# ?omit_actions: bool,
|
|
36
94
|
# ?superclass: String,
|
|
95
|
+
# ?verify_output: bool,
|
|
37
96
|
# ?check_only: bool,
|
|
38
97
|
# ?status: bool,
|
|
39
98
|
# ?profile: bool,
|
|
@@ -41,74 +100,179 @@ module Ibex
|
|
|
41
100
|
# ?version: bool,
|
|
42
101
|
# ?runtime_version: bool,
|
|
43
102
|
# ?copyright: bool,
|
|
44
|
-
# ?help: bool
|
|
103
|
+
# ?help: bool,
|
|
104
|
+
# ?explain_state: Integer,
|
|
105
|
+
# ?explain_token: String,
|
|
106
|
+
# ?explain_format: String,
|
|
107
|
+
# ?check_ambiguity: bool,
|
|
108
|
+
# ?check_format: String
|
|
45
109
|
# }
|
|
46
110
|
|
|
47
111
|
# Command-line pipeline coordinator.
|
|
48
112
|
# rubocop:disable Metrics/ClassLength -- inline type contracts add lines without adding runtime responsibilities.
|
|
49
113
|
class CLI
|
|
114
|
+
SUBCOMMAND_HANDLERS = {
|
|
115
|
+
"check" => %i[CLIAmbiguity run_check_command],
|
|
116
|
+
"diagnose" => %i[CLIDiagnostics run_diagnose_command],
|
|
117
|
+
"coverage" => %i[CLICoverage run_coverage_command],
|
|
118
|
+
"debug" => %i[CLIDebug run_debug_command],
|
|
119
|
+
"doc" => %i[CLIDocumentation run_documentation_command],
|
|
120
|
+
"errors" => %i[CLIErrorMessages run_error_messages_command],
|
|
121
|
+
"explain" => %i[CLIExplain run_explain_command],
|
|
122
|
+
"fmt" => %i[CLIFormatting run_format_command],
|
|
123
|
+
"test" => %i[CLIGrammarTests run_grammar_tests_command],
|
|
124
|
+
"lsp" => %i[CLILSP run_lsp_command],
|
|
125
|
+
"migrate-check" => %i[CLIRaccMigration run_migrate_check_command],
|
|
126
|
+
"migrate-harness" => %i[CLIRaccMigration run_migrate_harness_command],
|
|
127
|
+
"samples" => %i[CLISamples run_samples_command],
|
|
128
|
+
"validate-ir" => %i[CLIIRTools run_validate_ir_command],
|
|
129
|
+
"compare" => %i[CLIIRTools run_compare_command],
|
|
130
|
+
"migrate-ir" => %i[CLIIRTools run_migrate_ir_command]
|
|
131
|
+
}.freeze #: Hash[String, [Symbol, Symbol]]
|
|
132
|
+
|
|
50
133
|
include CLICounterexampleOptions
|
|
134
|
+
include CLIGenerationErrorMessages
|
|
135
|
+
include CLIGenerationArtifacts
|
|
51
136
|
include CLIOutputs
|
|
52
137
|
|
|
53
138
|
# @rbs @stdout: _CLIOutput
|
|
54
139
|
# @rbs @stderr: _CLIOutput
|
|
55
140
|
# @rbs @options: cli_options
|
|
56
141
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
142
|
+
# rubocop:disable Layout/LineLength
|
|
143
|
+
# @rbs (Array[String] arguments, ?stdin: _CLIInput, ?stdout: _CLIOutput, ?stderr: _CLIOutput, ?watch_clock: (^() -> Float)?, ?watch_sleeper: (^(Float) -> void)?, ?watch_iteration_hook: (^(Symbol, Integer, Array[String]) -> (Integer | Symbol | nil))?) -> Integer
|
|
144
|
+
def self.start(arguments, stdin: $stdin, stdout: $stdout, stderr: $stderr, watch_clock: nil, watch_sleeper: nil,
|
|
145
|
+
watch_iteration_hook: nil)
|
|
146
|
+
new(
|
|
147
|
+
stdin: stdin, stdout: stdout, stderr: stderr, watch_clock: watch_clock,
|
|
148
|
+
watch_sleeper: watch_sleeper, watch_iteration_hook: watch_iteration_hook
|
|
149
|
+
).run(arguments)
|
|
60
150
|
end
|
|
61
151
|
|
|
62
|
-
# @rbs (stdout: _CLIOutput, stderr: _CLIOutput) -> void
|
|
63
|
-
def initialize(stdout:, stderr:)
|
|
152
|
+
# @rbs (?stdin: _CLIInput, stdout: _CLIOutput, stderr: _CLIOutput, ?watch_clock: (^() -> Float)?, ?watch_sleeper: (^(Float) -> void)?, ?watch_iteration_hook: (^(Symbol, Integer, Array[String]) -> (Integer | Symbol | nil))?) -> void
|
|
153
|
+
def initialize(stdout:, stderr:, stdin: $stdin, watch_clock: nil, watch_sleeper: nil, watch_iteration_hook: nil)
|
|
154
|
+
@stdin = stdin
|
|
64
155
|
@stdout = stdout
|
|
65
156
|
@stderr = stderr
|
|
66
|
-
@
|
|
157
|
+
@watch_clock = watch_clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
158
|
+
@watch_sleeper = watch_sleeper || ->(seconds) { sleep(seconds) }
|
|
159
|
+
@watch_iteration_hook = watch_iteration_hook || ->(_event, _iteration, _paths) {}
|
|
160
|
+
@options = { emit: "ruby", mode: :default, table: :compact, line_convert: true }
|
|
67
161
|
.merge(CLICounterexampleOptions::DEFAULTS)
|
|
68
162
|
end
|
|
163
|
+
# rubocop:enable Layout/LineLength
|
|
69
164
|
|
|
70
165
|
# @rbs (Array[String] arguments) -> Integer
|
|
71
166
|
def run(arguments)
|
|
167
|
+
subcommand = dispatch_subcommand(arguments)
|
|
168
|
+
return subcommand unless subcommand.nil?
|
|
169
|
+
|
|
72
170
|
parser = option_parser
|
|
73
171
|
remaining = parser.parse(arguments)
|
|
172
|
+
validate_watch_information_options
|
|
74
173
|
information = informational_result(parser)
|
|
75
174
|
return information unless information.nil?
|
|
76
175
|
|
|
77
|
-
|
|
78
|
-
|
|
176
|
+
validate_messages_options
|
|
177
|
+
validate_generation_options
|
|
178
|
+
path = input_path(remaining)
|
|
179
|
+
validate_generation_paths!(path)
|
|
180
|
+
if @options[:watch]
|
|
181
|
+
run_watch_feature(path)
|
|
182
|
+
else
|
|
183
|
+
process_grammar(path)
|
|
184
|
+
end
|
|
185
|
+
rescue OptionParser::ParseError, Ibex::Error, SystemCallError, SystemStackError => e
|
|
79
186
|
@stderr.puts(e.message)
|
|
80
187
|
1
|
|
81
188
|
end
|
|
82
189
|
|
|
83
190
|
private
|
|
84
191
|
|
|
192
|
+
# @rbs (Array[String] arguments) -> Integer?
|
|
193
|
+
def dispatch_subcommand(arguments)
|
|
194
|
+
definition = SUBCOMMAND_HANDLERS[arguments.first]
|
|
195
|
+
return unless definition
|
|
196
|
+
|
|
197
|
+
feature, handler = definition
|
|
198
|
+
activate_cli_feature(feature)
|
|
199
|
+
send(handler, arguments.drop(1))
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# @rbs (Symbol feature) -> void
|
|
203
|
+
def activate_cli_feature(feature)
|
|
204
|
+
extension = Ibex.const_get(feature)
|
|
205
|
+
extend extension unless singleton_class.ancestors.include?(extension)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# @rbs (String path) -> Integer
|
|
209
|
+
def run_watch_feature(path)
|
|
210
|
+
activate_cli_feature(:CLIWatch)
|
|
211
|
+
send(:run_watch, path)
|
|
212
|
+
end
|
|
213
|
+
|
|
85
214
|
# @rbs () -> OptionParser
|
|
86
215
|
def option_parser
|
|
87
216
|
OptionParser.new do |options|
|
|
88
217
|
options.banner = "Usage: ibex [options] grammarfile"
|
|
89
218
|
add_pipeline_options(options)
|
|
90
219
|
add_output_options(options)
|
|
220
|
+
add_error_messages_generation_option(options)
|
|
91
221
|
add_compatibility_options(options)
|
|
92
222
|
add_information_options(options)
|
|
223
|
+
options.separator("")
|
|
224
|
+
options.separator("Subcommands:")
|
|
225
|
+
options.separator(" check --ambiguity search for ambiguity within explicit budgets")
|
|
226
|
+
options.separator(" coverage collect, merge, or check runtime coverage")
|
|
227
|
+
options.separator(" debug AUTOMATON [TOKEN] simulate validated Automaton IR tables")
|
|
228
|
+
options.separator(" diagnose collect frontend diagnostics")
|
|
229
|
+
options.separator(" doc render grammar documentation")
|
|
230
|
+
options.separator(" errors --list|--update list or update example-keyed syntax error messages")
|
|
231
|
+
options.separator(" explain explain selected parser conflicts")
|
|
232
|
+
options.separator(" fmt format grammar source")
|
|
233
|
+
options.separator(" test run grammar-declared source examples")
|
|
234
|
+
options.separator(" lsp run the language server over stdio")
|
|
235
|
+
options.separator(" migrate-check statically check a racc grammar migration")
|
|
236
|
+
options.separator(" migrate-harness generate a differential subprocess harness")
|
|
237
|
+
options.separator(" samples generate bounded terminal sentences")
|
|
238
|
+
options.separator(" validate-ir FILE validate a versioned IR document")
|
|
239
|
+
options.separator(" compare BEFORE AFTER compare two versioned IR documents")
|
|
240
|
+
options.separator(" migrate-ir INPUT --to=2 migrate a versioned IR document")
|
|
93
241
|
end
|
|
94
242
|
end
|
|
95
243
|
|
|
96
244
|
# @rbs (OptionParser options) -> void
|
|
97
245
|
def add_pipeline_options(options)
|
|
98
|
-
options.on("--emit=FORMAT", "ast, grammar-ir, automaton-ir, or ruby")
|
|
246
|
+
options.on("--emit=FORMAT", "ast, sets, lexer-ir, grammar-ir, automaton-ir, or ruby") do |value|
|
|
247
|
+
@options[:emit] = value
|
|
248
|
+
end
|
|
99
249
|
options.on("--from=FORMAT", %w[grammar-ir automaton-ir], "resume from IR JSON") do |value|
|
|
100
250
|
@options[:from] = value
|
|
101
251
|
end
|
|
102
|
-
options.on("--mode=MODE", %w[
|
|
252
|
+
options.on("--mode=MODE", %w[default extended], "grammar mode") { |value| @options[:mode] = value.to_sym }
|
|
103
253
|
options.on("--table=FORMAT", %w[plain compact], "parser table format") do |value|
|
|
104
254
|
@options[:table] = value.to_sym
|
|
105
255
|
end
|
|
106
|
-
options.on(
|
|
256
|
+
options.on(
|
|
257
|
+
"--cst-trivia=POLICY",
|
|
258
|
+
%w[leading balanced drop attach],
|
|
259
|
+
"leading, balanced, drop, or attach (alias for leading)"
|
|
260
|
+
) do |value|
|
|
261
|
+
@options[:cst_trivia] = value.to_sym
|
|
262
|
+
end
|
|
263
|
+
options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
|
|
107
264
|
@options[:algorithm] = value.to_sym
|
|
108
265
|
end
|
|
266
|
+
options.on("--suggest-ielr", "check whether IELR removes unexpected LALR conflicts") do
|
|
267
|
+
@options[:suggest_ielr] = true
|
|
268
|
+
end
|
|
269
|
+
options.on("--entry-isolation", "build independent state sets for each start symbol") do
|
|
270
|
+
@options[:entry_isolation] = true
|
|
271
|
+
end
|
|
109
272
|
options.on("--warnings=CATEGORIES", "all, error, all,error, or none") do |value|
|
|
110
273
|
@options[:warnings] = warning_categories(value)
|
|
111
274
|
end
|
|
275
|
+
options.on("--watch", "regenerate file outputs when grammar sources change") { @options[:watch] = true }
|
|
112
276
|
end
|
|
113
277
|
|
|
114
278
|
# @rbs (OptionParser options) -> void
|
|
@@ -119,11 +283,16 @@ module Ibex
|
|
|
119
283
|
options.on("-g", "obsolete alias for --debug") { @options[:debug] = true }
|
|
120
284
|
options.on("-v", "--verbose", "write an automaton report") { @options[:verbose] = true }
|
|
121
285
|
add_counterexample_options(options)
|
|
122
|
-
options
|
|
123
|
-
|
|
286
|
+
add_signature_output_options(options)
|
|
287
|
+
options.on("--manifest[=FILE]", "write a generation manifest (defaults beside parser)") do |value|
|
|
288
|
+
@options[:manifest] = value || true
|
|
124
289
|
end
|
|
125
290
|
options.on("--dot=FILE", "write Graphviz DOT") { |value| @options[:dot] = value }
|
|
291
|
+
options.on("--mermaid=FILE", "write a Mermaid flowchart") { |value| @options[:mermaid] = value }
|
|
126
292
|
options.on("--html=FILE", "write a self-contained HTML report") { |value| @options[:html] = value }
|
|
293
|
+
options.on("--railroad=FILE", "write a self-contained SVG railroad diagram") do |value|
|
|
294
|
+
@options[:railroad] = value
|
|
295
|
+
end
|
|
127
296
|
options.on("-O", "--log-file=FILE", "automaton report path") do |value|
|
|
128
297
|
@options[:verbose] = true
|
|
129
298
|
@options[:log_file] = value
|
|
@@ -133,6 +302,16 @@ module Ibex
|
|
|
133
302
|
end
|
|
134
303
|
end
|
|
135
304
|
|
|
305
|
+
# @rbs (OptionParser options) -> void
|
|
306
|
+
def add_signature_output_options(options)
|
|
307
|
+
options.on("--rbs[=FILE]", "write an RBS signature (defaults beside parser)") do |value|
|
|
308
|
+
@options[:rbs] = value || true
|
|
309
|
+
end
|
|
310
|
+
options.on("--action-source[=FILE]", "write static-check-only action Ruby (defaults beside parser)") do |value|
|
|
311
|
+
@options[:action_source] = value || true
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
136
315
|
# @rbs (OptionParser options) -> void
|
|
137
316
|
def add_compatibility_options(options)
|
|
138
317
|
options.on("-F", "--frozen", "emit frozen string literals") { @options[:frozen] = true }
|
|
@@ -146,6 +325,7 @@ module Ibex
|
|
|
146
325
|
end
|
|
147
326
|
options.on("-a", "--no-omit-actions", "generate implicit action methods") { @options[:omit_actions] = false }
|
|
148
327
|
options.on("--superclass=CLASS", "override parser superclass") { |value| @options[:superclass] = value }
|
|
328
|
+
options.on("--check", "verify generated parser content without rewriting") { @options[:verify_output] = true }
|
|
149
329
|
options.on("-C", "--check-only", "check grammar and exit") { @options[:check_only] = true }
|
|
150
330
|
options.on("-S", "--output-status", "show pipeline status") { @options[:status] = true }
|
|
151
331
|
options.on("-P", "accept the compatibility profiling flag") { @options[:profile] = true }
|
|
@@ -177,34 +357,206 @@ module Ibex
|
|
|
177
357
|
path
|
|
178
358
|
end
|
|
179
359
|
|
|
360
|
+
# @rbs () -> void
|
|
361
|
+
def validate_generation_options
|
|
362
|
+
if @options[:entry_isolation] && @options[:from] == "automaton-ir"
|
|
363
|
+
raise Ibex::Error, "(cli):1:1: --entry-isolation cannot be combined with --from=automaton-ir"
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
validate_watch_generation_options if @options[:watch]
|
|
367
|
+
validate_manifest_generation_options
|
|
368
|
+
validate_action_source_generation_options
|
|
369
|
+
validate_verification_generation_options
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# @rbs () -> void
|
|
373
|
+
def validate_manifest_generation_options
|
|
374
|
+
return unless @options[:manifest]
|
|
375
|
+
|
|
376
|
+
raise Ibex::Error, "(cli):1:1: --manifest requires --emit=ruby" unless @options[:emit] == "ruby"
|
|
377
|
+
raise Ibex::Error, "(cli):1:1: --manifest and --check-only cannot be combined" if @options[:check_only]
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# @rbs () -> void
|
|
381
|
+
def validate_action_source_generation_options
|
|
382
|
+
return unless @options[:action_source]
|
|
383
|
+
|
|
384
|
+
raise Ibex::Error, "(cli):1:1: --action-source requires --emit=ruby" unless @options[:emit] == "ruby"
|
|
385
|
+
raise Ibex::Error, "(cli):1:1: --action-source and --check-only cannot be combined" if @options[:check_only]
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# @rbs () -> void
|
|
389
|
+
def validate_verification_generation_options
|
|
390
|
+
return unless @options[:verify_output]
|
|
391
|
+
|
|
392
|
+
raise Ibex::Error, "(cli):1:1: --check requires --emit=ruby" unless @options[:emit] == "ruby"
|
|
393
|
+
raise Ibex::Error, "(cli):1:1: --check and --check-only cannot be combined" if @options[:check_only]
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# @rbs () -> void
|
|
397
|
+
def validate_watch_information_options
|
|
398
|
+
return unless @options[:watch]
|
|
399
|
+
return unless %i[version runtime_version copyright help].any? { |key| @options[key] }
|
|
400
|
+
|
|
401
|
+
raise Ibex::Error, "(cli):1:1: --watch cannot be combined with information options"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# @rbs () -> void
|
|
405
|
+
def validate_watch_generation_options
|
|
406
|
+
raise Ibex::Error, "(cli):1:1: --watch cannot be combined with --from" if @options[:from]
|
|
407
|
+
raise Ibex::Error, "(cli):1:1: --watch cannot be combined with --check" if @options[:verify_output]
|
|
408
|
+
raise Ibex::Error, "(cli):1:1: --watch cannot be combined with --check-only" if @options[:check_only]
|
|
409
|
+
return if @options[:emit] == "ruby"
|
|
410
|
+
|
|
411
|
+
raise Ibex::Error, "(cli):1:1: --watch requires --emit=ruby with file outputs"
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# @rbs (String input_path, ?source_paths: Array[String]) -> void
|
|
415
|
+
def validate_generation_paths!(input_path, source_paths: [input_path])
|
|
416
|
+
outputs = generation_paths(input_path).except(:input, :messages)
|
|
417
|
+
paths = outputs.filter_map do |kind, path|
|
|
418
|
+
[kind, path] if path
|
|
419
|
+
end #: Array[[Symbol, String]]
|
|
420
|
+
source_entries = source_paths.map.with_index do |path, index|
|
|
421
|
+
[index.zero? ? :input : :"include_#{index}", path]
|
|
422
|
+
end
|
|
423
|
+
source_entries << [:messages, @options[:messages]] if @options[:messages]
|
|
424
|
+
paths.concat(source_entries)
|
|
425
|
+
collision = paths.combination(2).find do |pair|
|
|
426
|
+
left = pair.fetch(0)
|
|
427
|
+
right = pair.fetch(1)
|
|
428
|
+
same_file_target?(left.fetch(1), right.fetch(1))
|
|
429
|
+
end
|
|
430
|
+
return unless collision
|
|
431
|
+
|
|
432
|
+
labels = collision.map { |kind, path| "#{kind}=#{path}" }
|
|
433
|
+
raise Ibex::Error, "(cli):1:1: paths must be distinct: #{labels.join(', ')}"
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# @rbs (String left, String right) -> bool
|
|
437
|
+
def same_file_target?(left, right)
|
|
438
|
+
expanded_left = File.expand_path(left)
|
|
439
|
+
expanded_right = File.expand_path(right)
|
|
440
|
+
return true if expanded_left == expanded_right
|
|
441
|
+
|
|
442
|
+
return true if File.exist?(expanded_left) && File.exist?(expanded_right) &&
|
|
443
|
+
File.identical?(expanded_left, expanded_right)
|
|
444
|
+
|
|
445
|
+
portable_target_key(expanded_left) == portable_target_key(expanded_right)
|
|
446
|
+
rescue SystemCallError
|
|
447
|
+
expanded_left == expanded_right
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# @rbs (String path) -> [String, String]
|
|
451
|
+
def portable_target_key(path)
|
|
452
|
+
canonical = canonical_target_path(path)
|
|
453
|
+
basename = File.basename(canonical)
|
|
454
|
+
folded = if basename.encoding == Encoding::UTF_8 && basename.valid_encoding?
|
|
455
|
+
basename.unicode_normalize(:nfc).downcase(:fold).unicode_normalize(:nfc)
|
|
456
|
+
else
|
|
457
|
+
basename.downcase
|
|
458
|
+
end
|
|
459
|
+
[File.dirname(canonical), folded]
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# @rbs (String path, ?Hash[String, bool] seen) -> String
|
|
463
|
+
def canonical_target_path(path, seen = {})
|
|
464
|
+
suffix = [] #: Array[String]
|
|
465
|
+
cursor = File.expand_path(path)
|
|
466
|
+
until File.exist?(cursor) || File.symlink?(cursor)
|
|
467
|
+
parent = File.dirname(cursor)
|
|
468
|
+
return path if parent == cursor
|
|
469
|
+
|
|
470
|
+
suffix.unshift(File.basename(cursor))
|
|
471
|
+
cursor = parent
|
|
472
|
+
end
|
|
473
|
+
if File.symlink?(cursor)
|
|
474
|
+
raise Errno::ELOOP, cursor if seen[cursor]
|
|
475
|
+
|
|
476
|
+
seen[cursor] = true
|
|
477
|
+
target = File.expand_path(File.readlink(cursor), File.dirname(cursor))
|
|
478
|
+
return File.join(canonical_target_path(target, seen), *suffix)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
File.join(File.realpath(cursor), *suffix)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# @rbs (String input_path) -> Hash[Symbol, String?]
|
|
485
|
+
def generation_paths(input_path)
|
|
486
|
+
paths = { input: input_path, messages: @options[:messages] } #: Hash[Symbol, String?]
|
|
487
|
+
if @options[:emit] == "ruby" && !@options[:check_only]
|
|
488
|
+
output = @options[:output] || default_output_path(input_path, ".rb")
|
|
489
|
+
paths[:parser] = output
|
|
490
|
+
paths[:rbs] = rbs_output_path(output) if @options[:rbs]
|
|
491
|
+
paths[:action_source] = action_source_output_path(output) if @options[:action_source]
|
|
492
|
+
paths[:manifest] = manifest_output_path_for(output) if @options[:manifest]
|
|
493
|
+
end
|
|
494
|
+
unless @options[:verify_output]
|
|
495
|
+
paths.merge!(dot: @options[:dot], mermaid: @options[:mermaid], html: @options[:html],
|
|
496
|
+
railroad: @options[:railroad])
|
|
497
|
+
paths[:report] = @options[:log_file] || default_output_path(input_path, ".output") if @options[:verbose]
|
|
498
|
+
end
|
|
499
|
+
paths
|
|
500
|
+
end
|
|
501
|
+
|
|
180
502
|
# @rbs (String path) -> Integer
|
|
181
503
|
def process_grammar(path)
|
|
182
504
|
return process_ir(path) if @options[:from]
|
|
183
505
|
|
|
506
|
+
begin_artifact_generation
|
|
184
507
|
report_status("reading #{path}")
|
|
185
|
-
|
|
186
|
-
|
|
508
|
+
resolution = resolve_grammar_path(path)
|
|
509
|
+
@generation_inputs = @last_resolver.source_records
|
|
510
|
+
@generation_sources = @generation_inputs.map(&:path)
|
|
511
|
+
validate_generation_paths!(path, source_paths: resolution.files)
|
|
512
|
+
return emit_ast(resolution.root) if @options[:emit] == "ast"
|
|
187
513
|
|
|
188
|
-
grammar = Normalizer.new(
|
|
514
|
+
grammar = Normalizer.new(resolution, mode: @options[:mode]).normalize
|
|
189
515
|
dispatch_grammar(grammar, path)
|
|
190
516
|
end
|
|
191
517
|
|
|
518
|
+
# @rbs (String path) -> Frontend::Resolution
|
|
519
|
+
def resolve_grammar_path(path)
|
|
520
|
+
loader = Frontend::SourceLoader.new(record_reads: true)
|
|
521
|
+
@last_resolver = Frontend::Resolver.new(path, mode: @options[:mode], loader: loader)
|
|
522
|
+
@last_resolver.resolve
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# @rbs (String path) -> IR::Grammar
|
|
526
|
+
def normalize_grammar_path(path)
|
|
527
|
+
Normalizer.new(resolve_grammar_path(path), mode: @options[:mode]).normalize
|
|
528
|
+
end
|
|
529
|
+
|
|
192
530
|
# @rbs (String path) -> Integer
|
|
193
531
|
def process_ir(path)
|
|
532
|
+
require_relative "ir/validator"
|
|
533
|
+
|
|
534
|
+
begin_artifact_generation
|
|
194
535
|
report_status("reading #{path}")
|
|
195
|
-
|
|
536
|
+
source = File.binread(path)
|
|
537
|
+
input = record_generation_input(path, source)
|
|
538
|
+
validate_generation_paths!(path, source_paths: [input.path])
|
|
539
|
+
value = IR::Validator.validate(source)
|
|
196
540
|
expected = @options[:from] == "grammar-ir" ? IR::Grammar : IR::Automaton
|
|
197
541
|
raise Ibex::Error, "#{path}:1:1: expected #{@options[:from]} input" unless value.is_a?(expected)
|
|
198
542
|
|
|
199
543
|
return dispatch_grammar(value, path) if value.is_a?(IR::Grammar)
|
|
544
|
+
return dispatch_automaton(value, path) if value.is_a?(IR::Automaton)
|
|
200
545
|
|
|
201
|
-
|
|
546
|
+
raise Ibex::Error, "#{path}:1:1: expected #{@options[:from]} input"
|
|
202
547
|
end
|
|
203
548
|
|
|
204
549
|
# @rbs (IR::Grammar grammar, String path) -> Integer
|
|
205
550
|
def dispatch_grammar(grammar, path)
|
|
206
551
|
handle_grammar_warnings(grammar, path)
|
|
207
|
-
|
|
552
|
+
if @options[:check_only]
|
|
553
|
+
build_automaton(grammar, path) if @options[:warnings]&.include?(:error)
|
|
554
|
+
return 0
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
write_railroad(grammar) unless @options[:verify_output]
|
|
558
|
+
return emit_sets(grammar) if @options[:emit] == "sets"
|
|
559
|
+
return emit_lexer(grammar) if @options[:emit] == "lexer-ir"
|
|
208
560
|
return emit_grammar(grammar) if @options[:emit] == "grammar-ir"
|
|
209
561
|
return emit_automaton(grammar, path) if @options[:emit] == "automaton-ir"
|
|
210
562
|
return emit_ruby(grammar, path) if @options[:emit] == "ruby"
|
|
@@ -216,6 +568,10 @@ module Ibex
|
|
|
216
568
|
def dispatch_automaton(automaton, path)
|
|
217
569
|
handle_grammar_warnings(automaton.grammar, path)
|
|
218
570
|
return 0 if @options[:check_only]
|
|
571
|
+
|
|
572
|
+
write_railroad(automaton.grammar) unless @options[:verify_output]
|
|
573
|
+
return emit_sets(automaton.grammar) if @options[:emit] == "sets"
|
|
574
|
+
return emit_lexer(automaton.grammar) if @options[:emit] == "lexer-ir"
|
|
219
575
|
return emit_grammar(automaton.grammar) if @options[:emit] == "grammar-ir"
|
|
220
576
|
return emit_loaded_automaton(automaton, path) if @options[:emit] == "automaton-ir"
|
|
221
577
|
|
|
@@ -253,13 +609,40 @@ module Ibex
|
|
|
253
609
|
|
|
254
610
|
# @rbs (IR::Grammar grammar) -> Integer
|
|
255
611
|
def emit_grammar(grammar)
|
|
612
|
+
finish_artifact_generation(@generation_sources)
|
|
256
613
|
@stdout.write(IR::Serialize.dump(grammar))
|
|
257
614
|
0
|
|
258
615
|
end
|
|
259
616
|
|
|
617
|
+
# @rbs (IR::Grammar grammar) -> Integer
|
|
618
|
+
def emit_lexer(grammar)
|
|
619
|
+
lexer = grammar.lexer
|
|
620
|
+
raise Ibex::Error, "(cli):1:1: grammar does not declare a lexer" unless lexer
|
|
621
|
+
|
|
622
|
+
finish_artifact_generation(@generation_sources)
|
|
623
|
+
@stdout.write(IR::Serialize.dump(lexer))
|
|
624
|
+
0
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# @rbs (IR::Grammar grammar) -> Integer
|
|
628
|
+
def emit_sets(grammar)
|
|
629
|
+
sets = Analysis::Sets.new(grammar)
|
|
630
|
+
nonterminals = grammar.nonterminals.sort_by(&:name)
|
|
631
|
+
output = {
|
|
632
|
+
nullable: nonterminals.filter_map { |symbol| symbol.name if sets.nullable?(symbol) },
|
|
633
|
+
first: nonterminals.to_h { |symbol| [symbol.name, sets.first(symbol).sort] },
|
|
634
|
+
follow: nonterminals.to_h { |symbol| [symbol.name, sets.follow(symbol).sort] }
|
|
635
|
+
}
|
|
636
|
+
finish_artifact_generation(@generation_sources)
|
|
637
|
+
@stdout.puts(JSON.pretty_generate(output))
|
|
638
|
+
0
|
|
639
|
+
end
|
|
640
|
+
|
|
260
641
|
# @rbs (IR::Grammar grammar, String input_path) -> Integer
|
|
261
642
|
def emit_automaton(grammar, input_path)
|
|
262
|
-
|
|
643
|
+
automaton = build_automaton(grammar, input_path)
|
|
644
|
+
finish_artifact_generation(@generation_sources)
|
|
645
|
+
@stdout.write(IR::Serialize.dump(automaton))
|
|
263
646
|
0
|
|
264
647
|
end
|
|
265
648
|
|
|
@@ -276,57 +659,143 @@ module Ibex
|
|
|
276
659
|
line_convert: @options.fetch(:line_convert), debug: @options.fetch(:debug, false),
|
|
277
660
|
line_convert_all: @options.fetch(:line_convert_all, false),
|
|
278
661
|
omit_action_call: @options[:omit_actions], superclass: @options[:superclass],
|
|
279
|
-
executable: @options[:executable]
|
|
662
|
+
executable: @options[:executable], cst_trivia: @options.fetch(:cst_trivia, :leading),
|
|
663
|
+
error_messages: configured_error_messages(automaton)
|
|
280
664
|
).generate
|
|
281
665
|
output_path = @options[:output] || default_output_path(input_path, ".rb")
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
666
|
+
action_source = action_source_source(automaton) if @options[:action_source]
|
|
667
|
+
write_action_source(output_path, action_source) if action_source
|
|
668
|
+
register_artifact(:parser, output_path, source, mode: (0o755 if @options[:executable]), status: true)
|
|
285
669
|
write_rbs(automaton, output_path) if @options[:rbs]
|
|
670
|
+
finish_artifact_generation(@generation_sources)
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
# @rbs (IR::Automaton automaton, String output_path, String source, String? action_source) -> Integer
|
|
674
|
+
def verify_generated_outputs(automaton, output_path, source, action_source)
|
|
675
|
+
verify_file(output_path, source, "parser")
|
|
676
|
+
verify_file(rbs_output_path(output_path), rbs_source(automaton), "RBS signature") if @options[:rbs]
|
|
677
|
+
verify_file(action_source_output_path(output_path), action_source, "action source") if action_source
|
|
678
|
+
report_status("verified #{output_path}")
|
|
286
679
|
0
|
|
287
680
|
end
|
|
288
681
|
|
|
682
|
+
# @rbs (String path, String source, String label) -> void
|
|
683
|
+
def verify_file(path, source, label)
|
|
684
|
+
raise Ibex::Error, "#{path}:1:1: generated #{label} is missing" unless File.exist?(path)
|
|
685
|
+
return if File.binread(path) == source
|
|
686
|
+
|
|
687
|
+
raise Ibex::Error, "#{path}:1:1: generated #{label} is stale; regenerate it with the same options"
|
|
688
|
+
end
|
|
689
|
+
|
|
289
690
|
# @rbs (IR::Automaton automaton, String output_path) -> void
|
|
290
691
|
def write_rbs(automaton, output_path)
|
|
692
|
+
path = rbs_output_path(output_path)
|
|
693
|
+
source = rbs_source(automaton)
|
|
694
|
+
register_artifact(:rbs, path, source, status: true)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
# @rbs (String output_path) -> String
|
|
698
|
+
def rbs_output_path(output_path)
|
|
291
699
|
configured_path = @options[:rbs]
|
|
292
700
|
path = configured_path == true ? default_output_path(output_path, ".rbs") : configured_path
|
|
293
701
|
raise ArgumentError, "RBS output path is required" unless path.is_a?(String)
|
|
294
702
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
703
|
+
path
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# @rbs (IR::Automaton automaton) -> String
|
|
707
|
+
def rbs_source(automaton)
|
|
708
|
+
require_relative "codegen/rbs"
|
|
709
|
+
Codegen::RBS.new(
|
|
710
|
+
automaton, superclass: @options[:superclass], omit_action_call: @options[:omit_actions]
|
|
711
|
+
).generate
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# @rbs (String output_path, String source) -> void
|
|
715
|
+
def write_action_source(output_path, source)
|
|
716
|
+
path = action_source_output_path(output_path)
|
|
717
|
+
register_artifact(:action_source, path, source, status: true)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
# @rbs (String output_path) -> String
|
|
721
|
+
def action_source_output_path(output_path)
|
|
722
|
+
configured_path = @options[:action_source]
|
|
723
|
+
path = configured_path == true ? default_output_path(output_path, ".actions.rb") : configured_path
|
|
724
|
+
raise Ibex::Error, "(cli):1:1: action source output path is required" unless path.is_a?(String) && !path.empty?
|
|
725
|
+
|
|
726
|
+
path
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
# @rbs (IR::Automaton automaton) -> String
|
|
730
|
+
def action_source_source(automaton)
|
|
731
|
+
require_relative "codegen/action_source"
|
|
732
|
+
Codegen::ActionSource.new(automaton, omit_action_call: @options[:omit_actions]).generate
|
|
298
733
|
end
|
|
299
734
|
|
|
300
735
|
# @rbs (IR::Automaton automaton, String input_path) -> Integer
|
|
301
736
|
def emit_loaded_automaton(automaton, input_path)
|
|
302
737
|
prepare_loaded_automaton(automaton, input_path)
|
|
738
|
+
finish_artifact_generation(@generation_sources)
|
|
303
739
|
@stdout.write(IR::Serialize.dump(automaton))
|
|
304
740
|
0
|
|
305
741
|
end
|
|
306
742
|
|
|
307
743
|
# @rbs (IR::Grammar grammar, String input_path) -> IR::Automaton
|
|
308
744
|
def build_automaton(grammar, input_path)
|
|
309
|
-
|
|
310
|
-
|
|
745
|
+
algorithm = @options[:algorithm] || :lalr
|
|
746
|
+
report_status("building #{algorithm} automaton")
|
|
747
|
+
automaton = LALR::Builder.new(
|
|
748
|
+
grammar, algorithm: algorithm, entry_isolation: @options[:entry_isolation] == true
|
|
749
|
+
).build
|
|
311
750
|
report_conflicts(automaton, input_path)
|
|
312
|
-
|
|
313
|
-
|
|
751
|
+
suggest_ielr(automaton, input_path)
|
|
752
|
+
write_report(automaton, input_path) if @options[:verbose] && !@options[:verify_output]
|
|
753
|
+
write_visualizations(automaton) unless @options[:verify_output]
|
|
314
754
|
automaton
|
|
315
755
|
end
|
|
316
756
|
|
|
317
757
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
318
758
|
def prepare_loaded_automaton(automaton, input_path)
|
|
319
759
|
report_conflicts(automaton, input_path)
|
|
320
|
-
|
|
321
|
-
|
|
760
|
+
suggest_ielr(automaton, input_path)
|
|
761
|
+
write_report(automaton, input_path) if @options[:verbose] && !@options[:verify_output]
|
|
762
|
+
write_visualizations(automaton) unless @options[:verify_output]
|
|
322
763
|
end
|
|
323
764
|
|
|
324
765
|
# @rbs (IR::Automaton automaton) -> void
|
|
325
766
|
def write_visualizations(automaton)
|
|
326
767
|
dot_path = @options[:dot]
|
|
768
|
+
mermaid_path = @options[:mermaid]
|
|
327
769
|
html_path = @options[:html]
|
|
328
|
-
|
|
329
|
-
|
|
770
|
+
if dot_path
|
|
771
|
+
require_relative "codegen/dot"
|
|
772
|
+
register_artifact(:dot, dot_path, Codegen::Dot.render(automaton))
|
|
773
|
+
end
|
|
774
|
+
if mermaid_path
|
|
775
|
+
require_relative "codegen/mermaid"
|
|
776
|
+
register_artifact(:mermaid, mermaid_path, Codegen::Mermaid.render(automaton))
|
|
777
|
+
end
|
|
778
|
+
return unless html_path
|
|
779
|
+
|
|
780
|
+
require_relative "codegen/html"
|
|
781
|
+
register_artifact(:html, html_path, Codegen::HTML.render(automaton))
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# @rbs (IR::Grammar grammar) -> void
|
|
785
|
+
def write_railroad(grammar)
|
|
786
|
+
path = @options[:railroad]
|
|
787
|
+
return unless path
|
|
788
|
+
|
|
789
|
+
require_relative "codegen/railroad"
|
|
790
|
+
register_artifact(:railroad, path, Codegen::Railroad.render(grammar))
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
# @rbs (String output_path) -> String
|
|
794
|
+
def manifest_output_path_for(output_path)
|
|
795
|
+
configured = @options[:manifest]
|
|
796
|
+
return configured if configured.is_a?(String) && !configured.empty?
|
|
797
|
+
|
|
798
|
+
default_output_path(output_path, ".ibex.json")
|
|
330
799
|
end
|
|
331
800
|
end
|
|
332
801
|
# rubocop:enable Metrics/ClassLength
|