ibex 0.2.0 → 0.3.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 +95 -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 +262 -0
- data/docs/development.md +179 -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 +1581 -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/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 +128 -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 +307 -0
- data/docs/stability.md +144 -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.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/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 +357 -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 +140 -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/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 +10 -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/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/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/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/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 +232 -12
- data/lib/ibex/ir/migration.rb +0 -120
- data/sig/ibex/ir/migration.rbs +0 -34
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Ibex
|
|
5
|
+
module Configuration
|
|
6
|
+
# One observed configuration source and how it participated in selection.
|
|
7
|
+
class Evidence
|
|
8
|
+
STATUSES = %i[accepted ignored duplicate conflicting].freeze #: Array[Symbol]
|
|
9
|
+
SOURCES = %i[grammar project cli].freeze #: Array[Symbol]
|
|
10
|
+
|
|
11
|
+
attr_reader :key #: Key
|
|
12
|
+
attr_reader :source #: Symbol
|
|
13
|
+
attr_reader :value #: config_value
|
|
14
|
+
attr_reader :status #: Symbol
|
|
15
|
+
attr_reader :location #: Location?
|
|
16
|
+
attr_reader :reason #: String
|
|
17
|
+
|
|
18
|
+
# @rbs (Key key, source: Symbol, value: config_value, status: Symbol, reason: String,
|
|
19
|
+
# ?location: Location?) -> void
|
|
20
|
+
def initialize(key, source:, value:, status:, reason:, location: nil)
|
|
21
|
+
raise ArgumentError, "configuration evidence key must be a Configuration::Key" unless key.is_a?(Key)
|
|
22
|
+
raise ArgumentError, "unknown configuration evidence source: #{source.inspect}" unless SOURCES.include?(source)
|
|
23
|
+
raise ArgumentError, "unknown configuration evidence status: #{status.inspect}" unless STATUSES.include?(status)
|
|
24
|
+
unless location.nil? || location.is_a?(Location)
|
|
25
|
+
raise ArgumentError, "configuration evidence location must be an Ibex::Location"
|
|
26
|
+
end
|
|
27
|
+
raise ArgumentError, "configuration evidence reason must not be empty" if reason.empty?
|
|
28
|
+
|
|
29
|
+
@key = key
|
|
30
|
+
@source = source
|
|
31
|
+
@value = key.validate(value)
|
|
32
|
+
@status = status
|
|
33
|
+
@location = location
|
|
34
|
+
@reason = reason.dup.freeze
|
|
35
|
+
freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @rbs () -> Hash[String, json_value]
|
|
39
|
+
def to_h
|
|
40
|
+
result = {
|
|
41
|
+
"source" => @source.to_s,
|
|
42
|
+
"value" => json_value(@value),
|
|
43
|
+
"status" => @status.to_s,
|
|
44
|
+
"reason" => @reason
|
|
45
|
+
} #: Hash[String, json_value]
|
|
46
|
+
location = @location
|
|
47
|
+
result["location"] = location.to_h.transform_keys(&:to_s) if location
|
|
48
|
+
result
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# @rbs (config_value value) -> (String | Integer | bool | nil)
|
|
54
|
+
def json_value(value)
|
|
55
|
+
value.is_a?(Symbol) ? value.to_s : value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Whether an input can prove the historical source of a selected setting.
|
|
60
|
+
class Recording
|
|
61
|
+
STATES = %i[source recorded unspecified unavailable not_applicable].freeze #: Array[Symbol]
|
|
62
|
+
|
|
63
|
+
attr_reader :state #: Symbol
|
|
64
|
+
attr_reader :reason #: String
|
|
65
|
+
|
|
66
|
+
# @rbs (Symbol state, String reason) -> void
|
|
67
|
+
def initialize(state, reason)
|
|
68
|
+
raise ArgumentError, "unknown configuration recording state: #{state.inspect}" unless STATES.include?(state)
|
|
69
|
+
raise ArgumentError, "configuration recording reason must not be empty" if reason.empty?
|
|
70
|
+
|
|
71
|
+
@state = state
|
|
72
|
+
@reason = reason.dup.freeze
|
|
73
|
+
freeze
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @rbs () -> Hash[String, String]
|
|
77
|
+
def to_h
|
|
78
|
+
{ "state" => @state.to_s.tr("_", "-"), "reason" => @reason }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Canonical, static input facts used to build a configuration report.
|
|
83
|
+
class Input
|
|
84
|
+
KINDS = %i[grammar_source grammar_ir].freeze #: Array[Symbol]
|
|
85
|
+
|
|
86
|
+
attr_reader :kind #: Symbol
|
|
87
|
+
attr_reader :path #: String
|
|
88
|
+
attr_reader :files #: Array[String]
|
|
89
|
+
attr_reader :schema_version #: Integer?
|
|
90
|
+
attr_reader :grammar_values #: Hash[String, config_value]
|
|
91
|
+
attr_reader :grammar_locations #: Hash[String, Location]
|
|
92
|
+
attr_reader :evidence #: Hash[String, Array[Evidence]]
|
|
93
|
+
attr_reader :recordings #: Hash[String, Recording]
|
|
94
|
+
|
|
95
|
+
# @rbs (kind: Symbol, path: String, ?files: Array[String], ?schema_version: Integer?,
|
|
96
|
+
# ?grammar_values: Hash[String, config_value], ?grammar_locations: Hash[String, Location],
|
|
97
|
+
# ?evidence: Hash[String, Array[Evidence]], ?recordings: Hash[String, Recording]) -> void
|
|
98
|
+
def initialize(kind:, path:, files: [path], schema_version: nil, grammar_values: {}, grammar_locations: {},
|
|
99
|
+
evidence: {}, recordings: {})
|
|
100
|
+
raise ArgumentError, "unknown configuration input kind: #{kind.inspect}" unless KINDS.include?(kind)
|
|
101
|
+
unless path.is_a?(String) && !path.empty?
|
|
102
|
+
raise ArgumentError, "configuration input path must be a non-empty String"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
validate_files!(files, path)
|
|
106
|
+
validate_schema_version!(kind, schema_version)
|
|
107
|
+
validate_entries!(grammar_values, grammar_locations, evidence, recordings)
|
|
108
|
+
assign_input(
|
|
109
|
+
kind, path, files, schema_version, grammar_values, grammar_locations, evidence, recordings
|
|
110
|
+
)
|
|
111
|
+
freeze
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @rbs () -> Hash[String, json_value]
|
|
115
|
+
def to_h
|
|
116
|
+
result = {
|
|
117
|
+
"kind" => @kind.to_s.tr("_", "-"),
|
|
118
|
+
"path" => @path,
|
|
119
|
+
"files" => @files
|
|
120
|
+
} #: Hash[String, json_value]
|
|
121
|
+
result["schema_version"] = @schema_version if @schema_version
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# @rbs (Symbol kind, String path, Array[String] files, Integer? schema_version,
|
|
128
|
+
# Hash[String, config_value] grammar_values, Hash[String, Location] grammar_locations,
|
|
129
|
+
# Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
|
|
130
|
+
def assign_input(kind, path, files, schema_version, grammar_values, grammar_locations, evidence, recordings)
|
|
131
|
+
@kind = kind
|
|
132
|
+
@path = path.dup.freeze
|
|
133
|
+
@files = files.map { |file| file.dup.freeze }.freeze
|
|
134
|
+
@schema_version = schema_version
|
|
135
|
+
@grammar_values = immutable_values(grammar_values)
|
|
136
|
+
@grammar_locations = grammar_locations.to_h { |key, location| [key.dup.freeze, location] }.freeze
|
|
137
|
+
@evidence = evidence.to_h { |key, entries| [key.dup.freeze, entries.dup.freeze] }.freeze
|
|
138
|
+
@recordings = recordings.to_h { |key, recording| [key.dup.freeze, recording] }.freeze
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @rbs (Array[String] files, String path) -> void
|
|
142
|
+
def validate_files!(files, path)
|
|
143
|
+
unless files.is_a?(Array) && !files.empty? && files.all? { |file| file.is_a?(String) && !file.empty? }
|
|
144
|
+
raise ArgumentError, "configuration input files must be non-empty Strings"
|
|
145
|
+
end
|
|
146
|
+
raise ArgumentError, "configuration input files must be unique" unless files.uniq.length == files.length
|
|
147
|
+
raise ArgumentError, "configuration input path must be the first file" unless files.first == path
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @rbs (Symbol kind, Integer? schema_version) -> void
|
|
151
|
+
def validate_schema_version!(kind, schema_version)
|
|
152
|
+
if kind == :grammar_source
|
|
153
|
+
raise ArgumentError, "grammar source input cannot have a schema version" unless schema_version.nil?
|
|
154
|
+
elsif !schema_version.is_a?(Integer) || !schema_version.positive?
|
|
155
|
+
raise ArgumentError, "Grammar IR input requires a positive schema version"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @rbs (Hash[String, config_value] values, Hash[String, Location] locations,
|
|
160
|
+
# Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
|
|
161
|
+
def validate_entries!(values, locations, evidence, recordings)
|
|
162
|
+
validate_fact_hashes!(values, locations, evidence, recordings)
|
|
163
|
+
names = values.keys | locations.keys | evidence.keys | recordings.keys
|
|
164
|
+
names.each do |name|
|
|
165
|
+
raise ArgumentError, "configuration input keys must be Strings" unless name.is_a?(String)
|
|
166
|
+
|
|
167
|
+
Registry.fetch(name)
|
|
168
|
+
end
|
|
169
|
+
validate_locations!(values, locations)
|
|
170
|
+
validate_evidence!(evidence)
|
|
171
|
+
validate_recordings!(recordings)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @rbs (*Hash[Object?, Object?] values) -> void
|
|
175
|
+
def validate_fact_hashes!(*values)
|
|
176
|
+
return if values.all?(Hash)
|
|
177
|
+
|
|
178
|
+
raise ArgumentError, "configuration input facts must be Hash values"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# @rbs (Hash[String, config_value] values, Hash[String, Location] locations) -> void
|
|
182
|
+
def validate_locations!(values, locations)
|
|
183
|
+
locations.each do |name, location|
|
|
184
|
+
raise ArgumentError, "configuration location for #{name} has no grammar value" unless values.key?(name)
|
|
185
|
+
raise ArgumentError, "configuration location must be an Ibex::Location" unless location.is_a?(Location)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @rbs (Hash[String, Array[Evidence]] evidence) -> void
|
|
190
|
+
def validate_evidence!(evidence)
|
|
191
|
+
evidence.each do |name, entries|
|
|
192
|
+
key = Registry.fetch(name)
|
|
193
|
+
unless entries.is_a?(Array) && entries.all? { |entry| entry.is_a?(Evidence) && entry.key.equal?(key) }
|
|
194
|
+
raise ArgumentError, "configuration evidence for #{name} has the wrong key"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @rbs (Hash[String, Recording] recordings) -> void
|
|
200
|
+
def validate_recordings!(recordings)
|
|
201
|
+
recordings.each do |name, recording|
|
|
202
|
+
next if recording.is_a?(Recording)
|
|
203
|
+
|
|
204
|
+
raise ArgumentError, "configuration recording for #{name} must be a Recording"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# @rbs (Hash[String, config_value] values) -> Hash[String, config_value]
|
|
209
|
+
def immutable_values(values)
|
|
210
|
+
values.to_h do |name, value|
|
|
211
|
+
[name.dup.freeze, Registry.fetch(name).validate(value)]
|
|
212
|
+
end.freeze
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# A fixed-contract mismatch retained as structured report data.
|
|
217
|
+
class ConflictRecord
|
|
218
|
+
attr_reader :key #: Key
|
|
219
|
+
attr_reader :declared #: Value
|
|
220
|
+
attr_reader :requested #: Value
|
|
221
|
+
|
|
222
|
+
# @rbs (Conflict conflict) -> void
|
|
223
|
+
def initialize(conflict)
|
|
224
|
+
raise ArgumentError, "conflict record requires a Configuration::Conflict" unless conflict.is_a?(Conflict)
|
|
225
|
+
|
|
226
|
+
@key = conflict.key
|
|
227
|
+
@declared = conflict.declared
|
|
228
|
+
@requested = conflict.requested
|
|
229
|
+
freeze
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @rbs () -> Hash[String, json_value]
|
|
233
|
+
def to_h
|
|
234
|
+
{
|
|
235
|
+
"key" => @key.name,
|
|
236
|
+
"policy" => @key.policy.to_s,
|
|
237
|
+
"declared" => selection(@declared),
|
|
238
|
+
"requested" => selection(@requested)
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# @rbs () -> String
|
|
243
|
+
def message
|
|
244
|
+
location = @declared.origin.location
|
|
245
|
+
prefix = location ? "#{location.file || '(source)'}:#{location.line}:#{location.column}: " : "(config):1:1: "
|
|
246
|
+
"#{prefix}configuration conflict for #{@key.name}: " \
|
|
247
|
+
"#{@declared.origin.label} selected #{@declared.value.inspect}, " \
|
|
248
|
+
"#{@requested.origin.label} requested #{@requested.value.inspect}"
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
# @rbs (Value value) -> Hash[String, json_value]
|
|
254
|
+
def selection(value)
|
|
255
|
+
serialized = value.to_h
|
|
256
|
+
{ "value" => serialized.fetch("value"), "origin" => serialized.fetch("origin") }
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# The effective value and all accepted or rejected evidence for one key.
|
|
261
|
+
class Explanation
|
|
262
|
+
attr_reader :value #: Value
|
|
263
|
+
attr_reader :evidence #: Array[Evidence]
|
|
264
|
+
attr_reader :recording #: Recording
|
|
265
|
+
|
|
266
|
+
# @rbs (Value value, evidence: Array[Evidence], recording: Recording) -> void
|
|
267
|
+
def initialize(value, evidence:, recording:)
|
|
268
|
+
raise ArgumentError, "explanation value must be a Configuration::Value" unless value.is_a?(Value)
|
|
269
|
+
unless recording.is_a?(Recording)
|
|
270
|
+
raise ArgumentError,
|
|
271
|
+
"explanation recording must be a Configuration::Recording"
|
|
272
|
+
end
|
|
273
|
+
unless evidence.all? { |entry| entry.is_a?(Evidence) && entry.key.equal?(value.key) }
|
|
274
|
+
raise ArgumentError, "explanation evidence has the wrong key"
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
@value = value
|
|
278
|
+
@evidence = evidence.dup.freeze
|
|
279
|
+
@recording = recording
|
|
280
|
+
freeze
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# @rbs () -> Hash[String, json_value]
|
|
284
|
+
def to_h
|
|
285
|
+
@value.to_h.merge(
|
|
286
|
+
"selection" => @value.explicit ? "explicit" : "implicit",
|
|
287
|
+
"conformance" => @value.canonical ? "canonical" : "noncanonical",
|
|
288
|
+
"recording" => @recording.to_h,
|
|
289
|
+
"evidence" => @evidence.map(&:to_h)
|
|
290
|
+
)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Deterministic, static-no-user-code explanation of every registered setting.
|
|
295
|
+
class Report
|
|
296
|
+
attr_reader :input #: Input
|
|
297
|
+
attr_reader :explanations #: Array[Explanation]
|
|
298
|
+
attr_reader :conflicts #: Array[ConflictRecord]
|
|
299
|
+
|
|
300
|
+
# @rbs (Input input, ?cli: Hash[String, config_value]) -> void
|
|
301
|
+
def initialize(input, cli: {})
|
|
302
|
+
raise ArgumentError, "configuration report input must be a Configuration::Input" unless input.is_a?(Input)
|
|
303
|
+
|
|
304
|
+
cli.each_key { |name| Registry.fetch(name) }
|
|
305
|
+
|
|
306
|
+
@input = input
|
|
307
|
+
conflicts = [] #: Array[ConflictRecord]
|
|
308
|
+
@explanations = Registry.keys.sort_by(&:name).map do |key|
|
|
309
|
+
value = resolve_key(key, cli)
|
|
310
|
+
explanation(value, cli)
|
|
311
|
+
rescue Conflict => e
|
|
312
|
+
record = ConflictRecord.new(e)
|
|
313
|
+
conflicts << record
|
|
314
|
+
explanation(e.declared, cli, conflict: record)
|
|
315
|
+
end.freeze
|
|
316
|
+
@conflicts = conflicts.freeze
|
|
317
|
+
freeze
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# @rbs () -> bool
|
|
321
|
+
def success?
|
|
322
|
+
@conflicts.empty?
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# @rbs () -> Hash[String, json_value]
|
|
326
|
+
def to_h
|
|
327
|
+
{
|
|
328
|
+
"ibex_report" => "configuration",
|
|
329
|
+
"schema_version" => 1,
|
|
330
|
+
"trust" => "static-no-user-code",
|
|
331
|
+
"status" => success? ? "ok" : "conflict",
|
|
332
|
+
"input" => @input.to_h,
|
|
333
|
+
"configuration" => @explanations.map(&:to_h),
|
|
334
|
+
"conflicts" => @conflicts.map(&:to_h)
|
|
335
|
+
}
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Deterministic JSON independent of caller hash insertion order.
|
|
339
|
+
# @rbs () -> String
|
|
340
|
+
def dump
|
|
341
|
+
"#{JSON.generate(to_h)}\n"
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# @rbs () -> String
|
|
345
|
+
def render_text
|
|
346
|
+
lines = ["configuration status=#{success? ? 'ok' : 'conflict'} (static-no-user-code): #{@input.path}"]
|
|
347
|
+
@explanations.each { |entry| lines.concat(render_explanation(entry)) }
|
|
348
|
+
@conflicts.each { |conflict| lines << "conflict: #{conflict.message}" }
|
|
349
|
+
"#{lines.join("\n")}\n"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
private
|
|
353
|
+
|
|
354
|
+
# @rbs (Key key, Hash[String, config_value] cli) -> Value
|
|
355
|
+
def resolve_key(key, cli)
|
|
356
|
+
name = key.name
|
|
357
|
+
grammar = {} #: Hash[String, config_value]
|
|
358
|
+
grammar[name] = @input.grammar_values.fetch(name) if @input.grammar_values.key?(name)
|
|
359
|
+
request = {} #: Hash[String, config_value]
|
|
360
|
+
request[name] = cli.fetch(name) if cli.key?(name)
|
|
361
|
+
locations = {} #: Hash[Symbol, Hash[String, Location]]
|
|
362
|
+
locations[:grammar] = { name => @input.grammar_locations.fetch(name) } \
|
|
363
|
+
if @input.grammar_locations.key?(name)
|
|
364
|
+
Resolver.new(keys: [key], grammar: grammar, cli: request, locations: locations).values.fetch(0)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# @rbs (Value value, Hash[String, config_value] cli, ?conflict: ConflictRecord?) -> Explanation
|
|
368
|
+
def explanation(value, cli, conflict: nil)
|
|
369
|
+
key = value.key
|
|
370
|
+
evidence = @input.evidence.fetch(key.name, []).dup
|
|
371
|
+
evidence << cli_evidence(key, cli.fetch(key.name), conflicting: !conflict.nil?) if cli.key?(key.name)
|
|
372
|
+
recording = @input.recordings.fetch(
|
|
373
|
+
key.name,
|
|
374
|
+
Recording.new(:not_applicable, "this setting has no historical source contract in this input")
|
|
375
|
+
)
|
|
376
|
+
Explanation.new(value, evidence: evidence, recording: recording)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# @rbs (Key key, config_value raw, conflicting: bool) -> Evidence
|
|
380
|
+
def cli_evidence(key, raw, conflicting:)
|
|
381
|
+
value = key.validate(raw)
|
|
382
|
+
grammar = @input.grammar_values
|
|
383
|
+
status = conflicting ? :conflicting : :accepted
|
|
384
|
+
reason = if conflicting
|
|
385
|
+
"conflicts with the fixed grammar contract and was not selected"
|
|
386
|
+
elsif grammar.key?(key.name) && grammar.fetch(key.name) == value
|
|
387
|
+
"matches the fixed grammar contract"
|
|
388
|
+
else
|
|
389
|
+
"selected canonical command-line request"
|
|
390
|
+
end
|
|
391
|
+
Evidence.new(key, source: :cli, value: value, status: status, reason: reason)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# @rbs (config_value value) -> String
|
|
395
|
+
def display(value)
|
|
396
|
+
value.nil? ? "null" : value.to_s
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# @rbs (Location location) -> String
|
|
400
|
+
def location_label(location)
|
|
401
|
+
"#{location.file || '(source)'}:#{location.line}:#{location.column}"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# @rbs (Explanation entry) -> Array[String]
|
|
405
|
+
def render_explanation(entry)
|
|
406
|
+
value = entry.value
|
|
407
|
+
lines = [
|
|
408
|
+
"#{value.key.name}=#{display(value.value)} owner=#{value.key.owner_name} " \
|
|
409
|
+
"origin=#{value.origin.label} policy=#{value.key.policy} " \
|
|
410
|
+
"#{value.explicit ? 'explicit' : 'implicit'} #{value.canonical ? 'canonical' : 'noncanonical'}",
|
|
411
|
+
" recording=#{entry.recording.state}: #{entry.recording.reason}"
|
|
412
|
+
]
|
|
413
|
+
entry.evidence.each do |evidence|
|
|
414
|
+
location = evidence.location ? " at #{location_label(evidence.location)}" : ""
|
|
415
|
+
lines << " #{evidence.status} #{evidence.source}=#{display(evidence.value)}#{location}: #{evidence.reason}"
|
|
416
|
+
end
|
|
417
|
+
lines
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative "../frontend/ast"
|
|
5
|
+
require_relative "../frontend/resolution"
|
|
6
|
+
require_relative "../ir/grammar_ir"
|
|
7
|
+
require_relative "explanation"
|
|
8
|
+
|
|
9
|
+
module Ibex
|
|
10
|
+
module Configuration
|
|
11
|
+
# Converts parsed source or validated Grammar IR into static configuration facts.
|
|
12
|
+
# rubocop:disable Metrics/ModuleLength -- source and IR adapters share one closed evidence vocabulary.
|
|
13
|
+
module Inspector
|
|
14
|
+
# @rbs (Frontend::Resolution resolution) -> Input
|
|
15
|
+
def from_source(resolution)
|
|
16
|
+
unless resolution.is_a?(Frontend::Resolution)
|
|
17
|
+
raise ArgumentError, "source configuration inspection requires a Frontend::Resolution"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
values = {} #: Hash[String, config_value]
|
|
21
|
+
locations = {} #: Hash[String, Location]
|
|
22
|
+
evidence = {} #: Hash[String, Array[Evidence]]
|
|
23
|
+
root = resolution.root
|
|
24
|
+
extended_loc = root.extended_loc
|
|
25
|
+
add_source_fact(values, locations, evidence, "grammar.mode", :extended, extended_loc) if extended_loc
|
|
26
|
+
add_source_fact(values, locations, evidence, "parser.superclass", root.superclass, root.loc) if root.superclass
|
|
27
|
+
add_source_options(root, values, locations, evidence)
|
|
28
|
+
add_source_parser_contract(root, values, locations, evidence)
|
|
29
|
+
recordings = Registry.keys.to_h do |key|
|
|
30
|
+
[key.name,
|
|
31
|
+
Recording.new(:source, "the grammar source and contained import closure were inspected statically")]
|
|
32
|
+
end
|
|
33
|
+
Input.new(
|
|
34
|
+
kind: :grammar_source, path: resolution.root_path, files: resolution.files,
|
|
35
|
+
grammar_values: values, grammar_locations: locations, evidence: evidence, recordings: recordings
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
module_function :from_source
|
|
39
|
+
|
|
40
|
+
# @rbs (IR::Grammar grammar, path: String) -> Input
|
|
41
|
+
def from_grammar_ir(grammar, path:)
|
|
42
|
+
raise ArgumentError, "configuration inspection requires Grammar IR" unless grammar.is_a?(IR::Grammar)
|
|
43
|
+
|
|
44
|
+
values = {} #: Hash[String, config_value]
|
|
45
|
+
locations = {} #: Hash[String, Location]
|
|
46
|
+
evidence = {} #: Hash[String, Array[Evidence]]
|
|
47
|
+
recordings = Registry.keys.to_h do |key|
|
|
48
|
+
reason = "Grammar IR does not persist #{key.name}; " \
|
|
49
|
+
"current builtin or CLI selection is not historical evidence"
|
|
50
|
+
[key.name, Recording.new(:unavailable, reason)]
|
|
51
|
+
end
|
|
52
|
+
add_ir_fact(values, evidence, recordings, "grammar.mode", grammar.mode) if grammar.mode != :default
|
|
53
|
+
add_ir_fact(values, evidence, recordings, "parser.superclass", grammar.superclass) if grammar.superclass
|
|
54
|
+
add_ir_fact(values, evidence, recordings, "actions.omit_calls", grammar.options.fetch(:omit_action_call))
|
|
55
|
+
add_parser_contract(grammar, values, locations, evidence, recordings)
|
|
56
|
+
Input.new(
|
|
57
|
+
kind: :grammar_ir, path: path, schema_version: grammar.schema_version,
|
|
58
|
+
grammar_values: values, grammar_locations: locations, evidence: evidence, recordings: recordings
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
module_function :from_grammar_ir
|
|
62
|
+
|
|
63
|
+
# @rbs (Hash[String, config_value] values, Hash[String, Location] locations,
|
|
64
|
+
# Hash[String, Array[Evidence]] evidence, String name, config_value value,
|
|
65
|
+
# Frontend::Location | Location location) -> void
|
|
66
|
+
def add_source_fact(values, locations, evidence, name, value, location)
|
|
67
|
+
key = Registry.fetch(name)
|
|
68
|
+
location = configuration_location(location)
|
|
69
|
+
values[name] = value
|
|
70
|
+
locations[name] = location
|
|
71
|
+
evidence[name] = [
|
|
72
|
+
Evidence.new(
|
|
73
|
+
key, source: :grammar, value: value, status: :accepted, location: location,
|
|
74
|
+
reason: "selected by the grammar source"
|
|
75
|
+
)
|
|
76
|
+
]
|
|
77
|
+
end
|
|
78
|
+
module_function :add_source_fact
|
|
79
|
+
private_class_method :add_source_fact
|
|
80
|
+
|
|
81
|
+
# @rbs (Frontend::AST::Root root, Hash[String, config_value] values, Hash[String, Location] locations,
|
|
82
|
+
# Hash[String, Array[Evidence]] evidence) -> void
|
|
83
|
+
def add_source_options(root, values, locations, evidence)
|
|
84
|
+
occurrences = root.declarations.filter_map do |declaration|
|
|
85
|
+
next unless declaration.is_a?(Frontend::AST::Options)
|
|
86
|
+
|
|
87
|
+
declaration.names.filter_map do |name|
|
|
88
|
+
value = option_value(name)
|
|
89
|
+
[value, configuration_location(declaration.loc)] unless value.nil?
|
|
90
|
+
end
|
|
91
|
+
end.flatten(1)
|
|
92
|
+
return if occurrences.empty?
|
|
93
|
+
|
|
94
|
+
name = "actions.omit_calls"
|
|
95
|
+
key = Registry.fetch(name)
|
|
96
|
+
selected_value, selected_location = occurrences.last
|
|
97
|
+
values[name] = selected_value
|
|
98
|
+
locations[name] = selected_location
|
|
99
|
+
evidence[name] = option_evidence(key, occurrences, selected_value)
|
|
100
|
+
end
|
|
101
|
+
module_function :add_source_options
|
|
102
|
+
private_class_method :add_source_options
|
|
103
|
+
|
|
104
|
+
# @rbs (Frontend::AST::Root root, Hash[String, config_value] values, Hash[String, Location] locations,
|
|
105
|
+
# Hash[String, Array[Evidence]] evidence) -> void
|
|
106
|
+
def add_source_parser_contract(root, values, locations, evidence)
|
|
107
|
+
declaration = root.declarations.grep(Frontend::AST::ParserConfiguration).first
|
|
108
|
+
return unless declaration.is_a?(Frontend::AST::ParserConfiguration)
|
|
109
|
+
|
|
110
|
+
declaration.settings.each do |setting|
|
|
111
|
+
key = setting.key == :cst_trivia ? "cst.trivia" : "parser.#{setting.key}"
|
|
112
|
+
add_source_fact(
|
|
113
|
+
values, locations, evidence, key, setting.value, setting.loc
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
module_function :add_source_parser_contract
|
|
118
|
+
private_class_method :add_source_parser_contract
|
|
119
|
+
|
|
120
|
+
# @rbs (String name) -> bool?
|
|
121
|
+
def option_value(name)
|
|
122
|
+
return true if name == "omit_action_call"
|
|
123
|
+
return false if name == "no_omit_action_call"
|
|
124
|
+
|
|
125
|
+
nil
|
|
126
|
+
end
|
|
127
|
+
module_function :option_value
|
|
128
|
+
private_class_method :option_value
|
|
129
|
+
|
|
130
|
+
# @rbs (Frontend::Location | Location location) -> Location
|
|
131
|
+
def configuration_location(location)
|
|
132
|
+
return location if location.is_a?(Ibex::Location)
|
|
133
|
+
|
|
134
|
+
Ibex::Location.new(file: location.file, line: location.line, column: location.column)
|
|
135
|
+
end
|
|
136
|
+
module_function :configuration_location
|
|
137
|
+
private_class_method :configuration_location
|
|
138
|
+
|
|
139
|
+
# @rbs (Key key, Array[[bool, Location]] occurrences, bool selected_value) -> Array[Evidence]
|
|
140
|
+
def option_evidence(key, occurrences, selected_value)
|
|
141
|
+
occurrences.each_with_index.map do |(value, location), index|
|
|
142
|
+
last = index == occurrences.length - 1
|
|
143
|
+
status = if last
|
|
144
|
+
:accepted
|
|
145
|
+
else
|
|
146
|
+
(value == selected_value ? :duplicate : :conflicting)
|
|
147
|
+
end
|
|
148
|
+
reason = if last
|
|
149
|
+
"last compatible options declaration selects the effective value"
|
|
150
|
+
elsif value == selected_value
|
|
151
|
+
"later identical source evidence selects the same value"
|
|
152
|
+
else
|
|
153
|
+
"later compatible source evidence supersedes this value"
|
|
154
|
+
end
|
|
155
|
+
Evidence.new(key, source: :grammar, value: value, status: status, location: location, reason: reason)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
module_function :option_evidence
|
|
159
|
+
private_class_method :option_evidence
|
|
160
|
+
|
|
161
|
+
# @rbs (Hash[String, config_value] values, Hash[String, Array[Evidence]] evidence,
|
|
162
|
+
# Hash[String, Recording] recordings, String name, config_value value) -> void
|
|
163
|
+
def add_ir_fact(values, evidence, recordings, name, value)
|
|
164
|
+
key = Registry.fetch(name)
|
|
165
|
+
values[name] = value
|
|
166
|
+
evidence[name] = [
|
|
167
|
+
Evidence.new(
|
|
168
|
+
key, source: :grammar, value: value, status: :accepted,
|
|
169
|
+
reason: "selected by persisted Grammar IR configuration; original declaration location is unavailable"
|
|
170
|
+
)
|
|
171
|
+
]
|
|
172
|
+
recordings[name] = Recording.new(
|
|
173
|
+
:recorded, "Grammar IR records the effective value but not the original declaration location"
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
module_function :add_ir_fact
|
|
177
|
+
private_class_method :add_ir_fact
|
|
178
|
+
|
|
179
|
+
# @rbs (IR::Grammar grammar, Hash[String, config_value] values, Hash[String, Location] locations,
|
|
180
|
+
# Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
|
|
181
|
+
def add_parser_contract(grammar, values, locations, evidence, recordings)
|
|
182
|
+
contract = grammar.parser_contract
|
|
183
|
+
contract_entries(contract).each do |name, entry|
|
|
184
|
+
add_contract_entry(name, entry, values, locations, evidence, recordings)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
module_function :add_parser_contract
|
|
188
|
+
private_class_method :add_parser_contract
|
|
189
|
+
|
|
190
|
+
# @rbs (String name, IR::ParserContract::Entry entry, Hash[String, config_value] values,
|
|
191
|
+
# Hash[String, Location] locations, Hash[String, Array[Evidence]] evidence,
|
|
192
|
+
# Hash[String, Recording] recordings) -> void
|
|
193
|
+
def add_contract_entry(name, entry, values, locations, evidence, recordings)
|
|
194
|
+
unless entry.explicit
|
|
195
|
+
recordings[name] = Recording.new(
|
|
196
|
+
:unspecified,
|
|
197
|
+
"The current Grammar IR marks this parser contract field unspecified; " \
|
|
198
|
+
"the current builtin or CLI value is not a historical fact"
|
|
199
|
+
)
|
|
200
|
+
return
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
value = entry.value || raise("explicit parser contract entry has no value")
|
|
204
|
+
location = entry.location || raise("explicit parser contract entry has no location")
|
|
205
|
+
key = Registry.fetch(name)
|
|
206
|
+
values[name] = value
|
|
207
|
+
locations[name] = location
|
|
208
|
+
evidence[name] = [
|
|
209
|
+
Evidence.new(
|
|
210
|
+
key, source: :grammar, value: value, status: :accepted, location: location,
|
|
211
|
+
reason: "selected by the current Grammar IR parser contract"
|
|
212
|
+
)
|
|
213
|
+
]
|
|
214
|
+
recordings[name] = Recording.new(:recorded, "The current Grammar IR records an explicit parser contract")
|
|
215
|
+
end
|
|
216
|
+
module_function :add_contract_entry
|
|
217
|
+
private_class_method :add_contract_entry
|
|
218
|
+
|
|
219
|
+
# @rbs (IR::ParserContract contract) -> Hash[String, IR::ParserContract::Entry]
|
|
220
|
+
def contract_entries(contract)
|
|
221
|
+
{
|
|
222
|
+
"parser.algorithm" => contract.algorithm,
|
|
223
|
+
"parser.entries" => contract.entries,
|
|
224
|
+
"cst.trivia" => contract.cst_trivia
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
module_function :contract_entries
|
|
228
|
+
private_class_method :contract_entries
|
|
229
|
+
end
|
|
230
|
+
# rubocop:enable Metrics/ModuleLength
|
|
231
|
+
end
|
|
232
|
+
end
|