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,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require_relative "../ir"
|
|
5
|
+
|
|
6
|
+
module Ibex
|
|
7
|
+
# @rbs!
|
|
8
|
+
# type migrate_ir_options = {
|
|
9
|
+
# paths: Array[String],
|
|
10
|
+
# ?to: Integer,
|
|
11
|
+
# ?output: String
|
|
12
|
+
# }
|
|
13
|
+
|
|
14
|
+
# CLI validation and structural comparison for versioned IR documents.
|
|
15
|
+
module CLIIRTools
|
|
16
|
+
# @rbs!
|
|
17
|
+
# private def same_file_target?: (String left, String right) -> bool
|
|
18
|
+
# private def atomic_write_ir: (String path, String source) -> void
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
23
|
+
def run_validate_ir_command(arguments)
|
|
24
|
+
path = single_ir_path(arguments, "validate-ir")
|
|
25
|
+
value = IR::Validator.validate(File.read(path))
|
|
26
|
+
kind = if value.is_a?(IR::Grammar)
|
|
27
|
+
"grammar"
|
|
28
|
+
elsif value.is_a?(IR::Lexer)
|
|
29
|
+
"lexer"
|
|
30
|
+
else
|
|
31
|
+
"automaton"
|
|
32
|
+
end
|
|
33
|
+
@stdout.puts("valid #{kind} IR v#{value.schema_version}")
|
|
34
|
+
0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
38
|
+
def run_compare_command(arguments)
|
|
39
|
+
raise Ibex::Error, "(cli):1:1: compare requires exactly two IR files" unless arguments.length == 2
|
|
40
|
+
|
|
41
|
+
before = IR::Validator.validate(File.read(arguments.fetch(0)))
|
|
42
|
+
after = IR::Validator.validate(File.read(arguments.fetch(1)))
|
|
43
|
+
compatible = (before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)) ||
|
|
44
|
+
(before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)) ||
|
|
45
|
+
(before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer))
|
|
46
|
+
raise Ibex::Error, "(cli):1:1: cannot compare different IR kinds" unless compatible
|
|
47
|
+
|
|
48
|
+
@stdout.puts(JSON.pretty_generate(compare_ir(before, after)))
|
|
49
|
+
0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
53
|
+
def run_migrate_ir_command(arguments)
|
|
54
|
+
settings = migrate_ir_options(arguments)
|
|
55
|
+
input = single_ir_path(settings.fetch(:paths), "migrate-ir")
|
|
56
|
+
output = settings[:output]
|
|
57
|
+
if output && same_file_target?(input, output)
|
|
58
|
+
raise Ibex::Error, "(cli):1:1: migrate-ir input and output paths must be distinct"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
value = IR::Validator.validate(File.read(input))
|
|
62
|
+
target = settings[:to] || raise(Ibex::Error, "(cli):1:1: migrate-ir requires --to=VERSION")
|
|
63
|
+
if value.is_a?(IR::Lexer)
|
|
64
|
+
raise Ibex::Error, "(cli):1:1: lexer IR is already at its only supported schema version" unless
|
|
65
|
+
target == value.schema_version
|
|
66
|
+
|
|
67
|
+
source = IR::Serialize.dump(value)
|
|
68
|
+
output ? atomic_write_ir(output, source) : @stdout.write(source)
|
|
69
|
+
return 0
|
|
70
|
+
end
|
|
71
|
+
unless IR::SUPPORTED_SCHEMA_VERSIONS.include?(target)
|
|
72
|
+
raise Ibex::Error, "(cli):1:1: unsupported migration target schema_version #{target}"
|
|
73
|
+
end
|
|
74
|
+
if target < value.schema_version
|
|
75
|
+
raise Ibex::Error,
|
|
76
|
+
"(cli):1:1: downgrading schema_version #{value.schema_version} to #{target} is not supported"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
source = IR::Serialize.dump(IR::Migration.to_version(value, to: target))
|
|
80
|
+
output ? atomic_write_ir(output, source) : @stdout.write(source)
|
|
81
|
+
0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @rbs (Array[String] arguments) -> migrate_ir_options
|
|
85
|
+
def migrate_ir_options(arguments)
|
|
86
|
+
settings = { paths: [] } #: migrate_ir_options
|
|
87
|
+
parser = OptionParser.new do |options|
|
|
88
|
+
options.banner = "Usage: ibex migrate-ir INPUT --to=VERSION [-o FILE]"
|
|
89
|
+
options.on("--to=VERSION", Integer, "target schema version") { |value| settings[:to] = value }
|
|
90
|
+
options.on("-o FILE", "--output=FILE", "write atomically to FILE") { |value| settings[:output] = value }
|
|
91
|
+
end
|
|
92
|
+
settings[:paths] = parser.parse(arguments)
|
|
93
|
+
settings
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @rbs (Array[String] arguments, String command) -> String
|
|
97
|
+
def single_ir_path(arguments, command)
|
|
98
|
+
return arguments.first if arguments.length == 1
|
|
99
|
+
|
|
100
|
+
raise Ibex::Error, "(cli):1:1: #{command} requires exactly one IR file"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @rbs (IR::Grammar | IR::Automaton | IR::Lexer before,
|
|
104
|
+
# IR::Grammar | IR::Automaton | IR::Lexer after) -> Hash[Symbol, untyped]
|
|
105
|
+
def compare_ir(before, after)
|
|
106
|
+
return compare_grammars(before, after) if before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)
|
|
107
|
+
if before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)
|
|
108
|
+
return {
|
|
109
|
+
kind: "automaton",
|
|
110
|
+
algorithm: { before: before.algorithm, after: after.algorithm },
|
|
111
|
+
states: numeric_change(before.states.length, after.states.length),
|
|
112
|
+
transitions: numeric_change(transition_count(before), transition_count(after)),
|
|
113
|
+
conflicts: %i[sr resolved_sr rr].to_h do |kind|
|
|
114
|
+
[kind, numeric_change(summary_count(before, kind), summary_count(after, kind))]
|
|
115
|
+
end,
|
|
116
|
+
grammar: compare_grammars(before.grammar, after.grammar)
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
return compare_lexers(before, after) if before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer)
|
|
120
|
+
|
|
121
|
+
raise Ibex::Error, "(cli):1:1: unsupported IR comparison"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @rbs (IR::Lexer before, IR::Lexer after) -> Hash[Symbol, untyped]
|
|
125
|
+
def compare_lexers(before, after)
|
|
126
|
+
before_rules = before.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
|
|
127
|
+
after_rules = after.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
|
|
128
|
+
{
|
|
129
|
+
kind: "lexer",
|
|
130
|
+
states: { added: after.states - before.states, removed: before.states - after.states },
|
|
131
|
+
rules: {
|
|
132
|
+
added: after_rules - before_rules, removed: before_rules - after_rules,
|
|
133
|
+
count: numeric_change(before.rules.length, after.rules.length)
|
|
134
|
+
},
|
|
135
|
+
warnings: numeric_change(before.warnings.length, after.warnings.length)
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @rbs (IR::Grammar before, IR::Grammar after) -> Hash[Symbol, untyped]
|
|
140
|
+
def compare_grammars(before, after)
|
|
141
|
+
before_symbols = before.symbols.map(&:name)
|
|
142
|
+
after_symbols = after.symbols.map(&:name)
|
|
143
|
+
before_productions = production_shapes(before)
|
|
144
|
+
after_productions = production_shapes(after)
|
|
145
|
+
{
|
|
146
|
+
kind: "grammar",
|
|
147
|
+
symbols: { added: (after_symbols - before_symbols).sort, removed: (before_symbols - after_symbols).sort },
|
|
148
|
+
productions: {
|
|
149
|
+
added: (after_productions - before_productions).sort,
|
|
150
|
+
removed: (before_productions - after_productions).sort,
|
|
151
|
+
count: numeric_change(before.productions.length, after.productions.length)
|
|
152
|
+
},
|
|
153
|
+
warnings: numeric_change(before.warnings.length, after.warnings.length)
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @rbs (IR::Grammar grammar) -> Array[String]
|
|
158
|
+
def production_shapes(grammar)
|
|
159
|
+
grammar.productions.map do |production|
|
|
160
|
+
lhs = grammar.symbol_by_id(production.lhs)&.name || production.lhs.to_s
|
|
161
|
+
rhs = production.rhs.map { |id| grammar.symbol_by_id(id)&.name || id.to_s }
|
|
162
|
+
"#{lhs} -> #{rhs.join(' ')}"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @rbs (IR::Automaton automaton) -> Integer
|
|
167
|
+
def transition_count(automaton)
|
|
168
|
+
automaton.states.sum { |state| state.transitions.length }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @rbs (IR::Automaton automaton, Symbol kind) -> Integer
|
|
172
|
+
def summary_count(automaton, kind)
|
|
173
|
+
value = automaton.conflict_summary.fetch(kind)
|
|
174
|
+
return value if value.is_a?(Integer)
|
|
175
|
+
|
|
176
|
+
raise Ibex::Error, "(ir):1:1: conflict count #{kind} must be an integer"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @rbs (Integer before, Integer after) -> Hash[Symbol, Integer]
|
|
180
|
+
def numeric_change(before, after)
|
|
181
|
+
{ before: before, after: after, delta: after - before }
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
data/lib/ibex/cli/lsp.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../lsp"
|
|
4
|
+
|
|
5
|
+
module Ibex
|
|
6
|
+
# CLI boundary for the stdio-only language server.
|
|
7
|
+
module CLILSP
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
11
|
+
def run_lsp_command(arguments)
|
|
12
|
+
settings = { help: false, stdio: false } #: Hash[Symbol, bool]
|
|
13
|
+
remaining = lsp_option_parser(settings).parse(arguments)
|
|
14
|
+
raise Ibex::Error, "(cli):1:1: ibex lsp does not accept file arguments" unless remaining.empty?
|
|
15
|
+
|
|
16
|
+
if settings.fetch(:help)
|
|
17
|
+
@stdout.puts(lsp_option_parser(settings))
|
|
18
|
+
return 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
LSP::Server.new(stdin: @stdin, stdout: @stdout, stderr: @stderr).run
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs (Hash[Symbol, bool] settings) -> OptionParser
|
|
25
|
+
def lsp_option_parser(settings)
|
|
26
|
+
OptionParser.new do |options|
|
|
27
|
+
options.banner = "Usage: ibex lsp [--stdio]"
|
|
28
|
+
options.on("--stdio", "use Content-Length framed JSON-RPC on standard IO") { settings[:stdio] = true }
|
|
29
|
+
options.on("--help", "show help") { settings[:help] = true }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/ibex/cli/outputs.rb
CHANGED
|
@@ -3,14 +3,24 @@
|
|
|
3
3
|
module Ibex
|
|
4
4
|
# Files, warnings, and progress emitted by CLI pipeline stages.
|
|
5
5
|
module CLIOutputs
|
|
6
|
+
# @rbs!
|
|
7
|
+
# private def register_artifact: (Symbol, String, String, ?mode: Integer?, ?status: bool) -> Artifact
|
|
8
|
+
|
|
6
9
|
WARNING_MESSAGES = {
|
|
7
10
|
undeclared_terminal: ->(warning) { "undeclared terminal #{warning[:symbol]}" },
|
|
8
11
|
unused_terminal: ->(warning) { "unused terminal #{warning[:symbol]}" },
|
|
12
|
+
unused_precedence: ->(warning) { "unused precedence #{warning[:symbol]}" },
|
|
13
|
+
unreachable_terminal: ->(warning) { "declared terminal #{warning[:symbol]} is unreachable" },
|
|
9
14
|
unreachable_nonterminal: ->(warning) { "unreachable nonterminal #{warning[:symbol]}" },
|
|
10
15
|
duplicate_production: lambda do |warning|
|
|
11
16
|
"duplicate production #{warning[:production]} (first defined as #{warning[:original]})"
|
|
12
17
|
end,
|
|
13
|
-
|
|
18
|
+
implicit_empty: ->(_warning) { "implicit empty alternative; write %empty to document intent" },
|
|
19
|
+
empty_language: ->(warning) { "start symbol #{warning[:symbol]} derives no terminal sentence" },
|
|
20
|
+
lexer_redos: lambda do |warning|
|
|
21
|
+
subject = warning[:symbol] ? " for #{warning[:symbol]}" : ""
|
|
22
|
+
"lexer pattern#{subject} may exhibit excessive backtracking"
|
|
23
|
+
end
|
|
14
24
|
}.freeze #: Hash[Symbol, ^(IR::grammar_warning) -> String]
|
|
15
25
|
|
|
16
26
|
private
|
|
@@ -55,34 +65,107 @@ module Ibex
|
|
|
55
65
|
|
|
56
66
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
57
67
|
def report_conflicts(automaton, input_path)
|
|
58
|
-
|
|
68
|
+
messages = conflict_messages(automaton.conflict_summary, input_path)
|
|
69
|
+
if @options[:warnings]&.include?(:error) && messages.any?
|
|
70
|
+
raise Ibex::Error, messages.map { |message| "#{message}; conflict treated as error" }.join("\n")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
messages.each { |message| @stderr.puts(message) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @rbs (IR::conflict_summary summary, String input_path) -> Array[String]
|
|
77
|
+
def conflict_messages(summary, input_path)
|
|
78
|
+
messages = [] #: Array[String]
|
|
59
79
|
unless summary[:expectation_met]
|
|
60
|
-
|
|
80
|
+
messages << "#{input_path}:1:1: #{summary[:sr]} shift/reduce conflicts; expected #{summary[:expected_sr]}"
|
|
81
|
+
end
|
|
82
|
+
if summary.key?(:rr_expectation_met)
|
|
83
|
+
unless summary[:rr_expectation_met]
|
|
84
|
+
messages << "#{input_path}:1:1: #{summary[:rr]} reduce/reduce conflicts; expected #{summary[:expected_rr]}"
|
|
85
|
+
end
|
|
86
|
+
elsif summary[:rr].positive?
|
|
87
|
+
messages << "#{input_path}:1:1: #{summary[:rr]} reduce/reduce conflicts"
|
|
61
88
|
end
|
|
62
|
-
|
|
89
|
+
messages
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
93
|
+
def suggest_ielr(automaton, input_path)
|
|
94
|
+
return unless @options[:suggest_ielr]
|
|
95
|
+
return unless automaton.algorithm == "lalr1"
|
|
96
|
+
return unless [nil, :lalr].include?(@options[:algorithm])
|
|
97
|
+
|
|
98
|
+
summary = automaton.conflict_summary
|
|
99
|
+
return if summary[:expectation_met] && summary.fetch(:rr_expectation_met, summary[:rr].zero?)
|
|
100
|
+
|
|
101
|
+
ielr = LALR::Builder.new(
|
|
102
|
+
automaton.grammar, algorithm: :ielr, entry_isolation: @options[:entry_isolation] == true
|
|
103
|
+
).build
|
|
104
|
+
removed_sr = [summary[:sr] - ielr.conflict_summary[:sr], 0].max
|
|
105
|
+
removed_rr = [summary[:rr] - ielr.conflict_summary[:rr], 0].max
|
|
106
|
+
avoided = [] #: Array[String]
|
|
107
|
+
avoided << conflict_count(removed_sr, "shift/reduce") if removed_sr.positive?
|
|
108
|
+
avoided << conflict_count(removed_rr, "reduce/reduce") if removed_rr.positive?
|
|
109
|
+
return if avoided.empty?
|
|
110
|
+
|
|
111
|
+
@stderr.puts("#{input_path}:1:1: note: --algorithm=ielr avoids #{avoided.join(' and ')}; " \
|
|
112
|
+
"consider --algorithm=ielr")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @rbs (Integer count, String kind) -> String
|
|
116
|
+
def conflict_count(count, kind)
|
|
117
|
+
"#{count} #{kind} conflict#{'s' unless count == 1}"
|
|
63
118
|
end
|
|
64
119
|
|
|
65
120
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
66
121
|
def write_report(automaton, input_path)
|
|
122
|
+
require_relative "../codegen/report"
|
|
67
123
|
path = @options[:log_file] || default_output_path(input_path, ".output")
|
|
68
124
|
report = Codegen::Report.render(
|
|
69
125
|
automaton,
|
|
70
126
|
max_tokens: @options[:counterexample_max_tokens],
|
|
71
127
|
max_configurations: @options[:counterexample_max_configurations]
|
|
72
128
|
)
|
|
73
|
-
|
|
74
|
-
report_status("wrote #{path}")
|
|
129
|
+
register_artifact(:report, path, report, status: true)
|
|
75
130
|
end
|
|
76
131
|
|
|
77
132
|
# @rbs (String input_path, String extension) -> String
|
|
78
133
|
def default_output_path(input_path, extension)
|
|
79
|
-
|
|
80
|
-
|
|
134
|
+
directory = File.dirname(input_path)
|
|
135
|
+
basename = File.basename(input_path)
|
|
136
|
+
replaced = basename.sub(/\.[^.]+\z/, extension)
|
|
137
|
+
replaced = "#{basename}#{extension}" if replaced == basename
|
|
138
|
+
directory == "." ? replaced : File.join(directory, replaced)
|
|
81
139
|
end
|
|
82
140
|
|
|
83
141
|
# @rbs (String message) -> void
|
|
84
142
|
def report_status(message)
|
|
85
143
|
@stderr.puts("ibex: #{message}") if @options[:status]
|
|
86
144
|
end
|
|
145
|
+
|
|
146
|
+
# @rbs (String path, String source) -> void
|
|
147
|
+
def atomic_write_ir(path, source)
|
|
148
|
+
require "tempfile"
|
|
149
|
+
|
|
150
|
+
target_path = File.symlink?(path) ? File.realpath(path) : path
|
|
151
|
+
directory = File.dirname(File.expand_path(target_path))
|
|
152
|
+
basename = File.basename(target_path)
|
|
153
|
+
Tempfile.create([".#{basename}.", ".tmp"], directory) do |temporary|
|
|
154
|
+
temporary.binmode
|
|
155
|
+
temporary.write(source)
|
|
156
|
+
temporary.flush
|
|
157
|
+
temporary.fsync
|
|
158
|
+
temporary.chmod(ir_file_mode(target_path))
|
|
159
|
+
temporary.close
|
|
160
|
+
File.rename(temporary.path, target_path)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @rbs (String path) -> Integer
|
|
165
|
+
def ir_file_mode(path)
|
|
166
|
+
return File.stat(path).mode & 0o777 if File.exist?(path)
|
|
167
|
+
|
|
168
|
+
0o666 & ~File.umask
|
|
169
|
+
end
|
|
87
170
|
end
|
|
88
171
|
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "optparse"
|
|
6
|
+
require_relative "../racc_migration"
|
|
7
|
+
|
|
8
|
+
module Ibex
|
|
9
|
+
# Static racc migration checks and differential harness generation.
|
|
10
|
+
module CLIRaccMigration
|
|
11
|
+
# @rbs!
|
|
12
|
+
# type migration_check_settings = {
|
|
13
|
+
# format: String,
|
|
14
|
+
# ?help: bool
|
|
15
|
+
# }
|
|
16
|
+
# type migration_harness_settings = {
|
|
17
|
+
# ?output: String,
|
|
18
|
+
# ?help: bool
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# private def input_path: (Array[String]) -> String
|
|
22
|
+
# private def atomic_write_ir: (String path, String source) -> void
|
|
23
|
+
# private def same_file_target?: (String left, String right) -> bool
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
28
|
+
def run_migrate_check_command(arguments)
|
|
29
|
+
settings = { format: "text" } #: migration_check_settings
|
|
30
|
+
parser = migrate_check_options(settings)
|
|
31
|
+
paths = parser.parse(arguments)
|
|
32
|
+
if settings[:help]
|
|
33
|
+
@stdout.puts(parser)
|
|
34
|
+
return 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
path = input_path(paths)
|
|
38
|
+
report = RaccMigration::Checker.new.check(File.binread(path), file: path)
|
|
39
|
+
@stdout.write(settings.fetch(:format) == "json" ? "#{report.to_json}\n" : report.to_text)
|
|
40
|
+
report.compatible? ? 0 : 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
44
|
+
def run_migrate_harness_command(arguments)
|
|
45
|
+
settings = {} #: migration_harness_settings
|
|
46
|
+
parser = migrate_harness_options(settings)
|
|
47
|
+
paths = parser.parse(arguments)
|
|
48
|
+
if settings[:help]
|
|
49
|
+
@stdout.puts(parser)
|
|
50
|
+
return 0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
path = input_path(paths)
|
|
54
|
+
report = RaccMigration::Checker.new.check(File.binread(path), file: path)
|
|
55
|
+
unless report.compatible? && report.class_name
|
|
56
|
+
raise Ibex::Error, "(migration):1:1: grammar must pass migrate-check before harness generation"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
source = RaccMigration::Harness.generate(report.class_name)
|
|
60
|
+
output = settings[:output]
|
|
61
|
+
if output && same_file_target?(path, output)
|
|
62
|
+
raise Ibex::Error, "(migration):1:1: grammar and harness output paths must be distinct"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
output ? atomic_write_ir(output, source) : @stdout.write(source)
|
|
66
|
+
0
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @rbs (migration_check_settings settings) -> OptionParser
|
|
70
|
+
def migrate_check_options(settings)
|
|
71
|
+
OptionParser.new do |options|
|
|
72
|
+
options.banner = "Usage: ibex migrate-check [--format=text|json] grammarfile"
|
|
73
|
+
options.on("--format=FORMAT", %w[text json], "text or json") { |value| settings[:format] = value }
|
|
74
|
+
options.on("--help", "show help") { settings[:help] = true }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @rbs (migration_harness_settings settings) -> OptionParser
|
|
79
|
+
def migrate_harness_options(settings)
|
|
80
|
+
OptionParser.new do |options|
|
|
81
|
+
options.banner = "Usage: ibex migrate-harness [-o FILE] grammarfile"
|
|
82
|
+
options.on("-o FILE", "--output=FILE", "write the harness atomically") { |value| settings[:output] = value }
|
|
83
|
+
options.on("--help", "show help") { settings[:help] = true }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "optparse"
|
|
5
|
+
require_relative "../samples"
|
|
6
|
+
|
|
7
|
+
module Ibex
|
|
8
|
+
# CLI entry point for bounded grammar sentence generation.
|
|
9
|
+
module CLISamples
|
|
10
|
+
# @rbs!
|
|
11
|
+
# private def print_help: (OptionParser) -> Integer
|
|
12
|
+
# private def input_path: (Array[String]) -> String
|
|
13
|
+
# private def handle_grammar_warnings: (IR::Grammar, String) -> void
|
|
14
|
+
# private def warning_categories: (String) -> Array[Symbol]
|
|
15
|
+
# private def normalize_grammar_path: (String) -> IR::Grammar
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# @rbs (Array[String] arguments) -> Integer
|
|
20
|
+
def run_samples_command(arguments)
|
|
21
|
+
parser = samples_option_parser
|
|
22
|
+
remaining = parser.parse(arguments)
|
|
23
|
+
return print_help(parser) if @options[:help]
|
|
24
|
+
|
|
25
|
+
path = input_path(remaining)
|
|
26
|
+
grammar = grammar_for_samples(path)
|
|
27
|
+
handle_grammar_warnings(grammar, path)
|
|
28
|
+
generator = Samples.new(
|
|
29
|
+
grammar,
|
|
30
|
+
seed: @options.fetch(:sample_seed, 0),
|
|
31
|
+
max_tokens: @options.fetch(:sample_max_tokens, 32),
|
|
32
|
+
max_depth: @options.fetch(:sample_max_depth, 16),
|
|
33
|
+
max_expansions: @options.fetch(:sample_max_expansions, Samples::DEFAULT_MAX_EXPANSIONS)
|
|
34
|
+
)
|
|
35
|
+
generator.generate(count: @options.fetch(:sample_count, 5)).each do |sample|
|
|
36
|
+
@stdout.puts(JSON.generate(sample))
|
|
37
|
+
end
|
|
38
|
+
0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @rbs () -> OptionParser
|
|
42
|
+
def samples_option_parser
|
|
43
|
+
OptionParser.new do |options|
|
|
44
|
+
options.banner = "Usage: ibex samples [options] grammarfile"
|
|
45
|
+
options.on("--count=N", Integer, "number of samples (default 5)") do |value|
|
|
46
|
+
@options[:sample_count] = positive_sample_option!("count", value)
|
|
47
|
+
end
|
|
48
|
+
options.on("--seed=N", Integer, "deterministic random seed (default 0)") do |value|
|
|
49
|
+
@options[:sample_seed] = value
|
|
50
|
+
end
|
|
51
|
+
options.on("--max-tokens=N", Integer, "maximum tokens per sample (default 32)") do |value|
|
|
52
|
+
@options[:sample_max_tokens] = positive_sample_option!("max-tokens", value)
|
|
53
|
+
end
|
|
54
|
+
options.on("--max-depth=N", Integer, "random expansion depth (default 16)") do |value|
|
|
55
|
+
@options[:sample_max_depth] = positive_sample_option!("max-depth", value)
|
|
56
|
+
end
|
|
57
|
+
options.on("--max-expansions=N", Integer, "total expansion steps (default 100000)") do |value|
|
|
58
|
+
@options[:sample_max_expansions] = positive_sample_option!("max-expansions", value)
|
|
59
|
+
end
|
|
60
|
+
options.on("--from=FORMAT", %w[grammar-ir automaton-ir], "read versioned IR JSON") do |value|
|
|
61
|
+
@options[:from] = value
|
|
62
|
+
end
|
|
63
|
+
options.on("--mode=MODE", %w[default extended], "grammar mode") { |value| @options[:mode] = value.to_sym }
|
|
64
|
+
options.on("--warnings=CATEGORIES", "all, error, all,error, or none") do |value|
|
|
65
|
+
@options[:warnings] = warning_categories(value)
|
|
66
|
+
end
|
|
67
|
+
options.on("--help", "show help") { @options[:help] = true }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @rbs (String path) -> IR::Grammar
|
|
72
|
+
def grammar_for_samples(path)
|
|
73
|
+
return normalize_grammar_path(path) unless @options[:from]
|
|
74
|
+
|
|
75
|
+
value = IR::Validator.validate(File.read(path))
|
|
76
|
+
expected = @options[:from] == "grammar-ir" ? IR::Grammar : IR::Automaton
|
|
77
|
+
raise Ibex::Error, "#{path}:1:1: expected #{@options[:from]} input" unless value.is_a?(expected)
|
|
78
|
+
|
|
79
|
+
return value if value.is_a?(IR::Grammar)
|
|
80
|
+
return value.grammar if value.is_a?(IR::Automaton)
|
|
81
|
+
|
|
82
|
+
raise Ibex::Error, "#{path}:1:1: expected #{@options[:from]} input"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @rbs (String name, Integer value) -> Integer
|
|
86
|
+
def positive_sample_option!(name, value)
|
|
87
|
+
return value if value.positive?
|
|
88
|
+
|
|
89
|
+
raise OptionParser::InvalidArgument, "--#{name} must be positive"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative "../watch"
|
|
5
|
+
require_relative "../generation_transaction"
|
|
6
|
+
|
|
7
|
+
module Ibex
|
|
8
|
+
# Long-running CLI generation over stable source snapshots.
|
|
9
|
+
module CLIWatch
|
|
10
|
+
# @rbs!
|
|
11
|
+
# private def process_grammar: (String) -> Integer
|
|
12
|
+
# private def generation_artifacts: () -> ArtifactSet
|
|
13
|
+
# private def report_status: (String) -> void
|
|
14
|
+
|
|
15
|
+
# Rendered watch generation awaiting its stability-guarded publication.
|
|
16
|
+
class Build
|
|
17
|
+
attr_reader :artifacts #: ArtifactSet
|
|
18
|
+
attr_reader :statuses #: Array[String]
|
|
19
|
+
attr_reader :source_paths #: Array[String]
|
|
20
|
+
attr_reader :attempted_paths #: Array[String]
|
|
21
|
+
attr_reader :source_records #: Array[GenerationInput]
|
|
22
|
+
|
|
23
|
+
# rubocop:disable Layout/LineLength
|
|
24
|
+
# @rbs (artifacts: ArtifactSet, statuses: Array[String], source_paths: Array[String], attempted_paths: Array[String], source_records: Array[GenerationInput]) -> void
|
|
25
|
+
def initialize(artifacts:, statuses:, source_paths:, attempted_paths:, source_records:)
|
|
26
|
+
@artifacts = artifacts
|
|
27
|
+
@statuses = statuses.freeze
|
|
28
|
+
@source_paths = source_paths.freeze
|
|
29
|
+
@attempted_paths = attempted_paths.freeze
|
|
30
|
+
@source_records = source_records.freeze
|
|
31
|
+
freeze
|
|
32
|
+
end
|
|
33
|
+
# rubocop:enable Layout/LineLength
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# @rbs (String path) -> Integer
|
|
39
|
+
def run_watch(path)
|
|
40
|
+
raise Ibex::Error, "(cli):1:1: --watch requires a grammar file, not stdin" if path == "-"
|
|
41
|
+
|
|
42
|
+
paths = [path]
|
|
43
|
+
paths << @options[:messages] if @options[:messages]
|
|
44
|
+
Watch::Runner.new(
|
|
45
|
+
paths: paths,
|
|
46
|
+
build: -> { prepare_watch_generation(path) },
|
|
47
|
+
publish: ->(build, snapshot, continue) { publish_watch_generation(build, snapshot, continue) },
|
|
48
|
+
failure_paths: -> { watch_failure_paths(path) },
|
|
49
|
+
stderr: @stderr,
|
|
50
|
+
clock: @watch_clock,
|
|
51
|
+
sleeper: @watch_sleeper,
|
|
52
|
+
iteration_hook: @watch_iteration_hook
|
|
53
|
+
).run
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @rbs (String path) -> Build
|
|
57
|
+
def prepare_watch_generation(path)
|
|
58
|
+
@defer_generation_publication = true
|
|
59
|
+
status = process_grammar(path)
|
|
60
|
+
raise Ibex::Error, "(watch):1:1: generation returned status #{status}" unless status.zero?
|
|
61
|
+
|
|
62
|
+
Build.new(
|
|
63
|
+
artifacts: generation_artifacts,
|
|
64
|
+
statuses: @generation_statuses.dup,
|
|
65
|
+
source_paths: @generation_sources.dup,
|
|
66
|
+
attempted_paths: @last_resolver&.attempted_paths || [],
|
|
67
|
+
source_records: @generation_inputs.dup
|
|
68
|
+
)
|
|
69
|
+
ensure
|
|
70
|
+
@defer_generation_publication = false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @rbs (Build build, Watch::SourceSnapshot snapshot, ^() -> bool continue) -> void
|
|
74
|
+
def publish_watch_generation(build, snapshot, continue)
|
|
75
|
+
stable = lambda do
|
|
76
|
+
continue.call && build.source_records.all?(&:current?) &&
|
|
77
|
+
Watch::SourceSnapshot.new(snapshot.paths) == snapshot
|
|
78
|
+
end
|
|
79
|
+
GenerationTransaction.new(
|
|
80
|
+
build.artifacts,
|
|
81
|
+
warning: ->(message) { @stderr.puts("ibex: warning: #{message}") },
|
|
82
|
+
stability_check: stable,
|
|
83
|
+
source_records: build.source_records,
|
|
84
|
+
lock_sleeper: @watch_sleeper
|
|
85
|
+
).commit
|
|
86
|
+
build.statuses.each { |message| report_status(message) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @rbs (String path) -> Array[String]
|
|
90
|
+
def watch_failure_paths(path)
|
|
91
|
+
paths = [path]
|
|
92
|
+
paths << @options[:messages] if @options[:messages]
|
|
93
|
+
paths.concat(@last_resolver.attempted_paths) if @last_resolver
|
|
94
|
+
unless @generation_artifacts.nil?
|
|
95
|
+
generation_artifacts.each do |artifact|
|
|
96
|
+
paths << artifact.path
|
|
97
|
+
paths << File.dirname(artifact.path)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
paths.compact
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|