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/docs/racc-migration.md
CHANGED
|
@@ -5,11 +5,14 @@ copy racc's internal table arrays, internal method names, native runtime, or gen
|
|
|
5
5
|
|
|
6
6
|
## Typical migration
|
|
7
7
|
|
|
8
|
-
1. Run `ibex -
|
|
9
|
-
2.
|
|
8
|
+
1. Run `ibex migrate-check grammar.y` and resolve any errors. Use `--format=json` for CI.
|
|
9
|
+
2. Generate a reviewable parity harness with `ibex migrate-harness -o migration_harness.rb grammar.y`, add explicit token
|
|
10
|
+
cases, and run it inside an isolation boundary appropriate for the grammar code.
|
|
11
|
+
3. Run `ibex -o parser.rb grammar.y` in place of `racc -o parser.rb grammar.y`.
|
|
12
|
+
4. Change the generated-file runtime dependency from deployment packaging only; application calls to `do_parse`, `yyparse`,
|
|
10
13
|
`next_token`, `on_error`, `token_to_str`, `yyerror`, `yyerrok`, and `yyaccept` remain the same.
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
5. Use `-E` if the generated parser must be a single file with no installed Ibex gem.
|
|
15
|
+
6. Keep the default `--mode=default` until intentionally adopting EBNF or names. Extended grammars can make that choice locally by
|
|
13
16
|
placing `pragma extended` immediately after their class header instead of requiring `--mode=extended` at each invocation.
|
|
14
17
|
|
|
15
18
|
## CLI mapping
|
|
@@ -23,6 +26,7 @@ copy racc's internal table arrays, internal method names, native runtime, or gen
|
|
|
23
26
|
| `-E`, `--embedded` | Embed the Pure Ruby runtime |
|
|
24
27
|
| `-F`, `--frozen` | Accepted; Ibex always emits frozen-string magic comments |
|
|
25
28
|
| `--rbs[=FILE]` | Ibex extension; emit a generated parser signature |
|
|
29
|
+
| `--action-source[=FILE]` | Ibex extension; emit a non-executable Steep shadow of semantic methods |
|
|
26
30
|
| `--warnings=all,error` | Ibex extension; display or promote structured grammar diagnostics |
|
|
27
31
|
| `--line-convert-all`, `-l` | Map header/inner/footer too, or disable all source mapping |
|
|
28
32
|
| `-a` | Generate methods for implicit actions |
|
|
@@ -34,6 +38,32 @@ copy racc's internal table arrays, internal method names, native runtime, or gen
|
|
|
34
38
|
Ibex defaults to `<input>.rb`; racc 1.8.1 was observed to default to `<input>.tab.rb`. Use `-o` for portable scripts.
|
|
35
39
|
By default, semantic actions and `inner` methods report grammar-file lines while `header` and `footer` retain generated-file
|
|
36
40
|
lines. `--line-convert-all` maps every user-code section; `-l` maps none. The mapping is retained through IR JSON resumption.
|
|
41
|
+
For static action checking, combine `--rbs --action-source`; configure the generated `.rbs` and `.actions.rb` in the
|
|
42
|
+
application's Steep target, and run Steep separately. The shadow is check-only input and must not replace or be required by the
|
|
43
|
+
runtime parser.
|
|
44
|
+
|
|
45
|
+
`migrate-check` never executes semantic actions or `header`/`inner`/`footer` code. `migrate-harness` also only writes source. The
|
|
46
|
+
generated harness is the explicit execution step: after reviewed cases are added, it invokes both generators and executes both
|
|
47
|
+
generated parsers in bounded child processes. This is not a sandbox; use a container or VM for untrusted grammar code. See
|
|
48
|
+
[ADR 0012](decisions/0012-bounded-nonexecuting-analysis.md).
|
|
49
|
+
|
|
50
|
+
## Compatibility baseline
|
|
51
|
+
|
|
52
|
+
The compatibility claims above were checked on 2026-07-22 against racc 1.8.1 using only its public documentation, `racc --help`,
|
|
53
|
+
and black-box execution. Ibex does not inspect racc implementation files or generated source. Self-authored probes compare
|
|
54
|
+
observable results for arithmetic precedence, empty rules, string tokens, `convert`, `no_result_var`, inline actions,
|
|
55
|
+
dangling-else `expect`, error recovery, source-line conversion, and a generated 500-production grammar.
|
|
56
|
+
|
|
57
|
+
Precedence-resolved conflicts remain visible in Automaton IR but are excluded from the CLI conflict count and `expect`. Error
|
|
58
|
+
recovery probes compare result values and the public `on_error` arguments. These tests skip when the `racc` executable is absent.
|
|
59
|
+
|
|
60
|
+
Application-defined parser initializers do not have to call `super`; the runtime completes its isolated session state on first
|
|
61
|
+
use while retaining application-owned instance variables. During parsing, the historical `@vstack` and `@racc_vstack` names
|
|
62
|
+
both refer to the live semantic-value stack for read compatibility. They are internal aliases: applications may inspect them
|
|
63
|
+
while tokenizing but must not mutate, replace, or retain them across parser sessions.
|
|
64
|
+
|
|
65
|
+
An unqualified `ParseError` in a parser method or semantic action resolves through `Runtime::Parser`, so application actions can
|
|
66
|
+
keep their existing explicit rejection path. Structured failures remain available as `Ibex::Runtime::ParseError`.
|
|
37
67
|
|
|
38
68
|
## Known differences
|
|
39
69
|
|
|
@@ -43,5 +73,3 @@ lines. `--line-convert-all` maps every user-code section; `-l` maps none. The ma
|
|
|
43
73
|
unknown lookahead through `on_error` first, then recovers if the callback returns. Declared invalid tokens match in the
|
|
44
74
|
black-box recovery probe.
|
|
45
75
|
- `require "racc/parser"` replacement and previously generated racc parser table compatibility are out of scope.
|
|
46
|
-
|
|
47
|
-
See [compatibility observations](compat-notes.md) for the tested version and probe set.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# v1.0 readiness report
|
|
2
|
+
|
|
3
|
+
This is the outcome-based v1.0 decision required by the project design. It
|
|
4
|
+
records measurements taken on 2026-07-27 with Ruby 4.0.0 on
|
|
5
|
+
`arm64-darwin24`. Passing the implementation checklist is not sufficient for a
|
|
6
|
+
stable release.
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
**HOLD v1.0.** Compatibility behavior, scale construction, semantic-value
|
|
11
|
+
signatures, and the repository compatibility suite have evidence. The external
|
|
12
|
+
generator/runtime performance target is not met, and the published ten-case
|
|
13
|
+
error comparison has not received independent third-party review.
|
|
14
|
+
|
|
15
|
+
The feature freeze remains in force while those two release gates are open.
|
|
16
|
+
The format-v6 batch Red/Green CST has been selected as part of the initial
|
|
17
|
+
Stable v1 API. Its syntax-only incremental layer remains Experimental. Other Preview and
|
|
18
|
+
Experimental features may continue to ship in prereleases without promotion.
|
|
19
|
+
|
|
20
|
+
## KPI result
|
|
21
|
+
|
|
22
|
+
| KPI | Result | Evidence |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Three public-gem migrations | Pass with documented adapters | Namae, BCDice, and Nokogiri behavior suites below |
|
|
25
|
+
| Hundreds-of-productions scale | Pass | 501 productions, 503 states, 125.008 ms average complete build |
|
|
26
|
+
| Ten-case error UX comparison | Partial | 10/10 snapshots and 8/10 useful repairs are public; independent review is missing |
|
|
27
|
+
| Generator and parser performance at least racc | Fail | Reuse medians are at parity, but only Nokogiri's interval passes; all cold-generation and new-instance rows fail |
|
|
28
|
+
| Semantic-value RBS and typed ratchet | Pass | Generated reduction signatures include declared RHS/LHS types; whole-library Steep is 89.6% typed |
|
|
29
|
+
| Compatibility suite unbeaten | Pass at the measured revision | Current black-box, self-host, IR, property, and runtime suites are green |
|
|
30
|
+
|
|
31
|
+
## Public-gem migration evidence
|
|
32
|
+
|
|
33
|
+
Only public grammar files, public commands, and observable test results were
|
|
34
|
+
used. Neither racc implementation files nor generated source were inspected.
|
|
35
|
+
Each repository was cloned at the recorded revision:
|
|
36
|
+
|
|
37
|
+
| Project | Revision and grammar | Static check | Behavior result |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| Namae | [`d33875a`](https://github.com/berkmancenter/namae/commit/d33875aaf1fc420a8dfe946a3b29cc3e19710061), `lib/namae/parser.y` | compatible | 151 RSpec examples, 85 Cucumber scenarios, 198 steps |
|
|
40
|
+
| BCDice | [`21b4a03`](https://github.com/bcdice/BCDice/commit/21b4a03789bf2080ad41aaf31299b609ee7bda86), `lib/bcdice/command/parser.y` | compatible; runtime-constant warning | 25 tests, 103 assertions |
|
|
41
|
+
| Nokogiri | [`04a4c29`](https://github.com/sparklemotion/nokogiri/commit/04a4c29c6a605ad40a78f4ce343ced0832a1805c), `lib/nokogiri/css/parser.y` | compatible; runtime-constant warning | 6 tests, 14 assertions |
|
|
42
|
+
|
|
43
|
+
Namae generated and ran as one embedded file. The other two grammars contain a
|
|
44
|
+
user header whose superclass is named `Racc::Parser`. Their PoCs preload this
|
|
45
|
+
explicit migration adapter before loading the generated parser:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
require "ibex/runtime"
|
|
49
|
+
|
|
50
|
+
module Racc
|
|
51
|
+
Parser = Ibex::Runtime::Parser
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This is the adapter recommended by `ibex migrate-check`; it does not load or
|
|
56
|
+
execute the racc runtime. The PoCs uncovered and fixed three compatibility
|
|
57
|
+
gaps: an omitted `end` before a user-code section, an application initializer
|
|
58
|
+
that omits `super` plus historical value-stack reads, and an unqualified
|
|
59
|
+
`ParseError`.
|
|
60
|
+
|
|
61
|
+
## Formal Pure Ruby performance comparison
|
|
62
|
+
|
|
63
|
+
The formal artifact was collected from clean revision `3fda78c` with ten
|
|
64
|
+
alternating isolated runs per implementation, 50 warm-up workloads, 250
|
|
65
|
+
measured workloads, and 10,000 bootstrap samples. Each workload parses the
|
|
66
|
+
five fixed public inputs. Ruby 4.0.0 ran with YJIT disabled; Racc 1.8.1 was
|
|
67
|
+
forced to its Ruby backend and every worker verified the selected runtime.
|
|
68
|
+
Ratios below are Ibex divided by Racc. Bold values meet the gate, whose timing
|
|
69
|
+
and allocation metrics require the bootstrap interval's upper bound to be no
|
|
70
|
+
greater than 1.0.
|
|
71
|
+
|
|
72
|
+
| Project | Cold generation time | Reuse time | Reuse allocations | New-instance time | New-instance allocations | Generated bytes |
|
|
73
|
+
|---|---:|---:|---:|---:|---:|---:|
|
|
74
|
+
| Namae | 1.194x | 0.994x | **0.849x** | 1.185x | 1.208x | **0.862x** |
|
|
75
|
+
| BCDice command | 1.204x | 0.995x | **0.666x** | 1.258x | 1.129x | **0.981x** |
|
|
76
|
+
| Nokogiri CSS | 1.187x | **0.953x** | **0.662x** | 1.167x | 1.075x | **0.937x** |
|
|
77
|
+
|
|
78
|
+
Result values and ordered result sequences are equivalent in every runtime
|
|
79
|
+
row. The measured parser-time medians in milliseconds per parse were
|
|
80
|
+
0.038783/0.039015 (Ibex/Racc) for Namae reuse,
|
|
81
|
+
0.019542/0.019646 for BCDice reuse, and
|
|
82
|
+
0.024074/0.025269 for Nokogiri reuse. The corresponding new-instance medians
|
|
83
|
+
were 0.047493/0.040076, 0.025937/0.020618, and 0.031041/0.026609.
|
|
84
|
+
|
|
85
|
+
The remaining runtime gap is isolated to parser-instance construction and the
|
|
86
|
+
per-instance mutation tracker; the reused hot loop's point estimates are at
|
|
87
|
+
practical parity, although Namae and BCDice still need narrower passing
|
|
88
|
+
intervals. Cold generation remains 18.7–20.4% slower and is dominated by
|
|
89
|
+
process/load, frontend, and code-generation setup. These are evidence for the
|
|
90
|
+
release decision, not portable scores.
|
|
91
|
+
|
|
92
|
+
## Superseded native-backend diagnostic
|
|
93
|
+
|
|
94
|
+
The previous artifact was collected from clean revision `53aa4ab` with ten
|
|
95
|
+
alternating isolated runs per implementation, 50 warm-up workloads, 250
|
|
96
|
+
measured workloads, and 10,000 bootstrap samples. Each workload parses the
|
|
97
|
+
five fixed public inputs. Ruby 4.0.0 ran with YJIT disabled; racc 1.8.1 was
|
|
98
|
+
verified to use its native backend. The formal release contract now compares
|
|
99
|
+
the two Pure Ruby runtimes, so these stricter native-backend measurements are
|
|
100
|
+
retained only as diagnostic context and do not decide the KPI. Ratios below
|
|
101
|
+
are Ibex divided by racc.
|
|
102
|
+
|
|
103
|
+
| Project | Cold generation time | Reuse time | Reuse allocations | New-instance time | New-instance allocations | Generated bytes |
|
|
104
|
+
|---|---:|---:|---:|---:|---:|---:|
|
|
105
|
+
| Namae | 1.210x | 1.177x | **0.970x** | 1.408x | 1.376x | **0.862x** |
|
|
106
|
+
| BCDice command | 1.211x | 1.943x | **0.852x** | 2.445x | 1.430x | **0.981x** |
|
|
107
|
+
| Nokogiri CSS | 1.236x | 1.562x | **0.881x** | 1.993x | 1.418x | **0.937x** |
|
|
108
|
+
|
|
109
|
+
Result values and ordered result sequences are equivalent in every runtime
|
|
110
|
+
row. The measured parser-time medians in milliseconds per parse were
|
|
111
|
+
0.038938/0.033094 (Ibex/racc) for Namae reuse,
|
|
112
|
+
0.020118/0.010354 for BCDice reuse, and
|
|
113
|
+
0.024413/0.015626 for Nokogiri reuse. The corresponding new-instance medians
|
|
114
|
+
were 0.046953/0.033340, 0.026933/0.011016, and 0.030744/0.015426.
|
|
115
|
+
|
|
116
|
+
Profiles attribute the remaining reuse gap to the Pure Ruby direct LR loop
|
|
117
|
+
after lexer and semantic-action costs. New-instance rows additionally include
|
|
118
|
+
the per-instance tracker that preserves runtime hook mutation semantics.
|
|
119
|
+
Cold-generation profiles are dominated by process/load cost plus frontend and
|
|
120
|
+
code-generation work. These are diagnostic optimization evidence, not
|
|
121
|
+
portable scores or formal release evidence.
|
|
122
|
+
|
|
123
|
+
## Scale evidence
|
|
124
|
+
|
|
125
|
+
Run:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
benchmark/scale.rb --rules 500 --iterations 3
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The reproducible synthetic chain shares its grammar shape with the 500-rule
|
|
132
|
+
black-box compatibility test. The measured report contains 501 productions,
|
|
133
|
+
503 direct-LALR construction states, 503 final states, no conflicts, and
|
|
134
|
+
41,332 bytes of compact generated Ruby. Three complete parse-to-codegen builds
|
|
135
|
+
averaged 125.008 ms with a 122.742–127.002 ms range. The Automaton IR digest
|
|
136
|
+
was `fe0e2f0c9bf16e7aa22a7ebcea40b229fedbb573f83af37596b76e0b989c3338`.
|
|
137
|
+
|
|
138
|
+
This synthetic result establishes the requested scale bound. The 139-production
|
|
139
|
+
representative grammar remains the realistic workload, so neither result is
|
|
140
|
+
presented as a substitute for an application-specific benchmark.
|
|
141
|
+
|
|
142
|
+
## Error UX and type evidence
|
|
143
|
+
|
|
144
|
+
[`error-ux.md`](error-ux.md) and its versioned JSON publish the same ten
|
|
145
|
+
malformed JSON inputs against the public racc callback. Eight selected repairs
|
|
146
|
+
were assessed useful. A maintainer assessment is not independent review; at
|
|
147
|
+
least one external reviewer must record a review of the cases and usefulness
|
|
148
|
+
labels before this KPI passes.
|
|
149
|
+
|
|
150
|
+
The current whole-library `steep stats` result is 18,115 typed calls and 2,097
|
|
151
|
+
untyped calls out of 20,212, or 89.6% typed. Generated parser RBS refines
|
|
152
|
+
declared terminal, nonterminal, RHS tuple, and reduction result types. Untyped
|
|
153
|
+
values remain explicit at undeclared grammar symbols, decoded JSON, dynamic
|
|
154
|
+
table cells, and opaque application Ruby boundaries.
|
|
155
|
+
|
|
156
|
+
## Actions required to release v1.0
|
|
157
|
+
|
|
158
|
+
1. Optimize cold generation and parser-instance construction, then repeat the
|
|
159
|
+
formal Pure Ruby measurements until no row is slower than Racc.
|
|
160
|
+
2. Obtain and link an independent review of the ten error cases and usefulness
|
|
161
|
+
judgments; revise the versioned assessment if the review disagrees.
|
|
162
|
+
3. Re-run every compatibility, IR, type, benchmark, and site gate on the exact
|
|
163
|
+
release revision.
|
data/docs/stability.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Stability, compatibility, and deprecation
|
|
2
|
+
|
|
3
|
+
Ibex separates support level from activation. Opt-in controls do not determine
|
|
4
|
+
maturity: a documented opt-in API can be Stable, Preview, or Experimental.
|
|
5
|
+
|
|
6
|
+
## Maturity ladder
|
|
7
|
+
|
|
8
|
+
| Level | Activation | Guarantee |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Stable | Default compatible mode or a documented stable API | Semantic versioning; compatible-mode behavior remains unchanged |
|
|
11
|
+
| Preview | Explicit pragma, mode, command, or algorithm | Breaking changes require notice one minor release in advance |
|
|
12
|
+
| Experimental | Explicit policy/object or research entry point | May change without notice; budgets and failure modes are part of the experiment |
|
|
13
|
+
|
|
14
|
+
After v1.0, promotion requires representative use in a shadow or gallery
|
|
15
|
+
grammar, feature-specific invariant/property tests, two released versions
|
|
16
|
+
without a specification change, dependent-tool support where applicable, and
|
|
17
|
+
complete public documentation. For the initial v1.0 contract, accepted ADRs,
|
|
18
|
+
versioned benchmark evidence, invariant/property coverage, dependent-tool
|
|
19
|
+
support where applicable, and complete public documentation replace the
|
|
20
|
+
impossible two-prior-release requirement. Only Stable features may be adopted
|
|
21
|
+
by the production self-hosted grammar.
|
|
22
|
+
|
|
23
|
+
## v1 inventory
|
|
24
|
+
|
|
25
|
+
Stable:
|
|
26
|
+
|
|
27
|
+
- racc-compatible grammar input and generated `do_parse`/`yyparse` runtime;
|
|
28
|
+
- default direct LALR construction, parser tables, recovery callbacks,
|
|
29
|
+
observation events, resource limits, migration checks, and bounded
|
|
30
|
+
counterexample/ambiguity analysis;
|
|
31
|
+
- versioned core Grammar IR, Automaton IR, Lexer IR, table formats, report
|
|
32
|
+
schemas, and their validators;
|
|
33
|
+
- format-v6 Red/Green batch CST parsing, typed syntax views, persistent editing
|
|
34
|
+
and diffing, and the closed `ibex_cst` schema v1 serialization contract.
|
|
35
|
+
|
|
36
|
+
Preview:
|
|
37
|
+
|
|
38
|
+
- `pragma extended` / `--mode=extended`, including EBNF groups, parameterized
|
|
39
|
+
and inline rules, middle actions, multiple entries, canonical imports,
|
|
40
|
+
generated lexers, semantic locations/types, AST generation, grammar tests,
|
|
41
|
+
and documentation tooling;
|
|
42
|
+
- the conservative `--algorithm=ielr` backend;
|
|
43
|
+
- LSP, watch, debug, coverage, browser playground, and static action-shadow
|
|
44
|
+
integration.
|
|
45
|
+
|
|
46
|
+
Experimental:
|
|
47
|
+
|
|
48
|
+
- opt-in bounded insertion/deletion/replacement repair through
|
|
49
|
+
`Runtime::RepairPolicy`;
|
|
50
|
+
- syntax-only incremental CST sessions and conservative Blender subtree reuse.
|
|
51
|
+
|
|
52
|
+
The batch CST contract is selected for the initial v1 API under the
|
|
53
|
+
initial-major evidence rule above. The remaining Preview features have not
|
|
54
|
+
completed the normal two-release field period. Repair passed its usefulness
|
|
55
|
+
spike but remains Experimental because two of ten baseline plans were not
|
|
56
|
+
useful and inserted semantic values may be nil.
|
|
57
|
+
|
|
58
|
+
## Research decisions
|
|
59
|
+
|
|
60
|
+
- IELR passed its correctness/state-bound spike and remains preview while it
|
|
61
|
+
gains field experience. Direct LALR remains the default.
|
|
62
|
+
- Bounded repair passed SP-4 with 8/10 useful plans and remains experimental.
|
|
63
|
+
- GLR and `%dprec`/`%merge` did not enter the product: no delayed-action spike
|
|
64
|
+
met the ≤5% deterministic-overhead and ambiguity-policy gates.
|
|
65
|
+
- Syntax-only incremental parsing entered as experimental after fixed-seed
|
|
66
|
+
structural edits matched fresh Green trees and the representative benchmark
|
|
67
|
+
measured 1.04–2.83x over Stage A and 1.66–4.48x over fresh syntax sessions.
|
|
68
|
+
It remains experimental until it completes the two-release field period.
|
|
69
|
+
|
|
70
|
+
The supported alternatives are canonical LR(1) or IELR for LALR inadequacy,
|
|
71
|
+
bounded ambiguity/counterexample analysis for ambiguous grammars, and
|
|
72
|
+
`--watch` with full deterministic reparsing for editing workflows.
|
|
73
|
+
|
|
74
|
+
## Core IR freeze
|
|
75
|
+
|
|
76
|
+
The required fields, meanings, identity rules, ordering, and validation
|
|
77
|
+
semantics of the published core Grammar IR and Automaton IR versions are
|
|
78
|
+
frozen. Existing version-1 and version-2 documents retain byte-stable
|
|
79
|
+
round-trips and one-generation read compatibility.
|
|
80
|
+
|
|
81
|
+
Additive optional core fields require a minor release. Meaning changes,
|
|
82
|
+
required-field changes, or removals require a new major schema version and
|
|
83
|
+
continued reading of the immediately preceding major version.
|
|
84
|
+
|
|
85
|
+
The `x-` namespace is reserved for future experimental data and is not frozen.
|
|
86
|
+
Current closed schemas intentionally reject unknown fields and do not emit
|
|
87
|
+
`x-` data. Introducing an experimental envelope therefore requires a new
|
|
88
|
+
additive schema version; it cannot silently place fields into an existing
|
|
89
|
+
closed document. Promotion moves reviewed data into a documented core field in
|
|
90
|
+
a later schema version.
|
|
91
|
+
|
|
92
|
+
## Compatibility policy
|
|
93
|
+
|
|
94
|
+
Compatible mode is the permanent default. Opt-in extensions, exact lookahead
|
|
95
|
+
defaults, repair, and research algorithms do not silently replace compatible
|
|
96
|
+
behavior. Public migration evidence uses public commands and black-box
|
|
97
|
+
behavior; implementation and generated-source layouts are not compatibility
|
|
98
|
+
surfaces.
|
|
99
|
+
|
|
100
|
+
An undeclared invalid token intentionally calls `on_error` before ordinary yacc
|
|
101
|
+
recovery. This is the documented recommended behavior and is not changed by
|
|
102
|
+
the freeze.
|
|
103
|
+
|
|
104
|
+
Parser-table formats v1 through v6 remain readable for non-CST parsers. CST
|
|
105
|
+
tables must use the current format v6 structured metadata; older CST tables
|
|
106
|
+
and the boolean `cst: true` shape fail before token consumption with a
|
|
107
|
+
regeneration instruction. Format v6 is the only writer and does not change
|
|
108
|
+
Grammar IR v2. The closed `ibex_cst` schema v1 is a versioned interchange
|
|
109
|
+
contract. Additive meaning requires a new schema version; readers do not
|
|
110
|
+
accept unknown fields.
|
|
111
|
+
|
|
112
|
+
## Deprecation policy
|
|
113
|
+
|
|
114
|
+
After v1.0, a Stable API or syntax first emits a migration warning for at least
|
|
115
|
+
two minor releases. The release notes and documentation must name the first
|
|
116
|
+
warning release, replacement, migration command or procedure, and earliest
|
|
117
|
+
removal release. Automated migration is supplied when practical; policy does
|
|
118
|
+
not promise a command that the product does not provide. Preview features
|
|
119
|
+
receive at least one minor release of notice, and Experimental features may
|
|
120
|
+
change without notice.
|
|
121
|
+
|
|
122
|
+
The pre-v1 mixed semantic/syntax CST was a Preview contract and is removed
|
|
123
|
+
while selecting the initial stable API. Its parser tables are rejected with a
|
|
124
|
+
regeneration instruction. There are no Stable removals scheduled.
|
data/examples/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Ibex examples
|
|
2
|
+
|
|
3
|
+
These examples pair an Ibex grammar with either the declarative lexer DSL or a
|
|
4
|
+
small handwritten lexer. Generate any example from the repository
|
|
5
|
+
root, then run the generated file:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bundle exec ruby -Ilib exe/ibex examples/calculator.y
|
|
9
|
+
bundle exec ruby -Ilib examples/calculator.rb "2 + 3 * (4 - 1)"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The generated `.rb` file is disposable. The `.y` file is the maintained
|
|
13
|
+
source.
|
|
14
|
+
|
|
15
|
+
Each grammar also keeps accept/reject source examples beside its rules. Run all
|
|
16
|
+
of them without generating repository files. The task also requires 100%
|
|
17
|
+
production coverage from the declared cases:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
bundle exec rake grammar:test
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Included grammars
|
|
24
|
+
|
|
25
|
+
- `calculator.y` evaluates integer arithmetic with parentheses.
|
|
26
|
+
- `csv.y` parses rows and quoted fields with a generated lexer.
|
|
27
|
+
- `json.y` parses JSON values into Ruby Hashes, Arrays, Strings, numbers,
|
|
28
|
+
booleans, and `nil`. It demonstrates the generated lexer, including conversion
|
|
29
|
+
actions and punctuation emission.
|
|
30
|
+
- `ini.y` parses sections and key/value entries into a nested Hash.
|
|
31
|
+
- `tiny_language.y` parses assignments, arithmetic, and `print` statements,
|
|
32
|
+
then executes the resulting small AST.
|
|
33
|
+
|
|
34
|
+
The JSON, INI, and tiny-language runners read standard input:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
printf '%s\n' '{"name":"Ibex","values":[1,true,null]}' |
|
|
38
|
+
bundle exec ruby -Ilib examples/json.rb
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Lexer integration patterns
|
|
42
|
+
|
|
43
|
+
The JSON example declares `lexer ... end`; generated `parse` accepts String,
|
|
44
|
+
IO, and Fiber input. Rules are anchored, choose the longest match, and use
|
|
45
|
+
declaration order for ties.
|
|
46
|
+
|
|
47
|
+
Handwritten lexers remain useful when an application already owns tokenization.
|
|
48
|
+
They implement `next_token`, returning `[token, semantic_value]`; `false` or
|
|
49
|
+
`nil` marks EOF. The remaining examples demonstrate two `StringScanner`
|
|
50
|
+
patterns:
|
|
51
|
+
|
|
52
|
+
1. calculator and tiny language scan the source incrementally and
|
|
53
|
+
return one token per `next_token` call;
|
|
54
|
+
2. INI tokenizes line-oriented records into a small queue before parsing.
|
|
55
|
+
|
|
56
|
+
Quoted grammar terminals such as `'+'` are returned as Strings. Declared bare
|
|
57
|
+
tokens such as `NUMBER` are returned as Symbols. A production lexer can retain
|
|
58
|
+
offset or line information alongside its own semantic values and use that
|
|
59
|
+
information in application diagnostics; these examples keep the contract
|
|
60
|
+
small enough to read in one file.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
class Examples::CalculatorParser
|
|
2
|
+
pragma extended
|
|
3
|
+
token NUMBER
|
|
4
|
+
%test accept "1+2*3"
|
|
5
|
+
%test accept "8-3"
|
|
6
|
+
%test accept "8/2"
|
|
7
|
+
%test accept "(1)"
|
|
8
|
+
%test reject "1+"
|
|
9
|
+
rule
|
|
10
|
+
expression : expression '+' term { result = val[0] + val[2] }
|
|
11
|
+
| expression '-' term { result = val[0] - val[2] }
|
|
12
|
+
| term { result = val[0] }
|
|
13
|
+
term : term '*' factor { result = val[0] * val[2] }
|
|
14
|
+
| term '/' factor { result = val[0] / val[2] }
|
|
15
|
+
| factor { result = val[0] }
|
|
16
|
+
factor : NUMBER { result = val[0] }
|
|
17
|
+
| '(' expression ')' { result = val[1] }
|
|
18
|
+
end
|
|
19
|
+
---- header
|
|
20
|
+
require "strscan"
|
|
21
|
+
---- inner
|
|
22
|
+
def parse(source)
|
|
23
|
+
@scanner = StringScanner.new(source)
|
|
24
|
+
do_parse
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def next_token
|
|
28
|
+
@scanner.skip(/\s+/)
|
|
29
|
+
return false if @scanner.eos?
|
|
30
|
+
|
|
31
|
+
if (number = @scanner.scan(/\d+/))
|
|
32
|
+
[:NUMBER, Integer(number, 10)]
|
|
33
|
+
elsif (operator = @scanner.scan(/[()+*\/-]/))
|
|
34
|
+
[operator, nil]
|
|
35
|
+
else
|
|
36
|
+
raise ArgumentError, "unexpected calculator input at offset #{@scanner.pos}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
---- footer
|
|
40
|
+
if $PROGRAM_NAME == __FILE__
|
|
41
|
+
abort "usage: ruby calculator.rb EXPRESSION" if ARGV.empty?
|
|
42
|
+
|
|
43
|
+
puts Examples::CalculatorParser.new.parse(ARGV.join(" "))
|
|
44
|
+
end
|
data/examples/csv.y
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Examples::CSVParser
|
|
2
|
+
pragma extended
|
|
3
|
+
token FIELD NEWLINE
|
|
4
|
+
%test accept "name,age\n\"Doe, Jane\",36\n"
|
|
5
|
+
%test reject "name,age"
|
|
6
|
+
lexer
|
|
7
|
+
FIELD /"(?:[^"]|"")*"|[^,\r\n]+/ { |source| decode_csv_field(source) }
|
|
8
|
+
NEWLINE /\r\n|\n|\r/
|
|
9
|
+
on /,/ { |source| emit source, nil }
|
|
10
|
+
end
|
|
11
|
+
rule
|
|
12
|
+
document : rows { result = val[0] }
|
|
13
|
+
rows : rows row { result = val[0] + [val[1]] }
|
|
14
|
+
| row { result = [val[0]] }
|
|
15
|
+
row : fields NEWLINE { result = val[0] }
|
|
16
|
+
fields : fields ',' field { result = val[0] + [val[2]] }
|
|
17
|
+
| field { result = [val[0]] }
|
|
18
|
+
field : FIELD { result = val[0] }
|
|
19
|
+
end
|
|
20
|
+
---- inner
|
|
21
|
+
def parse(source, file: "(csv)") = super
|
|
22
|
+
|
|
23
|
+
def decode_csv_field(source)
|
|
24
|
+
return source unless source.start_with?('"')
|
|
25
|
+
|
|
26
|
+
source[1...-1].gsub('""', '"')
|
|
27
|
+
end
|
|
28
|
+
---- footer
|
|
29
|
+
if $PROGRAM_NAME == __FILE__
|
|
30
|
+
require "json"
|
|
31
|
+
puts JSON.generate(Examples::CSVParser.new.parse(ARGF.read))
|
|
32
|
+
end
|
data/examples/ini.y
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class Examples::INIParser
|
|
2
|
+
pragma extended
|
|
3
|
+
token SECTION KEY VALUE NEWLINE
|
|
4
|
+
%test accept "[app]\nname=ibex\n"
|
|
5
|
+
%test accept "\n"
|
|
6
|
+
rule
|
|
7
|
+
document : lines { result = build_document(val[0]) }
|
|
8
|
+
lines : lines line { result = val[1] ? val[0] + [val[1]] : val[0] }
|
|
9
|
+
| { result = [] }
|
|
10
|
+
line : SECTION NEWLINE { result = [:section, val[0]] }
|
|
11
|
+
| KEY '=' VALUE NEWLINE { result = [:entry, val[0], val[2]] }
|
|
12
|
+
| NEWLINE { result = nil }
|
|
13
|
+
end
|
|
14
|
+
---- header
|
|
15
|
+
require "strscan"
|
|
16
|
+
---- inner
|
|
17
|
+
def parse(source)
|
|
18
|
+
@tokens = tokenize_ini(source)
|
|
19
|
+
do_parse
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def next_token
|
|
23
|
+
@tokens.shift || false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def tokenize_ini(source)
|
|
27
|
+
source.lines.flat_map.with_index(1) do |line, line_number|
|
|
28
|
+
scanner = StringScanner.new(line.chomp)
|
|
29
|
+
scanner.skip(/\s*/)
|
|
30
|
+
if scanner.eos? || scanner.peek(1).match?(/[;#]/)
|
|
31
|
+
[[:NEWLINE, nil]]
|
|
32
|
+
elsif scanner.scan(/\[/)
|
|
33
|
+
name = scanner.scan(/[^\]]+/)&.strip
|
|
34
|
+
closer = scanner.scan(/\]/)
|
|
35
|
+
scanner.skip(/\s*/)
|
|
36
|
+
invalid_ini_line!(line_number) unless name && !name.empty? && closer && scanner.eos?
|
|
37
|
+
[[:SECTION, name], [:NEWLINE, nil]]
|
|
38
|
+
else
|
|
39
|
+
key = scanner.scan(/[A-Za-z0-9_.-]+/)
|
|
40
|
+
scanner.skip(/\s*/)
|
|
41
|
+
separator = scanner.scan(/=/)
|
|
42
|
+
scanner.skip(/\s*/)
|
|
43
|
+
value = scanner.rest.strip
|
|
44
|
+
invalid_ini_line!(line_number) unless key && separator
|
|
45
|
+
[[:KEY, key], ["=", nil], [:VALUE, value], [:NEWLINE, nil]]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def invalid_ini_line!(line_number)
|
|
51
|
+
raise ArgumentError, "invalid INI input on line #{line_number}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_document(records)
|
|
55
|
+
document = {}
|
|
56
|
+
current = document
|
|
57
|
+
records.each do |record|
|
|
58
|
+
if record[0] == :section
|
|
59
|
+
current = (document[record[1]] ||= {})
|
|
60
|
+
else
|
|
61
|
+
current[record[1]] = record[2]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
document
|
|
65
|
+
end
|
|
66
|
+
---- footer
|
|
67
|
+
if $PROGRAM_NAME == __FILE__
|
|
68
|
+
require "json"
|
|
69
|
+
puts JSON.generate(Examples::INIParser.new.parse(ARGF.read))
|
|
70
|
+
end
|
data/examples/json.y
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class Examples::JSONParser
|
|
2
|
+
pragma extended
|
|
3
|
+
token STRING NUMBER TRUE FALSE NULL
|
|
4
|
+
%test accept "{\"ok\":[true,null]}"
|
|
5
|
+
%test accept "{\"object\":{},\"array\":[],\"string\":\"ibex\",\"number\":1,\"false\":false,\"many\":[1,2]}"
|
|
6
|
+
%test reject "{\"ok\":}"
|
|
7
|
+
lexer
|
|
8
|
+
skip /\s+/
|
|
9
|
+
STRING /"(?:\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\])*"/ { |source| decode_json_string(source) }
|
|
10
|
+
NUMBER /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/ {
|
|
11
|
+
|source| source.match?(/[.eE]/) ? Float(source) : Integer(source, 10)
|
|
12
|
+
}
|
|
13
|
+
TRUE /true/ { true }
|
|
14
|
+
FALSE /false/ { false }
|
|
15
|
+
NULL /null/ { nil }
|
|
16
|
+
on /[{}\[\],:]/ { |source| emit source, nil }
|
|
17
|
+
end
|
|
18
|
+
rule
|
|
19
|
+
document : value { result = val[0] }
|
|
20
|
+
value : object { result = val[0] }
|
|
21
|
+
| array { result = val[0] }
|
|
22
|
+
| STRING { result = val[0] }
|
|
23
|
+
| NUMBER { result = val[0] }
|
|
24
|
+
| TRUE { result = true }
|
|
25
|
+
| FALSE { result = false }
|
|
26
|
+
| NULL { result = nil }
|
|
27
|
+
object : '{' '}' { result = {} }
|
|
28
|
+
| '{' members '}' { result = val[1] }
|
|
29
|
+
members : pair { result = { val[0][0] => val[0][1] } }
|
|
30
|
+
| members ',' pair { result = val[0].merge(val[2][0] => val[2][1]) }
|
|
31
|
+
pair : STRING ':' value { result = [val[0], val[2]] }
|
|
32
|
+
array : '[' ']' { result = [] }
|
|
33
|
+
| '[' elements ']' { result = val[1] }
|
|
34
|
+
elements : value { result = [val[0]] }
|
|
35
|
+
| elements ',' value { result = val[0] + [val[2]] }
|
|
36
|
+
end
|
|
37
|
+
---- header
|
|
38
|
+
require "json"
|
|
39
|
+
---- inner
|
|
40
|
+
def parse(source, file: "(json)") = super
|
|
41
|
+
|
|
42
|
+
def decode_json_string(source)
|
|
43
|
+
JSON.parse(source)
|
|
44
|
+
end
|
|
45
|
+
---- footer
|
|
46
|
+
if $PROGRAM_NAME == __FILE__
|
|
47
|
+
puts JSON.generate(Examples::JSONParser.new.parse(ARGF.read))
|
|
48
|
+
end
|