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,288 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative "action_locations"
|
|
5
|
+
|
|
6
|
+
module Ibex
|
|
7
|
+
module Codegen
|
|
8
|
+
# Selects the smallest generated semantic-action ABI that preserves the
|
|
9
|
+
# action's declared inputs.
|
|
10
|
+
# rubocop:disable Metrics/ModuleLength -- ABI proof and source rewriting must evolve together.
|
|
11
|
+
module GeneratedActionABI
|
|
12
|
+
LEGACY_PARAMETERS = %w[
|
|
13
|
+
_values
|
|
14
|
+
_ibex_locations
|
|
15
|
+
_ibex_location_stack
|
|
16
|
+
_ibex_location
|
|
17
|
+
_ibex_lookahead_location
|
|
18
|
+
].freeze #: Array[String]
|
|
19
|
+
SIMPLE_POSITIONAL_READ = /(?<![.\w@$])val\[\s*(\d+)\s*\]/ #: Regexp
|
|
20
|
+
SIMPLE_POSITIONAL_UNSAFE = %r{['"`#%/\\]|<<} #: Regexp
|
|
21
|
+
private_constant :LEGACY_PARAMETERS
|
|
22
|
+
private_constant :SIMPLE_POSITIONAL_READ, :SIMPLE_POSITIONAL_UNSAFE
|
|
23
|
+
|
|
24
|
+
# Keeps semantic-action analysis local to one code-generation run.
|
|
25
|
+
class Cache
|
|
26
|
+
# @rbs @values_only: Hash[Integer, bool]
|
|
27
|
+
# @rbs @borrowed_values: Hash[Integer, bool]
|
|
28
|
+
# @rbs @positional_actions: Hash[Integer, String?]
|
|
29
|
+
|
|
30
|
+
# @rbs () -> void
|
|
31
|
+
def initialize
|
|
32
|
+
@values_only = {}
|
|
33
|
+
@borrowed_values = {}
|
|
34
|
+
@positional_actions = {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs (IR::Production production) -> bool
|
|
38
|
+
def values_only?(production)
|
|
39
|
+
@values_only.fetch(production.id) do
|
|
40
|
+
@values_only[production.id] = GeneratedActionABI.values_only?(production)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @rbs (IR::Production production) -> bool
|
|
45
|
+
def borrowed_values?(production)
|
|
46
|
+
@borrowed_values.fetch(production.id) do
|
|
47
|
+
@borrowed_values[production.id] =
|
|
48
|
+
GeneratedActionABI.borrowed_values?(production, analysis: self)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @rbs (IR::Production production) -> String?
|
|
53
|
+
def positional_action_source(production)
|
|
54
|
+
@positional_actions.fetch(production.id) do
|
|
55
|
+
@positional_actions[production.id] =
|
|
56
|
+
GeneratedActionABI.positional_action_source(production, analysis: self)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @rbs (IR::Production production) -> bool
|
|
61
|
+
def positional_values?(production)
|
|
62
|
+
!positional_action_source(production).nil?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module_function
|
|
67
|
+
|
|
68
|
+
# @rbs (IR::Production production) -> bool
|
|
69
|
+
def values_only?(production)
|
|
70
|
+
return false if production.action&.composition
|
|
71
|
+
|
|
72
|
+
action = production.action
|
|
73
|
+
return true unless action
|
|
74
|
+
return false if action.context_length.positive?
|
|
75
|
+
|
|
76
|
+
maximum = production.rhs.length
|
|
77
|
+
locations = ActionLocations.new(action.code, maximum: maximum, location: action.location)
|
|
78
|
+
!locations.references? && !references_legacy_parameter?(action.code)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The direct runtime can share one reduction-values array with an action
|
|
82
|
+
# and a hook installed by that action only when the action cannot mutate
|
|
83
|
+
# or retain the array itself. Element reads and parallel assignment copy
|
|
84
|
+
# values out without exposing the container.
|
|
85
|
+
# @rbs (IR::Production production, ?analysis: singleton(GeneratedActionABI) | Cache) -> bool
|
|
86
|
+
def borrowed_values?(production, analysis: self)
|
|
87
|
+
return false unless analysis.values_only?(production)
|
|
88
|
+
|
|
89
|
+
action = production.action
|
|
90
|
+
return false unless action
|
|
91
|
+
|
|
92
|
+
maximum = production.rhs.length
|
|
93
|
+
source = ActionLocations.new(action.code, maximum: maximum, location: action.location).rewrite
|
|
94
|
+
return true if simple_indexed_values_action?(source)
|
|
95
|
+
|
|
96
|
+
require "ripper"
|
|
97
|
+
syntax = Object.const_get(:Ripper).__send__(:sexp, source)
|
|
98
|
+
!syntax.nil? && read_only_value_references?(syntax)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return semantic source rewritten for zero-to-four positional RHS
|
|
102
|
+
# arguments, or nil when the values container remains observable.
|
|
103
|
+
# @rbs (IR::Production production, ?analysis: singleton(GeneratedActionABI) | Cache) -> String?
|
|
104
|
+
def positional_action_source(production, analysis: self)
|
|
105
|
+
return nil unless analysis.values_only?(production)
|
|
106
|
+
return nil unless production.rhs.length <= 4
|
|
107
|
+
|
|
108
|
+
action = production.action
|
|
109
|
+
return "" unless action
|
|
110
|
+
return nil unless analysis.borrowed_values?(production)
|
|
111
|
+
|
|
112
|
+
parameters = Array.new(production.rhs.length) { |index| "v#{index}" }
|
|
113
|
+
return nil if action.named_refs.any? { |reference| parameters.include?(reference[:name]) }
|
|
114
|
+
|
|
115
|
+
maximum = production.rhs.length
|
|
116
|
+
source = ActionLocations.new(action.code, maximum: maximum, location: action.location).rewrite
|
|
117
|
+
simple = fast_positional_action_source(source, parameters)
|
|
118
|
+
return simple unless simple.nil?
|
|
119
|
+
|
|
120
|
+
require "ripper"
|
|
121
|
+
tokens = Object.const_get(:Ripper).__send__(:lex, source)
|
|
122
|
+
return nil unless tokens.map { |_position, _event, token, _state| token }.join == source
|
|
123
|
+
|
|
124
|
+
rewrite_positional_values(tokens, parameters)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @rbs (String source, Array[String] parameters) -> String?
|
|
128
|
+
def fast_positional_action_source(source, parameters)
|
|
129
|
+
simple = simple_positional_action_source(source, parameters)
|
|
130
|
+
return simple unless simple.nil?
|
|
131
|
+
|
|
132
|
+
simple_indexed_positional_action_source(source, parameters)
|
|
133
|
+
end
|
|
134
|
+
private_class_method :fast_positional_action_source
|
|
135
|
+
|
|
136
|
+
# @rbs (IR::Production production) -> bool
|
|
137
|
+
def positional_values?(production)
|
|
138
|
+
!positional_action_source(production).nil?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Avoid loading a Ruby parser for the overwhelmingly common generated
|
|
142
|
+
# action shape. This accepts only a single result assignment (or a bare
|
|
143
|
+
# expression), simple numeric val reads, and no further assignment.
|
|
144
|
+
# Anything less obvious falls back to Ripper below.
|
|
145
|
+
# @rbs (String source) -> bool
|
|
146
|
+
def simple_indexed_values_action?(source)
|
|
147
|
+
body = source.strip
|
|
148
|
+
assignment = /\Aresult\s*=\s*(.*)\z/m.match(body)
|
|
149
|
+
body = (assignment[1] || "").strip if assignment
|
|
150
|
+
return false if body.empty? || body.match?(/(?<![=!<>])=(?!=|>)/)
|
|
151
|
+
|
|
152
|
+
without_reads = body.gsub(/\bval\[\s*\d+\s*\]/, "")
|
|
153
|
+
without_reads != body && !without_reads.match?(/\bval\b/)
|
|
154
|
+
end
|
|
155
|
+
private_class_method :simple_indexed_values_action?
|
|
156
|
+
|
|
157
|
+
# Keep the most common identity action off the Ripper load path. The
|
|
158
|
+
# whole-source match excludes strings, comments, receivers, and compound
|
|
159
|
+
# expressions before replacing the one direct read.
|
|
160
|
+
# @rbs (String source, Array[String] parameters) -> String?
|
|
161
|
+
def simple_positional_action_source(source, parameters)
|
|
162
|
+
match = /\A\s*(?:result\s*=\s*)?val\[\s*(\d+)\s*\]\s*\z/.match(source)
|
|
163
|
+
return nil unless match
|
|
164
|
+
|
|
165
|
+
parameter = parameters[Integer(match[1] || "", 10)]
|
|
166
|
+
source.sub(/\bval\[\s*\d+\s*\]/, parameter) if parameter
|
|
167
|
+
end
|
|
168
|
+
private_class_method :simple_positional_action_source
|
|
169
|
+
|
|
170
|
+
# Rewrite the common string/comment/regexp-free action shape without
|
|
171
|
+
# loading Ripper. The borrowed-values proof has already rejected writes
|
|
172
|
+
# and escaping uses of the values container.
|
|
173
|
+
# @rbs (String source, Array[String] parameters) -> String?
|
|
174
|
+
def simple_indexed_positional_action_source(source, parameters)
|
|
175
|
+
return nil if source.match?(SIMPLE_POSITIONAL_UNSAFE)
|
|
176
|
+
return nil if parameters.any? { |parameter| source.match?(/\b#{Regexp.escape(parameter)}\b/) }
|
|
177
|
+
|
|
178
|
+
valid = true
|
|
179
|
+
rewritten = source.gsub(SIMPLE_POSITIONAL_READ) do
|
|
180
|
+
parameter = parameters.fetch(Integer(Regexp.last_match(1) || "", 10), nil)
|
|
181
|
+
valid = false unless parameter
|
|
182
|
+
parameter || Regexp.last_match(0)
|
|
183
|
+
end
|
|
184
|
+
return nil unless valid
|
|
185
|
+
return nil if rewritten.match?(/\bval\b/)
|
|
186
|
+
|
|
187
|
+
rewritten
|
|
188
|
+
end
|
|
189
|
+
private_class_method :simple_indexed_positional_action_source
|
|
190
|
+
|
|
191
|
+
# @rbs (Array[untyped] tokens, Array[String] parameters) -> String?
|
|
192
|
+
def rewrite_positional_values(tokens, parameters)
|
|
193
|
+
result = [] #: Array[String]
|
|
194
|
+
index = 0
|
|
195
|
+
while index < tokens.length
|
|
196
|
+
_position, event, token, _state = tokens[index]
|
|
197
|
+
return nil if event == :on_ident && parameters.include?(token)
|
|
198
|
+
|
|
199
|
+
unless event == :on_ident && token == "val"
|
|
200
|
+
result << token
|
|
201
|
+
index += 1
|
|
202
|
+
next
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
reference = positional_value_reference(tokens, index, parameters)
|
|
206
|
+
if reference
|
|
207
|
+
result << reference.fetch(0)
|
|
208
|
+
index = reference.fetch(1)
|
|
209
|
+
elsif receiver_before_value?(tokens, index) || parameters.empty?
|
|
210
|
+
return nil
|
|
211
|
+
else
|
|
212
|
+
# borrowed_values? has already proven that a bare `val` occurs
|
|
213
|
+
# only as the right-hand side of parallel assignment.
|
|
214
|
+
result << parameters.join(", ")
|
|
215
|
+
index += 1
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
result.join
|
|
219
|
+
end
|
|
220
|
+
private_class_method :rewrite_positional_values
|
|
221
|
+
|
|
222
|
+
# @rbs (Array[untyped] tokens, Integer index, Array[String] parameters) -> [String, Integer]?
|
|
223
|
+
def positional_value_reference(tokens, index, parameters)
|
|
224
|
+
return nil if receiver_before_value?(tokens, index)
|
|
225
|
+
|
|
226
|
+
reference = tokens.slice(index + 1, 3)
|
|
227
|
+
return nil unless reference
|
|
228
|
+
return nil unless reference.map { |entry| entry[1] } == %i[on_lbracket on_int on_rbracket]
|
|
229
|
+
|
|
230
|
+
parameter = parameters[Integer(reference.fetch(1).fetch(2), 10)]
|
|
231
|
+
[parameter, index + 4] if parameter
|
|
232
|
+
rescue ArgumentError
|
|
233
|
+
nil
|
|
234
|
+
end
|
|
235
|
+
private_class_method :positional_value_reference
|
|
236
|
+
|
|
237
|
+
# @rbs (Array[untyped] tokens, Integer index) -> bool
|
|
238
|
+
def receiver_before_value?(tokens, index)
|
|
239
|
+
previous = tokens.first(index).reverse.find do |entry|
|
|
240
|
+
!%i[on_sp on_nl on_ignored_nl on_comment].include?(entry[1])
|
|
241
|
+
end
|
|
242
|
+
return false unless previous
|
|
243
|
+
|
|
244
|
+
previous[1] == :on_period || (previous[1] == :on_op && %w[&. ::].include?(previous[2]))
|
|
245
|
+
end
|
|
246
|
+
private_class_method :receiver_before_value?
|
|
247
|
+
|
|
248
|
+
# @rbs (String source) -> bool
|
|
249
|
+
def references_legacy_parameter?(source)
|
|
250
|
+
return false unless LEGACY_PARAMETERS.any? { |parameter| source.include?(parameter) }
|
|
251
|
+
|
|
252
|
+
require "ripper"
|
|
253
|
+
tokens = Object.const_get(:Ripper).__send__(:lex, source.gsub(/@(?:\$|\d+)/) { |match| "_" * match.bytesize })
|
|
254
|
+
tokens.any? do |_position, type, token, _state|
|
|
255
|
+
type == :on_ident && LEGACY_PARAMETERS.include?(token)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
private_class_method :references_legacy_parameter?
|
|
259
|
+
|
|
260
|
+
# @rbs (untyped node, ?untyped parent, ?Integer? child_index) -> bool
|
|
261
|
+
def read_only_value_references?(node, parent = nil, child_index = nil)
|
|
262
|
+
return true unless node.is_a?(Array)
|
|
263
|
+
return safe_value_reference?(parent, child_index) if value_reference?(node)
|
|
264
|
+
|
|
265
|
+
node.each_with_index.all? do |child, index|
|
|
266
|
+
read_only_value_references?(child, node, index)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
private_class_method :read_only_value_references?
|
|
270
|
+
|
|
271
|
+
# @rbs (untyped node) -> bool
|
|
272
|
+
def value_reference?(node)
|
|
273
|
+
node[0] == :vcall && node.dig(1, 0) == :@ident && node.dig(1, 1) == "val"
|
|
274
|
+
end
|
|
275
|
+
private_class_method :value_reference?
|
|
276
|
+
|
|
277
|
+
# @rbs (untyped parent, Integer? child_index) -> bool
|
|
278
|
+
def safe_value_reference?(parent, child_index)
|
|
279
|
+
return false unless parent.is_a?(Array)
|
|
280
|
+
|
|
281
|
+
(parent[0] == :aref && child_index == 1) ||
|
|
282
|
+
(parent[0] == :massign && child_index == 2)
|
|
283
|
+
end
|
|
284
|
+
private_class_method :safe_value_reference?
|
|
285
|
+
end
|
|
286
|
+
# rubocop:enable Metrics/ModuleLength
|
|
287
|
+
end
|
|
288
|
+
end
|
data/lib/ibex/codegen/html.rb
CHANGED
|
@@ -11,13 +11,23 @@ module Ibex
|
|
|
11
11
|
# Hash[Integer, String] labels) -> String
|
|
12
12
|
# private def self.state_sections: (IR::Automaton automaton, IR::Grammar grammar,
|
|
13
13
|
# Hash[Integer, String] labels) -> String
|
|
14
|
+
# private def controls: (IR::Automaton automaton) -> String
|
|
15
|
+
# private def self.controls: (IR::Automaton automaton) -> String
|
|
16
|
+
# private def styles: () -> String
|
|
17
|
+
# private def self.styles: () -> String
|
|
18
|
+
# private def interaction_script: () -> String
|
|
19
|
+
# private def self.interaction_script: () -> String
|
|
20
|
+
# private def one_hop_neighbors: (IR::Automaton automaton, Integer state_id) -> Array[Integer]
|
|
21
|
+
# private def self.one_hop_neighbors: (IR::Automaton automaton, Integer state_id) -> Array[Integer]
|
|
14
22
|
# private def item_html: (IR::AutomatonItem item, IR::Grammar grammar, Hash[Integer, String] labels) -> String
|
|
15
23
|
# private def self.item_html: (IR::AutomatonItem item, IR::Grammar grammar,
|
|
16
24
|
# Hash[Integer, String] labels) -> String
|
|
17
25
|
# private def rule_sections: (IR::Grammar grammar, Hash[Integer, String] labels) -> String
|
|
18
26
|
# private def self.rule_sections: (IR::Grammar grammar, Hash[Integer, String] labels) -> String
|
|
19
|
-
# private def conflict_sections: (IR::Automaton automaton
|
|
20
|
-
#
|
|
27
|
+
# private def conflict_sections: (IR::Automaton automaton, IR::Grammar grammar,
|
|
28
|
+
# Hash[Integer, String] labels) -> String
|
|
29
|
+
# private def self.conflict_sections: (IR::Automaton automaton, IR::Grammar grammar,
|
|
30
|
+
# Hash[Integer, String] labels) -> String
|
|
21
31
|
# private def escape: (String value) -> String
|
|
22
32
|
# private def self.escape: (String value) -> String
|
|
23
33
|
# private def symbol_name: (Hash[Integer, String] labels, Integer id) -> String
|
|
@@ -30,12 +40,14 @@ module Ibex
|
|
|
30
40
|
<<~HTML
|
|
31
41
|
<!doctype html>
|
|
32
42
|
<html lang="en"><head><meta charset="utf-8"><title>Ibex automaton</title>
|
|
33
|
-
|
|
43
|
+
#{styles}
|
|
34
44
|
</head><body><h1>Ibex #{escape(automaton.algorithm)} automaton</h1>
|
|
35
45
|
<nav><a href="#rules">Rules</a> · <a href="#conflicts">Conflicts</a></nav>
|
|
36
|
-
#{
|
|
46
|
+
#{controls(automaton)}
|
|
47
|
+
<main id="states">#{state_sections(automaton, grammar, labels)}</main>
|
|
37
48
|
<h2 id="rules">Rules</h2>#{rule_sections(grammar, labels)}
|
|
38
|
-
<h2 id="conflicts">Conflicts</h2>#{conflict_sections(automaton)}
|
|
49
|
+
<h2 id="conflicts">Conflicts</h2>#{conflict_sections(automaton, grammar, labels)}
|
|
50
|
+
#{interaction_script}
|
|
39
51
|
</body></html>
|
|
40
52
|
HTML
|
|
41
53
|
end
|
|
@@ -44,6 +56,62 @@ module Ibex
|
|
|
44
56
|
# @rbs skip
|
|
45
57
|
private
|
|
46
58
|
|
|
59
|
+
# @rbs skip
|
|
60
|
+
def styles
|
|
61
|
+
<<~HTML
|
|
62
|
+
<style>
|
|
63
|
+
body{font:14px system-ui;margin:2rem;max-width:90rem}code{white-space:pre-wrap}
|
|
64
|
+
.controls{align-items:center;display:flex;flex-wrap:wrap;gap:1rem;margin:1rem 0}
|
|
65
|
+
.state{border:1px solid #bbb;padding:1rem;margin:1rem 0}.state[hidden]{display:none}
|
|
66
|
+
.conflict-state{background:#fff7f7;border:2px solid #b91c1c}.conflict{color:#a00}a{color:#075985}
|
|
67
|
+
</style>
|
|
68
|
+
HTML
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @rbs skip
|
|
72
|
+
def interaction_script
|
|
73
|
+
<<~HTML
|
|
74
|
+
<script>
|
|
75
|
+
(() => {
|
|
76
|
+
const search = document.getElementById("state-search");
|
|
77
|
+
const conflictOnly = document.getElementById("conflict-only");
|
|
78
|
+
const neighborhood = document.getElementById("conflict-neighborhood");
|
|
79
|
+
const states = Array.from(document.querySelectorAll(".state"));
|
|
80
|
+
const update = () => {
|
|
81
|
+
const query = search.value.trim().toLowerCase();
|
|
82
|
+
const focus = neighborhood.value;
|
|
83
|
+
states.forEach((state) => {
|
|
84
|
+
const matchesText = !query || state.textContent.toLowerCase().includes(query);
|
|
85
|
+
const matchesConflict = !conflictOnly.checked || state.classList.contains("conflict-state");
|
|
86
|
+
const nearby = !focus || state.dataset.neighbors.split(" ").includes(focus);
|
|
87
|
+
state.hidden = !(matchesText && matchesConflict && nearby);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
search.addEventListener("input", update);
|
|
91
|
+
conflictOnly.addEventListener("change", update);
|
|
92
|
+
neighborhood.addEventListener("change", update);
|
|
93
|
+
})();
|
|
94
|
+
</script>
|
|
95
|
+
HTML
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs skip
|
|
99
|
+
def controls(automaton)
|
|
100
|
+
options = automaton.states.filter_map do |state|
|
|
101
|
+
next if state.conflicts.empty?
|
|
102
|
+
|
|
103
|
+
%(<option value="#{state.id}">State #{state.id}</option>)
|
|
104
|
+
end.join
|
|
105
|
+
<<~HTML
|
|
106
|
+
<div class="controls">
|
|
107
|
+
<label>Search states <input id="state-search" type="search" aria-controls="states"></label>
|
|
108
|
+
<label><input id="conflict-only" type="checkbox"> Conflicts only</label>
|
|
109
|
+
<label>Conflict neighborhood
|
|
110
|
+
<select id="conflict-neighborhood"><option value="">All states</option>#{options}</select></label>
|
|
111
|
+
</div>
|
|
112
|
+
HTML
|
|
113
|
+
end
|
|
114
|
+
|
|
47
115
|
# @rbs skip
|
|
48
116
|
def state_sections(automaton, grammar, labels)
|
|
49
117
|
automaton.states.map do |state|
|
|
@@ -52,13 +120,25 @@ module Ibex
|
|
|
52
120
|
"<li>#{symbol} → <a href=\"#state-#{target}\">state #{target}</a></li>"
|
|
53
121
|
end.join
|
|
54
122
|
items = state.items.map { |item| item_html(item, grammar, labels) }.join
|
|
123
|
+
class_name = state.conflicts.empty? ? "state" : "state conflict-state"
|
|
124
|
+
neighbors = one_hop_neighbors(automaton, state.id).join(" ")
|
|
55
125
|
<<~HTML
|
|
56
|
-
<section class="
|
|
126
|
+
<section class="#{class_name}" id="state-#{state.id}" data-state-id="#{state.id}" data-neighbors="#{neighbors}">
|
|
127
|
+
<h2>State #{state.id}</h2>
|
|
57
128
|
<ul>#{transitions}</ul><ul>#{items}</ul></section>
|
|
58
129
|
HTML
|
|
59
130
|
end.join
|
|
60
131
|
end
|
|
61
132
|
|
|
133
|
+
# @rbs skip
|
|
134
|
+
def one_hop_neighbors(automaton, state_id)
|
|
135
|
+
outgoing = automaton.states.fetch(state_id).transitions.values
|
|
136
|
+
incoming = automaton.states.filter_map do |state|
|
|
137
|
+
state.id if state.transitions.value?(state_id)
|
|
138
|
+
end
|
|
139
|
+
([state_id] + outgoing + incoming).uniq.sort
|
|
140
|
+
end
|
|
141
|
+
|
|
62
142
|
# @rbs skip
|
|
63
143
|
def item_html(item, grammar, labels)
|
|
64
144
|
return "<li><code>$accept</code></li>" if item.production.negative?
|
|
@@ -81,11 +161,13 @@ module Ibex
|
|
|
81
161
|
end
|
|
82
162
|
|
|
83
163
|
# @rbs skip
|
|
84
|
-
def conflict_sections(automaton)
|
|
164
|
+
def conflict_sections(automaton, grammar, labels)
|
|
85
165
|
conflicts = automaton.states.flat_map do |state|
|
|
86
166
|
state.conflicts.map do |conflict|
|
|
87
167
|
link = "<a href=\"#state-#{state.id}\">state #{state.id}</a>"
|
|
88
|
-
|
|
168
|
+
symbol = grammar.symbol(conflict[:symbol])
|
|
169
|
+
displayed = symbol ? conflict.merge(symbol: symbol_name(labels, symbol.id)) : conflict
|
|
170
|
+
"<li class=\"conflict\">#{link}: #{escape(displayed.inspect)}</li>"
|
|
89
171
|
end
|
|
90
172
|
end
|
|
91
173
|
conflicts.empty? ? "<p>None</p>" : "<ul>#{conflicts.join}</ul>"
|
|
@@ -100,10 +182,12 @@ module Ibex
|
|
|
100
182
|
def symbol_name(labels, id)
|
|
101
183
|
labels.fetch(id) { raise Ibex::Error, "missing grammar symbol id #{id}" }
|
|
102
184
|
end
|
|
103
|
-
module_function :
|
|
185
|
+
module_function :styles, :interaction_script, :controls, :state_sections, :one_hop_neighbors
|
|
186
|
+
module_function :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
|
|
104
187
|
|
|
105
188
|
class << self
|
|
106
|
-
private :
|
|
189
|
+
private :styles, :interaction_script, :controls, :state_sections, :one_hop_neighbors
|
|
190
|
+
private :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
|
|
107
191
|
end
|
|
108
192
|
end
|
|
109
193
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "symbol_labels"
|
|
4
|
+
|
|
5
|
+
module Ibex
|
|
6
|
+
module Codegen
|
|
7
|
+
# Renders an Automaton IR graph as a GitHub-compatible Mermaid flowchart.
|
|
8
|
+
module Mermaid
|
|
9
|
+
# @rbs (IR::Automaton automaton) -> String
|
|
10
|
+
def render(automaton)
|
|
11
|
+
labels = SymbolLabels.build(automaton.grammar)
|
|
12
|
+
lines = ["flowchart LR"]
|
|
13
|
+
automaton.states.each do |state|
|
|
14
|
+
lines << %( state_#{state.id}["State #{state.id}"])
|
|
15
|
+
state.transitions.each do |symbol_id, target|
|
|
16
|
+
label = escape(labels.fetch(symbol_id) { raise Ibex::Error, "missing grammar symbol id #{symbol_id}" })
|
|
17
|
+
lines << " state_#{state.id} -->|#{label}| state_#{target}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
conflicting = automaton.states.reject { |state| state.conflicts.empty? }
|
|
21
|
+
unless conflicting.empty?
|
|
22
|
+
lines << " classDef conflict fill:#fee2e2,stroke:#b91c1c,stroke-width:2px"
|
|
23
|
+
conflicting.each { |state| lines << " class state_#{state.id} conflict;" }
|
|
24
|
+
end
|
|
25
|
+
"#{lines.join("\n")}\n"
|
|
26
|
+
end
|
|
27
|
+
module_function :render
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# @rbs (String value) -> String
|
|
32
|
+
def escape(value)
|
|
33
|
+
value.to_s.gsub("&", "&").gsub("<", "<").gsub(">", ">").gsub("|", "|")
|
|
34
|
+
.gsub('"', """).gsub(/\s+/, " ")
|
|
35
|
+
end
|
|
36
|
+
module_function :escape
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
private :escape
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|