ibex 0.2.0 → 0.4.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 +100 -272
- data/docs/architecture.md +187 -35
- data/docs/bison-import.md +103 -0
- data/docs/comparison-policy.md +127 -0
- data/docs/configuration-model.md +59 -0
- data/docs/configuration-report.md +28 -0
- data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
- data/docs/conflict-explanation-study.md +62 -0
- data/docs/construction-profiling.md +157 -0
- data/docs/cst-migration.md +3 -4
- data/docs/cst.md +24 -0
- data/docs/decisions/0000-template.md +24 -0
- data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
- data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
- data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
- data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
- data/docs/decisions/0005-contained-grammar-composition.md +34 -0
- data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
- data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
- data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
- data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
- data/docs/decisions/0010-committed-runtime-observation.md +34 -0
- data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
- data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
- data/docs/decisions/0013-transactional-generation-publication.md +37 -0
- data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
- data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
- data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
- data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
- data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
- data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
- data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
- data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
- data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
- data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
- data/docs/decisions/0024-direct-ielr-construction.md +42 -0
- data/docs/decisions/README.md +74 -0
- data/docs/declarative-configuration.md +274 -0
- data/docs/development.md +196 -2
- data/docs/direct-ielr-decision.md +120 -0
- data/docs/direct-multi-entry-decision.md +82 -0
- data/docs/editor-setup.md +2 -1
- data/docs/error-ux-review-rubric-v1.md +117 -0
- data/docs/error-ux-reviews/v1/records/README.md +22 -0
- data/docs/error-ux-round2-review-status-v1.json +27 -0
- data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
- data/docs/error-ux-round2-v1.json +1617 -0
- data/docs/error-ux-round2.md +111 -0
- data/docs/error-ux.md +54 -0
- data/docs/getting-started.md +80 -0
- data/docs/grammar-reference.md +184 -13
- data/docs/ielr-design.md +43 -0
- data/docs/ielr.md +92 -0
- data/docs/impact.md +101 -0
- data/docs/ja/bison-import.md +51 -0
- data/docs/ja/stable-api.md +53 -0
- data/docs/lexer-construction-profile.md +109 -0
- data/docs/lexer-migration.md +5 -0
- data/docs/maturity.md +129 -0
- data/docs/project-site-strategy.md +54 -0
- data/docs/racc-migration-evidence.md +45 -0
- data/docs/racc-migration.md +11 -0
- data/docs/release-readiness.md +126 -68
- data/docs/repair-semantics.md +123 -0
- data/docs/runtime-abi-evolution.md +310 -0
- data/docs/stability.md +145 -34
- data/docs/status.md +43 -0
- data/docs/syntax-sessions.md +183 -0
- data/docs/table-artifact.md +125 -0
- data/docs/test-interactions.md +219 -0
- data/docs/verification-report.md +137 -0
- data/docs/verifier-trust-boundary.md +199 -0
- data/docs/workloads.md +132 -0
- data/lib/ibex/analysis/digraph.rb +128 -0
- data/lib/ibex/analysis/sets.rb +14 -0
- data/lib/ibex/analysis.rb +1 -0
- data/lib/ibex/bison_import/importer.rb +516 -0
- data/lib/ibex/bison_import/tokenizer.rb +260 -0
- data/lib/ibex/bison_import.rb +200 -0
- data/lib/ibex/bounded_subprocess.rb +171 -0
- data/lib/ibex/cli/ambiguity.rb +14 -5
- data/lib/ibex/cli/analysis.rb +134 -0
- data/lib/ibex/cli/bison_import.rb +121 -0
- data/lib/ibex/cli/config.rb +128 -0
- data/lib/ibex/cli/diagnostics.rb +31 -11
- data/lib/ibex/cli/documentation.rb +11 -8
- data/lib/ibex/cli/equiv.rb +169 -0
- data/lib/ibex/cli/error_messages.rb +11 -4
- data/lib/ibex/cli/explain.rb +14 -5
- data/lib/ibex/cli/fix.rb +203 -0
- data/lib/ibex/cli/formatting.rb +11 -10
- data/lib/ibex/cli/fuzz.rb +200 -0
- data/lib/ibex/cli/fuzz_regressions.rb +145 -0
- data/lib/ibex/cli/generation_artifacts.rb +53 -2
- data/lib/ibex/cli/generation_error_messages.rb +1 -1
- data/lib/ibex/cli/grammar_tests.rb +32 -14
- data/lib/ibex/cli/impact.rb +393 -0
- data/lib/ibex/cli/ir_tools.rb +5 -61
- data/lib/ibex/cli/outputs.rb +60 -33
- data/lib/ibex/cli/reduce.rb +202 -0
- data/lib/ibex/cli/reduce_reporting.rb +71 -0
- data/lib/ibex/cli/samples.rb +49 -23
- data/lib/ibex/cli/verify.rb +116 -0
- data/lib/ibex/cli/watch.rb +6 -5
- data/lib/ibex/cli.rb +362 -56
- data/lib/ibex/codegen/action_locations.rb +1 -1
- data/lib/ibex/codegen/action_method_source.rb +5 -6
- data/lib/ibex/codegen/action_source.rb +3 -2
- data/lib/ibex/codegen/ambiguity.rb +5 -4
- data/lib/ibex/codegen/explain.rb +123 -55
- data/lib/ibex/codegen/generated_action_abi.rb +10 -5
- data/lib/ibex/codegen/rbs.rb +15 -7
- data/lib/ibex/codegen/report.rb +25 -13
- data/lib/ibex/codegen/ruby.rb +28 -64
- data/lib/ibex/codegen/ruby_actions.rb +9 -6
- data/lib/ibex/codegen/ruby_syntax.rb +15 -9
- data/lib/ibex/configuration/analysis_grammar.rb +35 -0
- data/lib/ibex/configuration/explanation.rb +421 -0
- data/lib/ibex/configuration/inspector.rb +232 -0
- data/lib/ibex/configuration.rb +580 -0
- data/lib/ibex/coverage/collector.rb +21 -11
- data/lib/ibex/coverage/event_stream.rb +13 -7
- data/lib/ibex/coverage/report.rb +26 -18
- data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
- data/lib/ibex/delta_reducer.rb +99 -0
- data/lib/ibex/diff.rb +141 -0
- data/lib/ibex/equiv/machine.rb +135 -0
- data/lib/ibex/equiv.rb +373 -0
- data/lib/ibex/fix.rb +557 -0
- data/lib/ibex/frontend/ast.rb +22 -2
- data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
- data/lib/ibex/frontend/diagnostic.rb +1 -1
- data/lib/ibex/frontend/formatter.rb +72 -26
- data/lib/ibex/frontend/generated_parser.rb +147 -117
- data/lib/ibex/frontend/generated_parser_base.rb +7 -3
- data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
- data/lib/ibex/frontend/generation.rb +1 -1
- data/lib/ibex/frontend/parser/declarations.rb +17 -2
- data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
- data/lib/ibex/frontend/regenerator.rb +1 -0
- data/lib/ibex/frontend/resolution.rb +2 -1
- data/lib/ibex/frontend/resolver.rb +1 -1
- data/lib/ibex/frontend/rule_documentation.rb +2 -1
- data/lib/ibex/frontend/source_cursor.rb +2 -2
- data/lib/ibex/frontend/source_span.rb +9 -1
- data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
- data/lib/ibex/frontend/token_adapter.rb +10 -0
- data/lib/ibex/frontend.rb +1 -0
- data/lib/ibex/fuzz.rb +219 -0
- data/lib/ibex/generation_input.rb +1 -1
- data/lib/ibex/generation_manifest.rb +36 -25
- data/lib/ibex/generation_transaction.rb +1 -1
- data/lib/ibex/generation_transaction_recovery.rb +4 -4
- data/lib/ibex/generation_transaction_validation.rb +2 -2
- data/lib/ibex/grammar_tests.rb +1 -1
- data/lib/ibex/impact/action_impact.rb +148 -0
- data/lib/ibex/impact/automaton_impact.rb +57 -0
- data/lib/ibex/impact/coverage_impact.rb +53 -0
- data/lib/ibex/impact/graph.rb +132 -0
- data/lib/ibex/impact/propagation.rb +147 -0
- data/lib/ibex/impact/report.rb +273 -0
- data/lib/ibex/impact/seeds.rb +69 -0
- data/lib/ibex/impact/severity.rb +139 -0
- data/lib/ibex/impact.rb +16 -0
- data/lib/ibex/ir/automaton_ir.rb +61 -19
- data/lib/ibex/ir/grammar_ir.rb +76 -45
- data/lib/ibex/ir/lexer_ir.rb +5 -5
- data/lib/ibex/ir/parser_contract.rb +135 -0
- data/lib/ibex/ir/serialize.rb +203 -76
- data/lib/ibex/ir/validator/automaton.rb +64 -34
- data/lib/ibex/ir/validator/base.rb +51 -33
- data/lib/ibex/ir/validator/grammar.rb +113 -83
- data/lib/ibex/ir/validator/lexer.rb +2 -2
- data/lib/ibex/ir/validator.rb +2 -1
- data/lib/ibex/ir.rb +16 -3
- data/lib/ibex/lalr/build_metrics.rb +50 -2
- data/lib/ibex/lalr/builder.rb +191 -56
- data/lib/ibex/lalr/conflict_search.rb +15 -4
- data/lib/ibex/lalr/counterexample.rb +19 -14
- data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
- data/lib/ibex/lalr/goto_follows.rb +229 -0
- data/lib/ibex/lalr/ielr/annotator.rb +214 -0
- data/lib/ibex/lalr/ielr/bits.rb +28 -0
- data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
- data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
- data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
- data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
- data/lib/ibex/lalr/ielr/split_state.rb +20 -0
- data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
- data/lib/ibex/lalr/ielr_partition.rb +22 -8
- data/lib/ibex/lalr/inadequacy_report.rb +50 -0
- data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
- data/lib/ibex/lalr/lr0_collection.rb +121 -0
- data/lib/ibex/lalr/unreachable_states.rb +80 -0
- data/lib/ibex/lalr.rb +52 -1
- data/lib/ibex/location.rb +3 -3
- data/lib/ibex/lsp/document_handlers.rb +9 -7
- data/lib/ibex/lsp/initialization_handlers.rb +9 -7
- data/lib/ibex/lsp/navigation_handlers.rb +25 -11
- data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
- data/lib/ibex/lsp/position_codec.rb +6 -2
- data/lib/ibex/lsp/request_handlers.rb +3 -2
- data/lib/ibex/lsp/request_support.rb +14 -7
- data/lib/ibex/lsp/server.rb +13 -11
- data/lib/ibex/lsp/symbol_index.rb +13 -13
- data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
- data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
- data/lib/ibex/lsp/transport.rb +13 -3
- data/lib/ibex/lsp/workspace.rb +5 -4
- data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
- data/lib/ibex/lsp.rb +1 -0
- data/lib/ibex/messages/en.yml +28 -0
- data/lib/ibex/messages/ja.yml +28 -0
- data/lib/ibex/messages.rb +68 -0
- data/lib/ibex/metrics.rb +175 -0
- data/lib/ibex/normalize/declarations.rb +2 -0
- data/lib/ibex/normalize/diagnostics.rb +63 -38
- data/lib/ibex/normalize/expander.rb +5 -4
- data/lib/ibex/normalize/expression.rb +8 -6
- data/lib/ibex/normalize/grammar_builder.rb +26 -0
- data/lib/ibex/normalize/inline_expansion.rb +100 -49
- data/lib/ibex/normalize/lexer.rb +3 -3
- data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
- data/lib/ibex/normalize/parameter_lowering.rb +40 -23
- data/lib/ibex/normalize/parameters.rb +31 -4
- data/lib/ibex/normalize/parser_configuration.rb +70 -0
- data/lib/ibex/normalize.rb +10 -20
- data/lib/ibex/racc_migration/report.rb +6 -3
- data/lib/ibex/rake_task.rb +1 -1
- data/lib/ibex/samples.rb +43 -13
- data/lib/ibex/table_artifact/builder.rb +266 -0
- data/lib/ibex/table_artifact/cst_projection.rb +70 -0
- data/lib/ibex/table_artifact/document.rb +39 -0
- data/lib/ibex/table_artifact/executor.rb +187 -0
- data/lib/ibex/table_artifact/serializer.rb +57 -0
- data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
- data/lib/ibex/table_artifact/validator/support.rb +86 -0
- data/lib/ibex/table_artifact/validator/tables.rb +238 -0
- data/lib/ibex/table_artifact/validator.rb +253 -0
- data/lib/ibex/table_artifact.rb +88 -0
- data/lib/ibex/table_simulation/result.rb +6 -2
- data/lib/ibex/table_simulation/step.rb +3 -1
- data/lib/ibex/tables.rb +14 -0
- data/lib/ibex/verifiable_generation_bundle.rb +86 -0
- data/lib/ibex/verification_report/builder.rb +159 -0
- data/lib/ibex/verification_report/canonical_ir.rb +123 -0
- data/lib/ibex/verification_report/logical_path.rb +68 -0
- data/lib/ibex/verification_report/validator.rb +329 -0
- data/lib/ibex/verification_report.rb +69 -0
- data/lib/ibex/verify/action_correspondence.rb +138 -0
- data/lib/ibex/verify/language_witness.rb +295 -0
- data/lib/ibex/verify/reference_collection.rb +190 -0
- data/lib/ibex/verify/result.rb +73 -0
- data/lib/ibex/verify/verifier.rb +598 -0
- data/lib/ibex/verify.rb +15 -0
- data/lib/ibex/version.rb +1 -1
- data/lib/ibex/watch/runner.rb +10 -1
- data/lib/ibex/watch/source_snapshot.rb +13 -8
- data/lib/ibex.rb +11 -0
- data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
- data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
- data/schema/bison-import-v1.schema.json +106 -0
- data/schema/conflict-explanation-review-v1.schema.json +196 -0
- data/schema/conflict-explanation-study-v1.schema.json +528 -0
- data/schema/construction-profile-v1.schema.json +779 -0
- data/schema/diff-v1.schema.json +41 -0
- data/schema/direct-ielr-decision-v1.schema.json +200 -0
- data/schema/equiv-v1.schema.json +64 -0
- data/schema/error-ux-review-v1.schema.json +609 -0
- data/schema/error-ux-round2-review-v1.schema.json +191 -0
- data/schema/error-ux-round2-v1.schema.json +519 -0
- data/schema/explain-v1.schema.json +63 -1
- data/schema/fix-v1.schema.json +64 -0
- data/schema/fix-v2.schema.json +66 -0
- data/schema/fix-v3.schema.json +101 -0
- data/schema/fuzz-regression-v1.schema.json +79 -0
- data/schema/fuzz-v1.schema.json +114 -0
- data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
- data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
- data/schema/grammar-ir.schema.json +271 -0
- data/schema/ielr-benchmark-v1.schema.json +85 -0
- data/schema/impact-v1.schema.json +208 -0
- data/schema/lexer-profile-v1.schema.json +491 -0
- data/schema/metrics-v1.schema.json +60 -0
- data/schema/reduce-v1.schema.json +22 -0
- data/schema/reduce-v2.schema.json +63 -0
- data/schema/table-artifact-v1.schema.json +377 -0
- data/schema/verification-report-v1.schema.json +222 -0
- data/schema/verify-v1.schema.json +42 -0
- data/sig/ibex/analysis/digraph.rbs +19 -0
- data/sig/ibex/analysis/sets.rbs +10 -0
- data/sig/ibex/bison_import/importer.rbs +140 -0
- data/sig/ibex/bison_import/tokenizer.rbs +91 -0
- data/sig/ibex/bison_import.rbs +95 -0
- data/sig/ibex/bounded_subprocess.rbs +76 -0
- data/sig/ibex/cli/ambiguity.rbs +6 -0
- data/sig/ibex/cli/analysis.rbs +43 -0
- data/sig/ibex/cli/bison_import.rbs +32 -0
- data/sig/ibex/cli/config.rbs +35 -0
- data/sig/ibex/cli/diagnostics.rbs +16 -3
- data/sig/ibex/cli/documentation.rbs +5 -1
- data/sig/ibex/cli/equiv.rbs +49 -0
- data/sig/ibex/cli/error_messages.rbs +6 -0
- data/sig/ibex/cli/explain.rbs +6 -0
- data/sig/ibex/cli/fix.rbs +53 -0
- data/sig/ibex/cli/formatting.rbs +5 -1
- data/sig/ibex/cli/fuzz.rbs +47 -0
- data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
- data/sig/ibex/cli/generation_artifacts.rbs +21 -2
- data/sig/ibex/cli/generation_error_messages.rbs +2 -2
- data/sig/ibex/cli/grammar_tests.rbs +15 -3
- data/sig/ibex/cli/impact.rbs +95 -0
- data/sig/ibex/cli/ir_tools.rbs +7 -19
- data/sig/ibex/cli/outputs.rbs +9 -3
- data/sig/ibex/cli/reduce.rbs +60 -0
- data/sig/ibex/cli/reduce_reporting.rbs +27 -0
- data/sig/ibex/cli/samples.rbs +8 -0
- data/sig/ibex/cli/verify.rbs +28 -0
- data/sig/ibex/cli/watch.rbs +2 -2
- data/sig/ibex/cli.rbs +79 -5
- data/sig/ibex/codegen/action_method_source.rbs +4 -4
- data/sig/ibex/codegen/explain.rbs +45 -25
- data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
- data/sig/ibex/codegen/rbs.rbs +12 -10
- data/sig/ibex/codegen/report.rbs +12 -12
- data/sig/ibex/codegen/ruby.rbs +18 -15
- data/sig/ibex/codegen/ruby_actions.rbs +6 -6
- data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
- data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
- data/sig/ibex/configuration/explanation.rbs +195 -0
- data/sig/ibex/configuration/inspector.rbs +53 -0
- data/sig/ibex/configuration.rbs +242 -0
- data/sig/ibex/coverage/collector.rbs +16 -14
- data/sig/ibex/coverage/event_stream.rbs +12 -10
- data/sig/ibex/coverage/report.rbs +20 -18
- data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
- data/sig/ibex/delta_reducer.rbs +38 -0
- data/sig/ibex/diff.rbs +47 -0
- data/sig/ibex/equiv/machine.rbs +54 -0
- data/sig/ibex/equiv.rbs +129 -0
- data/sig/ibex/fix.rbs +138 -0
- data/sig/ibex/frontend/ast.rbs +33 -7
- data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
- data/sig/ibex/frontend/diagnostic.rbs +2 -2
- data/sig/ibex/frontend/formatter.rbs +38 -30
- data/sig/ibex/frontend/generated_parser.rbs +87 -77
- data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
- data/sig/ibex/frontend/parser/declarations.rbs +3 -0
- data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
- data/sig/ibex/frontend/resolution.rbs +3 -2
- data/sig/ibex/frontend/source_cursor.rbs +2 -2
- data/sig/ibex/frontend/source_span.rbs +5 -2
- data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
- data/sig/ibex/frontend/token_adapter.rbs +6 -0
- data/sig/ibex/fuzz.rbs +71 -0
- data/sig/ibex/generation_input.rbs +2 -2
- data/sig/ibex/generation_manifest.rbs +35 -32
- data/sig/ibex/generation_transaction_recovery.rbs +2 -2
- data/sig/ibex/generation_transaction_validation.rbs +2 -2
- data/sig/ibex/grammar_tests.rbs +2 -2
- data/sig/ibex/impact/action_impact.rbs +58 -0
- data/sig/ibex/impact/automaton_impact.rbs +37 -0
- data/sig/ibex/impact/coverage_impact.rbs +28 -0
- data/sig/ibex/impact/graph.rbs +67 -0
- data/sig/ibex/impact/propagation.rbs +56 -0
- data/sig/ibex/impact/report.rbs +107 -0
- data/sig/ibex/impact/seeds.rbs +36 -0
- data/sig/ibex/impact/severity.rbs +55 -0
- data/sig/ibex/impact.rbs +6 -0
- data/sig/ibex/ir/automaton_ir.rbs +30 -11
- data/sig/ibex/ir/grammar_ir.rbs +41 -27
- data/sig/ibex/ir/lexer_ir.rbs +8 -8
- data/sig/ibex/ir/parser_contract.rbs +69 -0
- data/sig/ibex/ir/serialize.rbs +63 -28
- data/sig/ibex/ir/validator/automaton.rbs +47 -44
- data/sig/ibex/ir/validator/base.rbs +35 -31
- data/sig/ibex/ir/validator/grammar.rbs +78 -67
- data/sig/ibex/ir/validator/lexer.rbs +4 -4
- data/sig/ibex/ir.rbs +9 -3
- data/sig/ibex/lalr/build_metrics.rbs +42 -2
- data/sig/ibex/lalr/builder.rbs +75 -24
- data/sig/ibex/lalr/conflict_search.rbs +5 -2
- data/sig/ibex/lalr/counterexample.rbs +17 -14
- data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
- data/sig/ibex/lalr/goto_follows.rbs +89 -0
- data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
- data/sig/ibex/lalr/ielr/bits.rbs +14 -0
- data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
- data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
- data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
- data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
- data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
- data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
- data/sig/ibex/lalr/ielr_partition.rbs +17 -5
- data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
- data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
- data/sig/ibex/lalr/lr0_collection.rbs +50 -0
- data/sig/ibex/lalr/unreachable_states.rbs +25 -0
- data/sig/ibex/lalr.rbs +11 -1
- data/sig/ibex/location.rbs +6 -6
- data/sig/ibex/lsp/document_handlers.rbs +8 -8
- data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
- data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
- data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
- data/sig/ibex/lsp/position_codec.rbs +8 -4
- data/sig/ibex/lsp/request_handlers.rbs +4 -4
- data/sig/ibex/lsp/request_support.rbs +8 -8
- data/sig/ibex/lsp/server.rbs +16 -16
- data/sig/ibex/lsp/symbol_index.rbs +20 -21
- data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
- data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
- data/sig/ibex/lsp/transport.rbs +18 -6
- data/sig/ibex/lsp/workspace.rbs +3 -2
- data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
- data/sig/ibex/messages.rbs +26 -0
- data/sig/ibex/metrics.rbs +50 -0
- data/sig/ibex/normalize/diagnostics.rbs +4 -4
- data/sig/ibex/normalize/expander.rbs +2 -2
- data/sig/ibex/normalize/expression.rbs +10 -8
- data/sig/ibex/normalize/grammar_builder.rbs +11 -0
- data/sig/ibex/normalize/inline_expansion.rbs +59 -50
- data/sig/ibex/normalize/lexer.rbs +4 -4
- data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
- data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
- data/sig/ibex/normalize/parameters.rbs +13 -8
- data/sig/ibex/normalize/parser_configuration.rbs +23 -0
- data/sig/ibex/normalize.rbs +11 -5
- data/sig/ibex/racc_migration/report.rbs +8 -6
- data/sig/ibex/rake_task.rbs +1 -1
- data/sig/ibex/samples.rbs +13 -4
- data/sig/ibex/table_artifact/builder.rbs +93 -0
- data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
- data/sig/ibex/table_artifact/document.rbs +27 -0
- data/sig/ibex/table_artifact/executor.rbs +66 -0
- data/sig/ibex/table_artifact/serializer.rbs +25 -0
- data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
- data/sig/ibex/table_artifact/validator/support.rbs +46 -0
- data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
- data/sig/ibex/table_artifact/validator.rbs +66 -0
- data/sig/ibex/table_artifact.rbs +32 -0
- data/sig/ibex/table_simulation/result.rbs +8 -4
- data/sig/ibex/table_simulation/step.rbs +4 -2
- data/sig/ibex/tables.rbs +10 -9
- data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
- data/sig/ibex/verification_report/builder.rbs +53 -0
- data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
- data/sig/ibex/verification_report/logical_path.rbs +34 -0
- data/sig/ibex/verification_report/validator.rbs +114 -0
- data/sig/ibex/verification_report.rbs +44 -0
- data/sig/ibex/verify/action_correspondence.rbs +50 -0
- data/sig/ibex/verify/language_witness.rbs +112 -0
- data/sig/ibex/verify/reference_collection.rbs +63 -0
- data/sig/ibex/verify/result.rbs +52 -0
- data/sig/ibex/verify/verifier.rbs +170 -0
- data/sig/ibex/verify.rbs +6 -0
- data/sig/ibex/watch/runner.rbs +12 -2
- data/sig/ibex/watch/source_snapshot.rbs +13 -9
- metadata +254 -12
- data/lib/ibex/ir/migration.rb +0 -120
- data/sig/ibex/ir/migration.rbs +0 -34
|
@@ -6,7 +6,11 @@ require "json"
|
|
|
6
6
|
|
|
7
7
|
module Ibex
|
|
8
8
|
# Deterministic description of one published parser generation.
|
|
9
|
+
# rubocop:disable Metrics/ModuleLength -- manifest schema validation is kept in one auditable unit.
|
|
9
10
|
module GenerationManifest
|
|
11
|
+
# @rbs!
|
|
12
|
+
# type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
|
|
13
|
+
|
|
10
14
|
SCHEMA_VERSION = 1 #: Integer
|
|
11
15
|
IDENTIFIER = "generation" #: String
|
|
12
16
|
ROOT_KEYS = %w[ibex_manifest schema_version input options artifacts].freeze #: Array[String]
|
|
@@ -16,7 +20,7 @@ module Ibex
|
|
|
16
20
|
|
|
17
21
|
module_function
|
|
18
22
|
|
|
19
|
-
# @rbs (ArtifactSet artifacts, source_records: Array[GenerationInput], options: Hash[String,
|
|
23
|
+
# @rbs (ArtifactSet artifacts, source_records: Array[GenerationInput], options: Hash[String, Object?]) -> String
|
|
20
24
|
def render(artifacts, source_records:, options:)
|
|
21
25
|
files = source_records.map(&:to_h)
|
|
22
26
|
document = {
|
|
@@ -34,24 +38,26 @@ module Ibex
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
# Validate the manifest shape and, by default, every published artifact digest.
|
|
37
|
-
# @rbs (String source, ?verify_artifacts: bool) -> Hash[String,
|
|
41
|
+
# @rbs (String source, ?verify_artifacts: bool) -> Hash[String, json_value]
|
|
38
42
|
def validate(source, verify_artifacts: true)
|
|
39
|
-
document = JSON.parse(source)
|
|
43
|
+
document = JSON.parse(source) #: Object?
|
|
40
44
|
validate_document(document)
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
document_hash = document #: Hash[String, json_value]
|
|
46
|
+
artifacts = document_hash.fetch("artifacts") #: Array[Object?]
|
|
47
|
+
verify_entries(artifacts) if verify_artifacts
|
|
48
|
+
document_hash
|
|
43
49
|
rescue JSON::ParserError, KeyError, TypeError, ArgumentError => e
|
|
44
50
|
raise Ibex::Error, "(manifest):1:1: invalid generation manifest: #{e.message}"
|
|
45
51
|
end
|
|
46
52
|
|
|
47
|
-
# @rbs (String path, ?verify_artifacts: bool) -> Hash[String,
|
|
53
|
+
# @rbs (String path, ?verify_artifacts: bool) -> Hash[String, json_value]
|
|
48
54
|
def validate_file(path, verify_artifacts: true)
|
|
49
55
|
validate(File.binread(path), verify_artifacts: verify_artifacts)
|
|
50
56
|
rescue SystemCallError, ArgumentError => e
|
|
51
57
|
raise Ibex::Error, "#{path}:1:1: cannot read generation manifest: #{e.message}"
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
# @rbs (Artifact artifact) -> Hash[String,
|
|
60
|
+
# @rbs (Artifact artifact) -> Hash[String, json_value]
|
|
55
61
|
def artifact_entry(artifact)
|
|
56
62
|
{
|
|
57
63
|
"kind" => artifact.kind.to_s,
|
|
@@ -62,19 +68,20 @@ module Ibex
|
|
|
62
68
|
end
|
|
63
69
|
private_class_method :artifact_entry
|
|
64
70
|
|
|
65
|
-
# @rbs (Array[Hash[String,
|
|
71
|
+
# @rbs (Array[Hash[String, json_value]] files) -> String
|
|
66
72
|
def input_digest(files)
|
|
67
73
|
digest = Digest::SHA256.new
|
|
68
74
|
files.each do |entry|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
path = entry.fetch("path") # @type var path: String
|
|
76
|
+
sha256 = entry.fetch("sha256") # @type var sha256: String
|
|
77
|
+
bytesize = entry.fetch("bytesize") # @type var bytesize: Integer
|
|
78
|
+
digest << path << "\0" << sha256 << "\0" << bytesize.to_s << "\0"
|
|
72
79
|
end
|
|
73
80
|
digest.hexdigest
|
|
74
81
|
end
|
|
75
82
|
private_class_method :input_digest
|
|
76
83
|
|
|
77
|
-
# @rbs (Hash[String,
|
|
84
|
+
# @rbs (Hash[String, Object?] value) -> Hash[String, Object?]
|
|
78
85
|
def sorted_hash(value)
|
|
79
86
|
value.keys.sort.to_h do |key|
|
|
80
87
|
child = value.fetch(key)
|
|
@@ -84,7 +91,7 @@ module Ibex
|
|
|
84
91
|
end
|
|
85
92
|
private_class_method :sorted_hash
|
|
86
93
|
|
|
87
|
-
# @rbs (
|
|
94
|
+
# @rbs (Object? document) -> void
|
|
88
95
|
def validate_document(document)
|
|
89
96
|
raise TypeError, "document must be an object" unless document.is_a?(Hash)
|
|
90
97
|
|
|
@@ -100,14 +107,14 @@ module Ibex
|
|
|
100
107
|
end
|
|
101
108
|
private_class_method :validate_document
|
|
102
109
|
|
|
103
|
-
# @rbs (Hash[
|
|
110
|
+
# @rbs (Hash[Object?, Object?] document) -> void
|
|
104
111
|
def validate_identity(document)
|
|
105
112
|
raise TypeError, "unsupported manifest identifier" unless document["ibex_manifest"] == IDENTIFIER
|
|
106
113
|
raise TypeError, "unsupported schema_version" unless document["schema_version"] == SCHEMA_VERSION
|
|
107
114
|
end
|
|
108
115
|
private_class_method :validate_identity
|
|
109
116
|
|
|
110
|
-
# @rbs (
|
|
117
|
+
# @rbs (Object? input) -> void
|
|
111
118
|
def validate_input(input)
|
|
112
119
|
raise TypeError, "input must be an object" unless input.is_a?(Hash)
|
|
113
120
|
|
|
@@ -125,13 +132,13 @@ module Ibex
|
|
|
125
132
|
end
|
|
126
133
|
private_class_method :validate_input
|
|
127
134
|
|
|
128
|
-
# @rbs (Array[
|
|
135
|
+
# @rbs (Array[Object?] entries, String label, ?require_kind: bool) -> void
|
|
129
136
|
def validate_entries(entries, label, require_kind: false)
|
|
130
137
|
entries.each { |entry| validate_entry(entry, label, require_kind) }
|
|
131
138
|
end
|
|
132
139
|
private_class_method :validate_entries
|
|
133
140
|
|
|
134
|
-
# @rbs (
|
|
141
|
+
# @rbs (Object? entry, String label, bool require_kind) -> void
|
|
135
142
|
def validate_entry(entry, label, require_kind)
|
|
136
143
|
raise TypeError, "#{label} must be an object" unless entry.is_a?(Hash)
|
|
137
144
|
|
|
@@ -146,7 +153,7 @@ module Ibex
|
|
|
146
153
|
end
|
|
147
154
|
private_class_method :validate_entry
|
|
148
155
|
|
|
149
|
-
# @rbs (
|
|
156
|
+
# @rbs (Object? path, String label) -> void
|
|
150
157
|
def validate_path(path, label)
|
|
151
158
|
raise TypeError, "#{label} path must be a non-empty string" unless path.is_a?(String) && !path.empty?
|
|
152
159
|
return unless path.include?("\0") || path.match?(/[[:cntrl:]]/)
|
|
@@ -155,7 +162,7 @@ module Ibex
|
|
|
155
162
|
end
|
|
156
163
|
private_class_method :validate_path
|
|
157
164
|
|
|
158
|
-
# @rbs (
|
|
165
|
+
# @rbs (Object? kind, String label) -> void
|
|
159
166
|
def validate_kind(kind, label)
|
|
160
167
|
return if kind.is_a?(String) && !kind.empty?
|
|
161
168
|
|
|
@@ -163,7 +170,7 @@ module Ibex
|
|
|
163
170
|
end
|
|
164
171
|
private_class_method :validate_kind
|
|
165
172
|
|
|
166
|
-
# @rbs (
|
|
173
|
+
# @rbs (Object? digest, String label) -> void
|
|
167
174
|
def validate_digest(digest, label)
|
|
168
175
|
return if digest.is_a?(String) && digest.match?(/\A[0-9a-f]{64}\z/)
|
|
169
176
|
|
|
@@ -171,7 +178,7 @@ module Ibex
|
|
|
171
178
|
end
|
|
172
179
|
private_class_method :validate_digest
|
|
173
180
|
|
|
174
|
-
# @rbs (Hash[
|
|
181
|
+
# @rbs (Hash[Object?, Object?] value, Array[String] expected, String label) -> void
|
|
175
182
|
def exact_keys!(value, expected, label)
|
|
176
183
|
keys = value.keys
|
|
177
184
|
raise TypeError, "#{label} keys must be strings" unless keys.all?(String)
|
|
@@ -183,18 +190,22 @@ module Ibex
|
|
|
183
190
|
end
|
|
184
191
|
private_class_method :exact_keys!
|
|
185
192
|
|
|
186
|
-
# @rbs (Array[
|
|
193
|
+
# @rbs (Array[Object?] entries) -> void
|
|
187
194
|
def verify_entries(entries)
|
|
188
195
|
entries.each do |entry|
|
|
189
|
-
|
|
196
|
+
entry_hash = entry #: Hash[String, json_value]
|
|
197
|
+
path = entry_hash.fetch("path") #: String
|
|
190
198
|
source = File.binread(path)
|
|
191
|
-
|
|
199
|
+
bytesize = entry_hash.fetch("bytesize") #: Integer
|
|
200
|
+
sha256 = entry_hash.fetch("sha256") #: String
|
|
201
|
+
raise TypeError, "artifact bytesize mismatch for #{path}" unless source.bytesize == bytesize
|
|
192
202
|
raise TypeError, "artifact digest mismatch for #{path}" unless
|
|
193
|
-
Digest::SHA256.hexdigest(source) ==
|
|
203
|
+
Digest::SHA256.hexdigest(source) == sha256
|
|
194
204
|
end
|
|
195
205
|
rescue SystemCallError => e
|
|
196
206
|
raise TypeError, "cannot read artifact: #{e.message}"
|
|
197
207
|
end
|
|
198
208
|
private_class_method :verify_entries
|
|
199
209
|
end
|
|
210
|
+
# rubocop:enable Metrics/ModuleLength
|
|
200
211
|
end
|
|
@@ -37,7 +37,7 @@ module Ibex
|
|
|
37
37
|
@lock_sleeper = lock_sleeper
|
|
38
38
|
@locks = [] #: Array[File]
|
|
39
39
|
@locked_paths = [] #: Array[String]
|
|
40
|
-
@records = [] #: Array[Hash[Symbol,
|
|
40
|
+
@records = [] #: Array[Hash[Symbol, Object?]]
|
|
41
41
|
@committed = false
|
|
42
42
|
@temporary_sequence = 0
|
|
43
43
|
end
|
|
@@ -57,14 +57,14 @@ module Ibex
|
|
|
57
57
|
failures
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
# @rbs (Hash[Symbol,
|
|
60
|
+
# @rbs (Hash[Symbol, Object?] record) -> void
|
|
61
61
|
def rollback_record(record)
|
|
62
|
-
target = record.fetch(:target)
|
|
62
|
+
target = record.fetch(:target) #: String
|
|
63
63
|
if record.fetch(:backed_up)
|
|
64
64
|
if record.fetch(:installed)
|
|
65
|
-
File.rename(record.fetch(:backup), target)
|
|
65
|
+
File.rename(record.fetch(:backup).to_s, target)
|
|
66
66
|
else
|
|
67
|
-
File.unlink(record.fetch(:backup))
|
|
67
|
+
File.unlink(record.fetch(:backup).to_s)
|
|
68
68
|
end
|
|
69
69
|
record[:backup] = nil
|
|
70
70
|
record[:backed_up] = false
|
|
@@ -170,9 +170,9 @@ module Ibex
|
|
|
170
170
|
"(generation):1:1: cannot revalidate artifact target #{phase}: #{e.message}"
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
# @rbs (Hash[Symbol,
|
|
173
|
+
# @rbs (Hash[Symbol, Object?] record, Array[String] sources, String phase) -> void
|
|
174
174
|
def validate_current_target!(record, sources, phase)
|
|
175
|
-
artifact = record.fetch(:artifact)
|
|
175
|
+
artifact = record.fetch(:artifact) #: Artifact
|
|
176
176
|
current = resolved_target(artifact.path, validate_links: false)
|
|
177
177
|
unless current == record.fetch(:target)
|
|
178
178
|
raise GenerationTransaction::SourceChanged,
|
data/lib/ibex/grammar_tests.rb
CHANGED
|
@@ -182,7 +182,7 @@ module Ibex
|
|
|
182
182
|
raise Ibex::Error, "(test):1:1: invalid grammar test process result: #{e.message}"
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
-
# @rbs (IR::grammar_test test,
|
|
185
|
+
# @rbs (IR::grammar_test test, Object? document) -> Result
|
|
186
186
|
def build_result(test, document)
|
|
187
187
|
unless document.is_a?(Hash) && %w[accept reject error].include?(document["actual"])
|
|
188
188
|
raise Ibex::Error, "(test):1:1: grammar test process returned an invalid case result"
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Ibex
|
|
5
|
+
module Impact
|
|
6
|
+
# Checks only structured action metadata; action source remains opaque.
|
|
7
|
+
class ActionImpact
|
|
8
|
+
# @rbs @before: IR::Grammar
|
|
9
|
+
# @rbs @after: IR::Grammar
|
|
10
|
+
# @rbs @affected_names: Array[String]?
|
|
11
|
+
attr_reader :findings #: Array[Hash[Symbol, Object?]]
|
|
12
|
+
|
|
13
|
+
# @rbs (IR::Grammar before, IR::Grammar after, ?affected_names: Array[String]?) -> void
|
|
14
|
+
def initialize(before, after, affected_names: nil)
|
|
15
|
+
@before = before
|
|
16
|
+
@after = after
|
|
17
|
+
@affected_names = affected_names
|
|
18
|
+
@findings = compare
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs () -> Array[Hash[Symbol, Object?]]
|
|
23
|
+
def to_a
|
|
24
|
+
@findings
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# @rbs () -> Array[Hash[Symbol, Object?]]
|
|
30
|
+
def compare
|
|
31
|
+
names = @after.nonterminals.map(&:name)
|
|
32
|
+
names &= @affected_names if @affected_names
|
|
33
|
+
names.sort.flat_map { |name| compare_rule(name) }.sort_by { |finding| finding.fetch(:production) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @rbs (String name) -> Array[Hash[Symbol, Object?]]
|
|
37
|
+
def compare_rule(name)
|
|
38
|
+
before = productions_for(@before, name)
|
|
39
|
+
after = productions_for(@after, name)
|
|
40
|
+
pair_productions(before, after).filter_map do |previous, production|
|
|
41
|
+
next if previous.rhs.length == production.rhs.length
|
|
42
|
+
|
|
43
|
+
finding_for(previous, production)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @rbs (Array[IR::Production] before, Array[IR::Production] after) -> Array[[IR::Production, IR::Production]]
|
|
48
|
+
def pair_productions(before, after)
|
|
49
|
+
unmatched = before.dup
|
|
50
|
+
pairs = pair_exact_productions(unmatched, after)
|
|
51
|
+
pair_by_location(unmatched, after, pairs)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs (Array[IR::Production] unmatched, Array[IR::Production] after) -> Array[[IR::Production, IR::Production]]
|
|
55
|
+
def pair_exact_productions(unmatched, after)
|
|
56
|
+
pairs = [] #: Array[[IR::Production, IR::Production]]
|
|
57
|
+
|
|
58
|
+
after.each do |production|
|
|
59
|
+
index = unmatched.index do |candidate|
|
|
60
|
+
production_signature(@before, candidate) == production_signature(@after, production)
|
|
61
|
+
end
|
|
62
|
+
next unless index
|
|
63
|
+
|
|
64
|
+
pairs << [unmatched.delete_at(index), production]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
pairs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @rbs (Array[IR::Production] unmatched, Array[IR::Production] after,
|
|
71
|
+
# Array[[IR::Production, IR::Production]]) -> Array[[IR::Production, IR::Production]]
|
|
72
|
+
def pair_by_location(unmatched, after, pairs)
|
|
73
|
+
after.each do |production|
|
|
74
|
+
next if pairs.any? { |_, candidate| candidate.equal?(production) }
|
|
75
|
+
|
|
76
|
+
index = unmatched.index do |candidate|
|
|
77
|
+
location = action_location_identity(production)
|
|
78
|
+
location && location == action_location_identity(candidate)
|
|
79
|
+
end
|
|
80
|
+
next unless index
|
|
81
|
+
|
|
82
|
+
pairs << [unmatched.delete_at(index), production]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
pairs
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @rbs (IR::Grammar grammar, String name) -> Array[IR::Production]
|
|
89
|
+
def productions_for(grammar, name)
|
|
90
|
+
lhs = grammar.symbol(name)&.id
|
|
91
|
+
return [] unless lhs
|
|
92
|
+
|
|
93
|
+
grammar.productions.select { |production| production.lhs == lhs }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @rbs (IR::Grammar grammar, IR::Production production) -> [Array[String], String?]
|
|
97
|
+
def production_signature(grammar, production)
|
|
98
|
+
rhs = production.rhs.map { |id| grammar.symbol_by_id(id)&.name || id.to_s }
|
|
99
|
+
precedence = grammar.symbol_by_id(production.precedence_override)&.name
|
|
100
|
+
[rhs, precedence]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @rbs (IR::Production production) -> [Integer, Integer]?
|
|
104
|
+
def action_location_identity(production)
|
|
105
|
+
location = production.origin[:loc] || production.action&.location #: IR::location?
|
|
106
|
+
return unless location
|
|
107
|
+
|
|
108
|
+
line = location[:line]
|
|
109
|
+
column = location[:column]
|
|
110
|
+
return unless line && column
|
|
111
|
+
|
|
112
|
+
[line, column]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @rbs (IR::Production before, IR::Production after) -> Hash[Symbol, Object?]
|
|
116
|
+
def finding_for(before, after)
|
|
117
|
+
action = after.action
|
|
118
|
+
reason, severity = finding_reason(action, before.rhs.length, after.rhs.length)
|
|
119
|
+
{
|
|
120
|
+
production: production_name(@after, after), severity: severity,
|
|
121
|
+
reason: reason,
|
|
122
|
+
context_length: { before: before.rhs.length, after: after.rhs.length },
|
|
123
|
+
loc: after.action&.location || after.origin[:loc]
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @rbs (IR::Action?, Integer, Integer) -> [String, String]
|
|
128
|
+
def finding_reason(action, before_length, after_length)
|
|
129
|
+
return %w[named_ref_index_out_of_range high] if out_of_range?(action, after_length)
|
|
130
|
+
return %w[context_length_stale high] if action&.context_length == before_length
|
|
131
|
+
|
|
132
|
+
%w[rhs_length_changed medium]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @rbs (IR::Action?, Integer) -> bool
|
|
136
|
+
def out_of_range?(action, length)
|
|
137
|
+
action&.named_refs&.any? { |reference| reference.fetch(:index) >= length } || false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @rbs (IR::Grammar grammar, IR::Production production) -> String
|
|
141
|
+
def production_name(grammar, production)
|
|
142
|
+
lhs = grammar.symbol_by_id(production.lhs)&.name || production.lhs.to_s
|
|
143
|
+
rhs = production.rhs.map { |id| grammar.symbol_by_id(id)&.name || id.to_s }
|
|
144
|
+
"#{lhs} -> #{rhs.join(' ')}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Ibex
|
|
5
|
+
module Impact
|
|
6
|
+
# Maps propagated grammar symbols to parser states and conflict changes.
|
|
7
|
+
class AutomatonImpact
|
|
8
|
+
# @rbs @automaton: IR::Automaton
|
|
9
|
+
# @rbs @symbol_ids: Array[Integer]
|
|
10
|
+
# @rbs @production_ids: Array[Integer]
|
|
11
|
+
attr_reader :affected_states #: Array[Integer]
|
|
12
|
+
attr_reader :production_ids #: Array[Integer]
|
|
13
|
+
attr_reader :conflict_states #: Array[Integer]
|
|
14
|
+
|
|
15
|
+
# @rbs (IR::Automaton automaton, Array[Integer]) -> void
|
|
16
|
+
def initialize(automaton, symbol_ids)
|
|
17
|
+
@automaton = automaton
|
|
18
|
+
@symbol_ids = symbol_ids.uniq.sort #: Array[Integer]
|
|
19
|
+
@production_ids = affected_productions
|
|
20
|
+
@affected_states = affected_state_ids
|
|
21
|
+
@conflict_states = conflict_state_ids
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @rbs (Hash[Symbol, Object?] diff) -> Hash[Symbol, Object?]
|
|
26
|
+
def conflict_changes(diff)
|
|
27
|
+
diff.fetch(:conflicts) #: Hash[Symbol, Object?]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs () -> Hash[Symbol, Object?]
|
|
31
|
+
def to_h
|
|
32
|
+
{ states: @affected_states, productions: @production_ids, conflict_states: @conflict_states }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# @rbs () -> Array[Integer]
|
|
38
|
+
def affected_productions
|
|
39
|
+
@automaton.grammar.productions.filter_map do |production|
|
|
40
|
+
production.id if @symbol_ids.include?(production.lhs) || production.rhs.any? { |id| @symbol_ids.include?(id) }
|
|
41
|
+
end.sort
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @rbs () -> Array[Integer]
|
|
45
|
+
def affected_state_ids
|
|
46
|
+
@automaton.states.filter_map do |state|
|
|
47
|
+
state.id if state.items.any? { |item| @production_ids.include?(item.production) }
|
|
48
|
+
end.sort
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @rbs () -> Array[Integer]
|
|
52
|
+
def conflict_state_ids
|
|
53
|
+
@automaton.states.filter_map { |state| state.id unless state.conflicts.empty? }.sort
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Ibex
|
|
5
|
+
module Impact
|
|
6
|
+
# Selects separated runtime coverage reports for affected productions.
|
|
7
|
+
class CoverageImpact
|
|
8
|
+
attr_reader :status #: String
|
|
9
|
+
attr_reader :reports #: Array[Hash[Symbol, Object?]]
|
|
10
|
+
attr_reader :warnings #: Array[String]
|
|
11
|
+
|
|
12
|
+
# @rbs (IR::Automaton automaton, Array[Integer], Array[[String, Coverage::Report]]) -> void
|
|
13
|
+
def initialize(automaton, production_ids, reports)
|
|
14
|
+
@automaton = automaton
|
|
15
|
+
@production_ids = production_ids.sort
|
|
16
|
+
@warnings = []
|
|
17
|
+
@reports = select_reports(reports)
|
|
18
|
+
@status = coverage_status(reports)
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs () -> Hash[Symbol, Object?]
|
|
23
|
+
def to_h
|
|
24
|
+
{ status: @status, reports: @reports }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# @rbs (Array[[String, Coverage::Report]]) -> Array[Hash[Symbol, Object?]]
|
|
30
|
+
def select_reports(reports)
|
|
31
|
+
selected = reports.filter_map do |path, report|
|
|
32
|
+
unless report.grammar_digest == @automaton.grammar_digest
|
|
33
|
+
@warnings << "#{path}: coverage grammar digest does not match the analysis input; report ignored"
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
hits = report.production_hits.keys & @production_ids
|
|
38
|
+
next if hits.empty?
|
|
39
|
+
|
|
40
|
+
{ path: path, productions: hits.sort }
|
|
41
|
+
end
|
|
42
|
+
selected.sort_by { |entry| entry.fetch(:path) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @rbs (Array[[String, Coverage::Report]]) -> String
|
|
46
|
+
def coverage_status(reports)
|
|
47
|
+
return "not_requested" if reports.empty?
|
|
48
|
+
|
|
49
|
+
@reports.empty? ? "unmatched" : "matched"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative "../analysis"
|
|
5
|
+
|
|
6
|
+
module Ibex
|
|
7
|
+
module Impact
|
|
8
|
+
# A grammar dependency edge with enough origin information for a witness.
|
|
9
|
+
class Edge
|
|
10
|
+
attr_reader :source #: Integer
|
|
11
|
+
attr_reader :target #: Integer
|
|
12
|
+
attr_reader :kind #: Symbol
|
|
13
|
+
attr_reader :production #: Integer?
|
|
14
|
+
attr_reader :position #: Integer?
|
|
15
|
+
|
|
16
|
+
# @rbs (source: Integer, target: Integer, kind: Symbol, production: Integer?, position: Integer?) -> void
|
|
17
|
+
def initialize(source:, target:, kind:, production: nil, position: nil)
|
|
18
|
+
@source = source
|
|
19
|
+
@target = target
|
|
20
|
+
@kind = kind.to_sym
|
|
21
|
+
@production = production
|
|
22
|
+
@position = position
|
|
23
|
+
freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @rbs () -> Array[Integer | Symbol | nil]
|
|
27
|
+
def sort_key
|
|
28
|
+
[@source, @target, @production || -1, @position || -1, @kind]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Combines reference, FIRST, and FOLLOW propagation dependencies.
|
|
33
|
+
class Graph
|
|
34
|
+
EDGE_KINDS = %i[reference first follow_lhs follow_first].freeze #: Array[Symbol]
|
|
35
|
+
KIND_ALIASES = {
|
|
36
|
+
all: EDGE_KINDS, reference: [:reference], first: [:first], follow: %i[follow_lhs follow_first],
|
|
37
|
+
follow_lhs: [:follow_lhs], follow_first: [:follow_first]
|
|
38
|
+
}.freeze #: Hash[Symbol, Array[Symbol]]
|
|
39
|
+
|
|
40
|
+
attr_reader :grammar #: IR::Grammar
|
|
41
|
+
attr_reader :sets #: Analysis::Sets
|
|
42
|
+
|
|
43
|
+
# @rbs (IR::Grammar grammar, ?sets: Analysis::Sets) -> void
|
|
44
|
+
def initialize(grammar, sets: nil)
|
|
45
|
+
@grammar = grammar
|
|
46
|
+
@sets = sets || Analysis::Sets.new(grammar)
|
|
47
|
+
@edges = build_edges
|
|
48
|
+
freeze_edges
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @rbs (?Symbol kind) -> untyped
|
|
52
|
+
def edges(kind = :all)
|
|
53
|
+
selected = KIND_ALIASES.fetch(kind.to_sym) { raise ArgumentError, "unknown impact edge kind #{kind}" }
|
|
54
|
+
return selected.flat_map { |name| @edges.fetch(name) }.sort_by(&:sort_key) if selected.length > 1
|
|
55
|
+
|
|
56
|
+
@edges.fetch(selected.fetch(0))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @rbs (Symbol kind) -> Array[Array[Integer]]
|
|
60
|
+
def adjacency(kind)
|
|
61
|
+
result = Array.new(@grammar.symbols.length) { [] }
|
|
62
|
+
edges(kind).each { |edge| result[edge.source] << edge.target }
|
|
63
|
+
result.each(&:uniq!)
|
|
64
|
+
result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# @rbs () -> Hash[Symbol, Array[Edge]]
|
|
70
|
+
def build_edges
|
|
71
|
+
result = EDGE_KINDS.to_h { |kind| [kind, []] }
|
|
72
|
+
@grammar.productions.each do |production|
|
|
73
|
+
add_production_edges(result, production)
|
|
74
|
+
end
|
|
75
|
+
result.each_value { |edges| edges.sort_by!(&:sort_key) }
|
|
76
|
+
result
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @rbs (Hash[Symbol, Array[Edge]], IR::Production) -> void
|
|
80
|
+
def add_production_edges(result, production)
|
|
81
|
+
production.rhs.each_with_index do |symbol_id, position|
|
|
82
|
+
next unless @grammar.symbol_by_id(symbol_id)&.nonterminal?
|
|
83
|
+
|
|
84
|
+
result[:reference] << edge(symbol_id, production.lhs, :reference, production, position)
|
|
85
|
+
add_first_edge(result, production, symbol_id, position)
|
|
86
|
+
add_follow_edges(result, production, symbol_id, position)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @rbs (Hash[Symbol, Array[Edge]], IR::Production, Integer, Integer) -> void
|
|
91
|
+
def add_first_edge(result, production, symbol_id, position)
|
|
92
|
+
prefix = production.rhs[0...position] || []
|
|
93
|
+
return unless @sets.sequence_nullable?(prefix)
|
|
94
|
+
return unless @sets.first_dependencies.fetch(symbol_id).include?(production.lhs)
|
|
95
|
+
|
|
96
|
+
result[:first] << edge(symbol_id, production.lhs, :first, production, position)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @rbs (Hash[Symbol, Array[Edge]], IR::Production, Integer, Integer) -> void
|
|
100
|
+
def add_follow_edges(result, production, symbol_id, position)
|
|
101
|
+
suffix = production.rhs[(position + 1)..] || []
|
|
102
|
+
if @sets.sequence_nullable?(suffix) && @sets.follow_dependencies.fetch(production.lhs).include?(symbol_id)
|
|
103
|
+
result[:follow_lhs] << edge(production.lhs, symbol_id, :follow_lhs, production, position)
|
|
104
|
+
end
|
|
105
|
+
add_follow_first_edges(result, production, symbol_id, position)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @rbs (Hash[Symbol, Array[Edge]], IR::Production, Integer, Integer) -> void
|
|
109
|
+
def add_follow_first_edges(result, production, symbol_id, position)
|
|
110
|
+
suffix = production.rhs[(position + 1)..] || []
|
|
111
|
+
suffix.each_with_index do |candidate, offset|
|
|
112
|
+
candidate_prefix = suffix[0...offset] || []
|
|
113
|
+
break unless @sets.sequence_nullable?(candidate_prefix)
|
|
114
|
+
break unless @grammar.symbol_by_id(candidate)&.nonterminal?
|
|
115
|
+
|
|
116
|
+
result[:follow_first] << edge(candidate, symbol_id, :follow_first, production, position + 1 + offset)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @rbs (Integer source, Integer target, Symbol kind, IR::Production production, Integer position) -> Edge
|
|
121
|
+
def edge(source, target, kind, production, position)
|
|
122
|
+
Edge.new(source: source, target: target, kind: kind, production: production.id, position: position)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @rbs () -> void
|
|
126
|
+
def freeze_edges
|
|
127
|
+
@edges.each_value(&:freeze)
|
|
128
|
+
@edges.freeze
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|