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,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "digest"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
module Ibex
|
|
9
|
+
# @rbs!
|
|
10
|
+
# type fuzz_external_description = {
|
|
11
|
+
# command: String,
|
|
12
|
+
# target_runtime: String,
|
|
13
|
+
# timeout_seconds: Integer,
|
|
14
|
+
# max_output_bytes: Integer,
|
|
15
|
+
# host_ruby_engine: String,
|
|
16
|
+
# host_ruby_version: String,
|
|
17
|
+
# host_platform: String
|
|
18
|
+
# }
|
|
19
|
+
# type fuzz_external_target = [
|
|
20
|
+
# ^(Array[String]) -> Symbol,
|
|
21
|
+
# fuzz_external_description
|
|
22
|
+
# ]
|
|
23
|
+
|
|
24
|
+
# Automatic bounded minimization and persistence for fuzz differences.
|
|
25
|
+
module CLIFuzzRegressions
|
|
26
|
+
# @rbs!
|
|
27
|
+
# private def atomic_write_ir: (String path, String source) -> void
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# @rbs (Fuzz fuzzer, Fuzz::Mismatch mismatch, String grammar_path,
|
|
32
|
+
# fuzz_external_target? external) -> Integer
|
|
33
|
+
def write_minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
|
|
34
|
+
details = minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
|
|
35
|
+
report = {
|
|
36
|
+
ibex_report: "fuzz", schema_version: 1, result: "difference", mismatch: details
|
|
37
|
+
} #: Hash[Symbol, untyped]
|
|
38
|
+
report[:external] = external[1] if external
|
|
39
|
+
write_fuzz_report(report)
|
|
40
|
+
1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @rbs (Hash[Symbol, untyped] report) -> void
|
|
44
|
+
def write_fuzz_report(report)
|
|
45
|
+
if @options.fetch(:fuzz_format, "json") == "json"
|
|
46
|
+
@stdout.puts JSON.pretty_generate(report)
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@stdout.puts("result=#{report.fetch(:result)}")
|
|
51
|
+
case report.fetch(:result)
|
|
52
|
+
when "no_difference_within_bounds"
|
|
53
|
+
@stdout.puts("seed=#{report.fetch(:seed)} bounds=#{report.fetch(:bounds).inspect}")
|
|
54
|
+
@stdout.puts("no difference found within the declared bounds; this is not a proof of equivalence")
|
|
55
|
+
when "difference"
|
|
56
|
+
mismatch = report.fetch(:mismatch) #: Hash[Symbol, untyped]
|
|
57
|
+
@stdout.puts("kind=#{mismatch.fetch(:kind)} tokens=#{mismatch.fetch(:tokens).inspect}")
|
|
58
|
+
@stdout.puts("minimized=#{mismatch.fetch(:minimized_tokens).inspect} " \
|
|
59
|
+
"reduction_complete=#{mismatch.dig(:reduction, :complete)}")
|
|
60
|
+
@stdout.puts("regression=#{mismatch.dig(:regression, :path)}") if mismatch[:regression]
|
|
61
|
+
when "budget_exhausted"
|
|
62
|
+
@stdout.puts("search incomplete: #{report.dig(:budget, :message)}")
|
|
63
|
+
end
|
|
64
|
+
external = report[:external] #: fuzz_external_description?
|
|
65
|
+
return unless external
|
|
66
|
+
|
|
67
|
+
@stdout.puts("target_runtime=#{external.fetch(:target_runtime)}")
|
|
68
|
+
@stdout.puts("target_command=#{external.fetch(:command)}")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @rbs (Fuzz::BudgetExceeded error,
|
|
72
|
+
# fuzz_external_target? external, phase: String) -> Integer
|
|
73
|
+
def write_fuzz_budget_report(error, external, phase:)
|
|
74
|
+
report = {
|
|
75
|
+
ibex_report: "fuzz", schema_version: 1, result: "budget_exhausted",
|
|
76
|
+
budget: error.details.merge(phase: phase)
|
|
77
|
+
} #: Hash[Symbol, untyped]
|
|
78
|
+
report[:external] = external[1] if external
|
|
79
|
+
write_fuzz_report(report)
|
|
80
|
+
2
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @rbs (Fuzz fuzzer, Fuzz::Mismatch mismatch, String grammar_path,
|
|
84
|
+
# fuzz_external_target? external) -> Hash[Symbol, untyped]
|
|
85
|
+
def minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
|
|
86
|
+
result = fuzzer.minimize(
|
|
87
|
+
mismatch, max_trials: @options.fetch(:fuzz_max_reduction_trials, 1_000)
|
|
88
|
+
)
|
|
89
|
+
details = mismatch.details.dup #: Hash[Symbol, untyped]
|
|
90
|
+
details[:minimized_tokens] = result.items
|
|
91
|
+
details[:reduction] = {
|
|
92
|
+
original_size: result.original_size, minimized_size: result.items.length,
|
|
93
|
+
trials: result.trials, complete: result.complete
|
|
94
|
+
}
|
|
95
|
+
regression = persist_fuzz_regression(
|
|
96
|
+
mismatch.details, result, grammar_path, external&.[](1)
|
|
97
|
+
)
|
|
98
|
+
details[:regression] = regression if regression
|
|
99
|
+
details
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @rbs (fuzz_mismatch mismatch, DeltaReducer::Result result, String grammar_path,
|
|
103
|
+
# fuzz_external_description? external) -> Hash[Symbol, String]?
|
|
104
|
+
def persist_fuzz_regression(mismatch, result, grammar_path, external)
|
|
105
|
+
return if @options.fetch(:fuzz_save_regression, true) == false
|
|
106
|
+
|
|
107
|
+
directory = @options.fetch(:fuzz_regression_dir, "test/fuzz/regressions")
|
|
108
|
+
FileUtils.mkdir_p(directory)
|
|
109
|
+
document = fuzz_regression_document(mismatch, result, grammar_path, external)
|
|
110
|
+
source = "#{JSON.pretty_generate(document)}\n"
|
|
111
|
+
digest = Digest::SHA256.hexdigest(source)
|
|
112
|
+
seed = @options.fetch(:fuzz_seed, 0)
|
|
113
|
+
path = File.join(directory, "fuzz-seed-#{seed}-#{digest.slice(0, 16)}.json")
|
|
114
|
+
validate_fuzz_regression_target!(path)
|
|
115
|
+
atomic_write_ir(path, source)
|
|
116
|
+
{ path: path, sha256: digest }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @rbs (String path) -> void
|
|
120
|
+
def validate_fuzz_regression_target!(path)
|
|
121
|
+
return unless File.symlink?(path) || (File.exist?(path) && File.stat(path).nlink > 1)
|
|
122
|
+
|
|
123
|
+
raise Ibex::Error, "(fuzz):1:1: regression output refuses symlinks and files with multiple hard links"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @rbs (fuzz_mismatch mismatch, DeltaReducer::Result result, String grammar_path,
|
|
127
|
+
# fuzz_external_description? external) -> Hash[Symbol, Object?]
|
|
128
|
+
def fuzz_regression_document(mismatch, result, grammar_path, external)
|
|
129
|
+
document = {
|
|
130
|
+
ibex_report: "fuzz-regression", schema_version: 1, grammar: grammar_path,
|
|
131
|
+
seed: @options.fetch(:fuzz_seed, 0), kind: mismatch[:kind],
|
|
132
|
+
sentence: mismatch[:sentence],
|
|
133
|
+
algorithms: mismatch[:outcomes].keys.reject { |name| name == :external }.map(&:to_s),
|
|
134
|
+
original_tokens: mismatch[:tokens], minimized_tokens: result.items,
|
|
135
|
+
bounds: mismatch[:bounds],
|
|
136
|
+
reduction: {
|
|
137
|
+
original_size: result.original_size, minimized_size: result.items.length,
|
|
138
|
+
trials: result.trials, complete: result.complete
|
|
139
|
+
}
|
|
140
|
+
} #: Hash[Symbol, Object?]
|
|
141
|
+
document[:external] = external if external
|
|
142
|
+
document
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -12,6 +12,8 @@ module Ibex
|
|
|
12
12
|
# private def report_status: (String) -> void
|
|
13
13
|
# private def verify_file: (String, String, String) -> void
|
|
14
14
|
# private def default_output_path: (String, String) -> String
|
|
15
|
+
# private def configuration_value: (String) -> Object?
|
|
16
|
+
# private def effective_configuration: () -> Configuration::Resolver
|
|
15
17
|
|
|
16
18
|
private
|
|
17
19
|
|
|
@@ -21,6 +23,7 @@ module Ibex
|
|
|
21
23
|
@generation_statuses = [] #: Array[String]
|
|
22
24
|
@generation_sources = [] #: Array[String]
|
|
23
25
|
@generation_inputs = [] #: Array[GenerationInput]
|
|
26
|
+
@generation_automaton = nil
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
# @rbs (Symbol kind, String path, String content, ?mode: Integer?, ?status: bool) -> Artifact
|
|
@@ -120,19 +123,67 @@ module Ibex
|
|
|
120
123
|
default_output_path(parser.path, ".ibex.json")
|
|
121
124
|
end
|
|
122
125
|
|
|
123
|
-
# @rbs () -> Hash[String,
|
|
126
|
+
# @rbs () -> Hash[String, Object?]
|
|
124
127
|
def generation_manifest_options
|
|
125
128
|
keys = %i[
|
|
126
129
|
action_source algorithm counterexample_max_configurations counterexample_max_tokens debug
|
|
127
130
|
embedded emit entry_isolation executable frozen line_convert line_convert_all messages mode omit_actions rbs
|
|
128
131
|
superclass table
|
|
129
132
|
]
|
|
130
|
-
options = {} #: Hash[String,
|
|
133
|
+
options = {} #: Hash[String, Object?]
|
|
131
134
|
keys.each do |key|
|
|
132
135
|
value = @options[key]
|
|
133
136
|
options[key.to_s] = value unless value.nil?
|
|
134
137
|
end
|
|
138
|
+
options["cst_trivia"] = effective_cst_trivia.to_s
|
|
139
|
+
append_ir_manifest_options(options)
|
|
135
140
|
options
|
|
136
141
|
end
|
|
142
|
+
|
|
143
|
+
# @rbs (Hash[String, Object?] options) -> void
|
|
144
|
+
def append_ir_manifest_options(options)
|
|
145
|
+
automaton = @generation_automaton
|
|
146
|
+
return unless automaton
|
|
147
|
+
|
|
148
|
+
grammar = automaton.grammar
|
|
149
|
+
contract = grammar.parser_contract
|
|
150
|
+
options["grammar_ir"] = {
|
|
151
|
+
"schema_version" => grammar.schema_version,
|
|
152
|
+
"digest" => automaton.grammar_digest,
|
|
153
|
+
"parser_contract" => stringify_contract(contract)
|
|
154
|
+
}
|
|
155
|
+
options["automaton_ir"] = {
|
|
156
|
+
"schema_version" => automaton.schema_version,
|
|
157
|
+
"algorithm" => automaton.algorithm,
|
|
158
|
+
"entry_construction" => automaton.entry_construction,
|
|
159
|
+
"construction_authority" => construction_authority
|
|
160
|
+
}
|
|
161
|
+
options["effective_configuration"] = manifest_effective_configuration
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @rbs () -> String
|
|
165
|
+
def construction_authority
|
|
166
|
+
@options[:from] == "automaton-ir" ? "embedded_automaton" : "grammar_contract"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @rbs () -> Array[Hash[String, Object?]]
|
|
170
|
+
def manifest_effective_configuration
|
|
171
|
+
values = effective_configuration.to_h.fetch("configuration")
|
|
172
|
+
return values unless @options[:from] == "automaton-ir"
|
|
173
|
+
|
|
174
|
+
values.reject { |entry| %w[parser.algorithm parser.entries].include?(entry.fetch("key")) }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @rbs (IR::ParserContract contract) -> Hash[String, Object?]
|
|
178
|
+
def stringify_contract(contract)
|
|
179
|
+
contract.to_h.to_h do |name, entry|
|
|
180
|
+
[name.to_s, entry.to_h { |key, value| [key.to_s, value] }]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# @rbs () -> Symbol
|
|
185
|
+
def effective_cst_trivia
|
|
186
|
+
configuration_value("cst.trivia") #: Symbol
|
|
187
|
+
end
|
|
137
188
|
end
|
|
138
189
|
end
|
|
@@ -16,7 +16,7 @@ module Ibex
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# @rbs (IR::Automaton automaton) -> Hash[Integer,
|
|
19
|
+
# @rbs (IR::Automaton automaton) -> Hash[Integer, String | { id: String, message: String }]
|
|
20
20
|
def configured_error_messages(automaton)
|
|
21
21
|
path = @options[:messages]
|
|
22
22
|
return {} unless path
|
|
@@ -7,26 +7,19 @@ module Ibex
|
|
|
7
7
|
# `%test` command parsing and human-readable result reporting.
|
|
8
8
|
module CLIGrammarTests
|
|
9
9
|
# @rbs!
|
|
10
|
-
# type grammar_test_settings =
|
|
11
|
-
# mode: Symbol,
|
|
12
|
-
# algorithm: Symbol,
|
|
13
|
-
# entry_isolation: bool,
|
|
14
|
-
# timeout: Integer,
|
|
15
|
-
# coverage: Integer?,
|
|
16
|
-
# ?help: bool
|
|
17
|
-
# }
|
|
10
|
+
# type grammar_test_settings = Hash[Symbol, untyped]
|
|
18
11
|
# private def print_help: (OptionParser) -> Integer
|
|
19
12
|
# private def resolve_grammar_path: (String) -> Frontend::Resolution
|
|
20
13
|
# private def handle_grammar_warnings: (IR::Grammar, String) -> void
|
|
21
14
|
# private def build_automaton: (IR::Grammar, String) -> IR::Automaton
|
|
15
|
+
# private def configuration_value: (String) -> untyped
|
|
16
|
+
# private def mark_configuration_option: (Symbol) -> void
|
|
22
17
|
|
|
23
18
|
private
|
|
24
19
|
|
|
25
20
|
# @rbs (Array[String] arguments) -> Integer
|
|
26
21
|
def run_grammar_tests_command(arguments)
|
|
27
|
-
settings =
|
|
28
|
-
mode: :default, algorithm: :lalr, entry_isolation: false, timeout: GrammarTests::DEFAULT_TIMEOUT, coverage: nil
|
|
29
|
-
} #: grammar_test_settings
|
|
22
|
+
settings = default_grammar_test_settings
|
|
30
23
|
options = grammar_test_option_parser(settings)
|
|
31
24
|
remaining = options.parse(arguments)
|
|
32
25
|
return print_help(options) if settings[:help]
|
|
@@ -39,7 +32,7 @@ module Ibex
|
|
|
39
32
|
@options[:algorithm] = settings[:algorithm]
|
|
40
33
|
@options[:entry_isolation] = settings[:entry_isolation]
|
|
41
34
|
resolution = resolve_grammar_path(path)
|
|
42
|
-
grammar =
|
|
35
|
+
grammar = normalize_analysis_grammar(resolution)
|
|
43
36
|
handle_grammar_warnings(grammar, path)
|
|
44
37
|
automaton = build_automaton(grammar, path)
|
|
45
38
|
runner = GrammarTests::Runner.new(automaton, timeout: settings[:timeout])
|
|
@@ -51,16 +44,31 @@ module Ibex
|
|
|
51
44
|
render_grammar_test_coverage(runner.production_coverage(results), coverage, failures)
|
|
52
45
|
end
|
|
53
46
|
|
|
54
|
-
# @rbs (
|
|
47
|
+
# @rbs () -> Hash[Symbol, untyped]
|
|
48
|
+
def default_grammar_test_settings
|
|
49
|
+
{
|
|
50
|
+
mode: Configuration::Registry.fetch("grammar.mode").default,
|
|
51
|
+
algorithm: Configuration::Registry.fetch("parser.algorithm").default,
|
|
52
|
+
entry_isolation: Configuration::Registry.fetch("parser.entries").default == :isolated,
|
|
53
|
+
timeout: GrammarTests::DEFAULT_TIMEOUT, coverage: nil
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @rbs (Hash[Symbol, untyped] settings) -> OptionParser
|
|
55
58
|
def grammar_test_option_parser(settings)
|
|
56
59
|
OptionParser.new do |options|
|
|
57
60
|
options.banner = "Usage: ibex test [options] grammarfile"
|
|
58
|
-
options.on("--mode=MODE", %w[default extended], "grammar mode")
|
|
61
|
+
options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
|
|
62
|
+
settings[:mode] = value.to_sym
|
|
63
|
+
mark_configuration_option(:mode)
|
|
64
|
+
end
|
|
59
65
|
options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
|
|
60
66
|
settings[:algorithm] = value.to_sym
|
|
67
|
+
mark_configuration_option(:algorithm)
|
|
61
68
|
end
|
|
62
69
|
options.on("--entry-isolation", "build independent state sets for each start symbol") do
|
|
63
70
|
settings[:entry_isolation] = true
|
|
71
|
+
mark_configuration_option(:entry_isolation)
|
|
64
72
|
end
|
|
65
73
|
options.on("--timeout=SECONDS", Integer, "whole-suite timeout (default: 10)") do |value|
|
|
66
74
|
raise OptionParser::InvalidArgument, "timeout must be positive" unless value.positive?
|
|
@@ -83,6 +91,16 @@ module Ibex
|
|
|
83
91
|
raise OptionParser::InvalidArgument, "test accepts exactly one grammarfile"
|
|
84
92
|
end
|
|
85
93
|
|
|
94
|
+
# @rbs (Frontend::Resolution resolution) -> IR::Grammar
|
|
95
|
+
def normalize_analysis_grammar(resolution)
|
|
96
|
+
grammar = Normalizer.new(resolution, mode: configuration_value("grammar.mode")).normalize
|
|
97
|
+
activate_analysis_grammar(grammar)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @rbs!
|
|
101
|
+
# private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
|
|
102
|
+
# ?explicit_keys: Array[Symbol]) -> IR::Grammar
|
|
103
|
+
|
|
86
104
|
# @rbs (Array[GrammarTests::Result] results) -> Integer
|
|
87
105
|
def render_grammar_test_results(results)
|
|
88
106
|
failures = 0
|
data/lib/ibex/cli/ir_tools.rb
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "tempfile"
|
|
4
3
|
require_relative "../ir"
|
|
5
4
|
|
|
6
5
|
module Ibex
|
|
7
|
-
#
|
|
8
|
-
# type migrate_ir_options = {
|
|
9
|
-
# paths: Array[String],
|
|
10
|
-
# ?to: Integer,
|
|
11
|
-
# ?output: String
|
|
12
|
-
# }
|
|
13
|
-
|
|
14
|
-
# CLI validation and structural comparison for versioned IR documents.
|
|
6
|
+
# CLI validation and structural comparison for current IR documents.
|
|
15
7
|
module CLIIRTools
|
|
16
|
-
# @rbs!
|
|
17
|
-
# private def same_file_target?: (String left, String right) -> bool
|
|
18
|
-
# private def atomic_write_ir: (String path, String source) -> void
|
|
19
|
-
|
|
20
8
|
private
|
|
21
9
|
|
|
22
10
|
# @rbs (Array[String] arguments) -> Integer
|
|
@@ -30,7 +18,7 @@ module Ibex
|
|
|
30
18
|
else
|
|
31
19
|
"automaton"
|
|
32
20
|
end
|
|
33
|
-
@stdout.puts("valid #{kind} IR
|
|
21
|
+
@stdout.puts("valid current #{kind} IR")
|
|
34
22
|
0
|
|
35
23
|
end
|
|
36
24
|
|
|
@@ -49,50 +37,6 @@ module Ibex
|
|
|
49
37
|
0
|
|
50
38
|
end
|
|
51
39
|
|
|
52
|
-
# @rbs (Array[String] arguments) -> Integer
|
|
53
|
-
def run_migrate_ir_command(arguments)
|
|
54
|
-
settings = migrate_ir_options(arguments)
|
|
55
|
-
input = single_ir_path(settings.fetch(:paths), "migrate-ir")
|
|
56
|
-
output = settings[:output]
|
|
57
|
-
if output && same_file_target?(input, output)
|
|
58
|
-
raise Ibex::Error, "(cli):1:1: migrate-ir input and output paths must be distinct"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
value = IR::Validator.validate(File.read(input))
|
|
62
|
-
target = settings[:to] || raise(Ibex::Error, "(cli):1:1: migrate-ir requires --to=VERSION")
|
|
63
|
-
if value.is_a?(IR::Lexer)
|
|
64
|
-
raise Ibex::Error, "(cli):1:1: lexer IR is already at its only supported schema version" unless
|
|
65
|
-
target == value.schema_version
|
|
66
|
-
|
|
67
|
-
source = IR::Serialize.dump(value)
|
|
68
|
-
output ? atomic_write_ir(output, source) : @stdout.write(source)
|
|
69
|
-
return 0
|
|
70
|
-
end
|
|
71
|
-
unless IR::SUPPORTED_SCHEMA_VERSIONS.include?(target)
|
|
72
|
-
raise Ibex::Error, "(cli):1:1: unsupported migration target schema_version #{target}"
|
|
73
|
-
end
|
|
74
|
-
if target < value.schema_version
|
|
75
|
-
raise Ibex::Error,
|
|
76
|
-
"(cli):1:1: downgrading schema_version #{value.schema_version} to #{target} is not supported"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
source = IR::Serialize.dump(IR::Migration.to_version(value, to: target))
|
|
80
|
-
output ? atomic_write_ir(output, source) : @stdout.write(source)
|
|
81
|
-
0
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# @rbs (Array[String] arguments) -> migrate_ir_options
|
|
85
|
-
def migrate_ir_options(arguments)
|
|
86
|
-
settings = { paths: [] } #: migrate_ir_options
|
|
87
|
-
parser = OptionParser.new do |options|
|
|
88
|
-
options.banner = "Usage: ibex migrate-ir INPUT --to=VERSION [-o FILE]"
|
|
89
|
-
options.on("--to=VERSION", Integer, "target schema version") { |value| settings[:to] = value }
|
|
90
|
-
options.on("-o FILE", "--output=FILE", "write atomically to FILE") { |value| settings[:output] = value }
|
|
91
|
-
end
|
|
92
|
-
settings[:paths] = parser.parse(arguments)
|
|
93
|
-
settings
|
|
94
|
-
end
|
|
95
|
-
|
|
96
40
|
# @rbs (Array[String] arguments, String command) -> String
|
|
97
41
|
def single_ir_path(arguments, command)
|
|
98
42
|
return arguments.first if arguments.length == 1
|
|
@@ -101,7 +45,7 @@ module Ibex
|
|
|
101
45
|
end
|
|
102
46
|
|
|
103
47
|
# @rbs (IR::Grammar | IR::Automaton | IR::Lexer before,
|
|
104
|
-
# IR::Grammar | IR::Automaton | IR::Lexer after) -> Hash[Symbol,
|
|
48
|
+
# IR::Grammar | IR::Automaton | IR::Lexer after) -> Hash[Symbol, Object?]
|
|
105
49
|
def compare_ir(before, after)
|
|
106
50
|
return compare_grammars(before, after) if before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)
|
|
107
51
|
if before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)
|
|
@@ -121,7 +65,7 @@ module Ibex
|
|
|
121
65
|
raise Ibex::Error, "(cli):1:1: unsupported IR comparison"
|
|
122
66
|
end
|
|
123
67
|
|
|
124
|
-
# @rbs (IR::Lexer before, IR::Lexer after) -> Hash[Symbol,
|
|
68
|
+
# @rbs (IR::Lexer before, IR::Lexer after) -> Hash[Symbol, Object?]
|
|
125
69
|
def compare_lexers(before, after)
|
|
126
70
|
before_rules = before.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
|
|
127
71
|
after_rules = after.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
|
|
@@ -136,7 +80,7 @@ module Ibex
|
|
|
136
80
|
}
|
|
137
81
|
end
|
|
138
82
|
|
|
139
|
-
# @rbs (IR::Grammar before, IR::Grammar after) -> Hash[Symbol,
|
|
83
|
+
# @rbs (IR::Grammar before, IR::Grammar after) -> Hash[Symbol, Object?]
|
|
140
84
|
def compare_grammars(before, after)
|
|
141
85
|
before_symbols = before.symbols.map(&:name)
|
|
142
86
|
after_symbols = after.symbols.map(&:name)
|
data/lib/ibex/cli/outputs.rb
CHANGED
|
@@ -5,23 +5,19 @@ module Ibex
|
|
|
5
5
|
module CLIOutputs
|
|
6
6
|
# @rbs!
|
|
7
7
|
# private def register_artifact: (Symbol, String, String, ?mode: Integer?, ?status: bool) -> Artifact
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
subject = warning[:symbol] ? " for #{warning[:symbol]}" : ""
|
|
22
|
-
"lexer pattern#{subject} may exhibit excessive backtracking"
|
|
23
|
-
end
|
|
24
|
-
}.freeze #: Hash[Symbol, ^(IR::grammar_warning) -> String]
|
|
8
|
+
# private def configuration_value: (String) -> Object?
|
|
9
|
+
|
|
10
|
+
WARNING_MESSAGE_IDS = {
|
|
11
|
+
undeclared_terminal: "warning.undeclared_terminal",
|
|
12
|
+
unused_terminal: "warning.unused_terminal",
|
|
13
|
+
unused_precedence: "warning.unused_precedence",
|
|
14
|
+
unreachable_terminal: "warning.unreachable_terminal",
|
|
15
|
+
unreachable_nonterminal: "warning.unreachable_nonterminal",
|
|
16
|
+
duplicate_production: "warning.duplicate_production",
|
|
17
|
+
implicit_empty: "warning.implicit_empty",
|
|
18
|
+
empty_language: "warning.empty_language",
|
|
19
|
+
lexer_redos: "warning.lexer_redos"
|
|
20
|
+
}.freeze #: Hash[Symbol, String]
|
|
25
21
|
|
|
26
22
|
private
|
|
27
23
|
|
|
@@ -42,32 +38,45 @@ module Ibex
|
|
|
42
38
|
categories = @options[:warnings]
|
|
43
39
|
return if categories.nil? || categories.include?(:none) || grammar.warnings.empty?
|
|
44
40
|
|
|
45
|
-
messages = grammar.warnings.map { |warning| format_grammar_warning(warning, input_path) }
|
|
46
41
|
if categories.include?(:error)
|
|
47
|
-
promoted =
|
|
42
|
+
promoted = grammar.warnings.map do |warning|
|
|
43
|
+
format_grammar_warning(warning, input_path, promoted: true)
|
|
44
|
+
end
|
|
48
45
|
raise Ibex::Error, promoted.join("\n")
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
grammar.warnings.each { |warning| @stderr.puts(format_grammar_warning(warning, input_path)) }
|
|
52
49
|
end
|
|
53
50
|
|
|
54
|
-
# @rbs (IR::grammar_warning warning, String input_path) -> String
|
|
55
|
-
def format_grammar_warning(warning, input_path)
|
|
51
|
+
# @rbs (IR::grammar_warning warning, String input_path, ?promoted: bool) -> String
|
|
52
|
+
def format_grammar_warning(warning, input_path, promoted: false)
|
|
56
53
|
location = warning[:loc]
|
|
57
54
|
rendered = if location
|
|
58
55
|
"#{location[:file] || input_path}:#{location[:line] || 1}:#{location[:column] || 1}"
|
|
59
56
|
else
|
|
60
57
|
"#{input_path}:1:1"
|
|
61
58
|
end
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
label_id = promoted ? "label.warning_as_error" : "label.warning"
|
|
60
|
+
label = Messages.translate(label_id, language: @language)
|
|
61
|
+
"#{rendered}: #{label}: #{grammar_warning_message(warning)}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @rbs (IR::grammar_warning warning) -> String
|
|
65
|
+
def grammar_warning_message(warning)
|
|
66
|
+
id = WARNING_MESSAGE_IDS.fetch(warning[:type])
|
|
67
|
+
id = "warning.lexer_redos_symbol" if warning[:type] == :lexer_redos && warning[:symbol]
|
|
68
|
+
values = warning.to_h.transform_values(&:to_s) #: Hash[Symbol, String]
|
|
69
|
+
Messages.translate(id, language: @language, **values)
|
|
64
70
|
end
|
|
65
71
|
|
|
66
72
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
67
73
|
def report_conflicts(automaton, input_path)
|
|
68
74
|
messages = conflict_messages(automaton.conflict_summary, input_path)
|
|
69
75
|
if @options[:warnings]&.include?(:error) && messages.any?
|
|
70
|
-
|
|
76
|
+
promoted = messages.map do |message|
|
|
77
|
+
Messages.translate("conflict.treated_as_error", language: @language, detail: message)
|
|
78
|
+
end
|
|
79
|
+
raise Ibex::Error, promoted.join("\n")
|
|
71
80
|
end
|
|
72
81
|
|
|
73
82
|
messages.each { |message| @stderr.puts(message) }
|
|
@@ -77,29 +86,44 @@ module Ibex
|
|
|
77
86
|
def conflict_messages(summary, input_path)
|
|
78
87
|
messages = [] #: Array[String]
|
|
79
88
|
unless summary[:expectation_met]
|
|
80
|
-
|
|
89
|
+
detail = Messages.translate(
|
|
90
|
+
"conflict.shift_reduce_expected",
|
|
91
|
+
language: @language,
|
|
92
|
+
actual: summary[:sr],
|
|
93
|
+
expected: summary[:expected_sr]
|
|
94
|
+
)
|
|
95
|
+
messages << "#{input_path}:1:1: #{detail}"
|
|
81
96
|
end
|
|
82
97
|
if summary.key?(:rr_expectation_met)
|
|
83
98
|
unless summary[:rr_expectation_met]
|
|
84
|
-
|
|
99
|
+
detail = Messages.translate(
|
|
100
|
+
"conflict.reduce_reduce_expected",
|
|
101
|
+
language: @language,
|
|
102
|
+
actual: summary[:rr],
|
|
103
|
+
expected: summary[:expected_rr]
|
|
104
|
+
)
|
|
105
|
+
messages << "#{input_path}:1:1: #{detail}"
|
|
85
106
|
end
|
|
86
107
|
elsif summary[:rr].positive?
|
|
87
|
-
|
|
108
|
+
detail = Messages.translate("conflict.reduce_reduce", language: @language, actual: summary[:rr])
|
|
109
|
+
messages << "#{input_path}:1:1: #{detail}"
|
|
88
110
|
end
|
|
89
111
|
messages
|
|
90
112
|
end
|
|
91
113
|
|
|
92
114
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|
|
115
|
+
# rubocop:disable Metrics/AbcSize -- the bounded comparison reports each conflict class independently.
|
|
93
116
|
def suggest_ielr(automaton, input_path)
|
|
94
117
|
return unless @options[:suggest_ielr]
|
|
95
118
|
return unless automaton.algorithm == "lalr1"
|
|
96
|
-
return unless
|
|
119
|
+
return unless configuration_value("parser.algorithm") == :lalr
|
|
97
120
|
|
|
98
121
|
summary = automaton.conflict_summary
|
|
99
122
|
return if summary[:expectation_met] && summary.fetch(:rr_expectation_met, summary[:rr].zero?)
|
|
100
123
|
|
|
101
124
|
ielr = LALR::Builder.new(
|
|
102
|
-
automaton.grammar,
|
|
125
|
+
Configuration::AnalysisGrammar.for_algorithm(automaton.grammar, :ielr),
|
|
126
|
+
algorithm: :ielr, entry_isolation: configuration_value("parser.entries") == :isolated
|
|
103
127
|
).build
|
|
104
128
|
removed_sr = [summary[:sr] - ielr.conflict_summary[:sr], 0].max
|
|
105
129
|
removed_rr = [summary[:rr] - ielr.conflict_summary[:rr], 0].max
|
|
@@ -108,13 +132,16 @@ module Ibex
|
|
|
108
132
|
avoided << conflict_count(removed_rr, "reduce/reduce") if removed_rr.positive?
|
|
109
133
|
return if avoided.empty?
|
|
110
134
|
|
|
111
|
-
@
|
|
112
|
-
|
|
135
|
+
conjunction = @language == "ja" ? "、" : " and "
|
|
136
|
+
detail = Messages.translate("note.ielr", language: @language, avoided: avoided.join(conjunction))
|
|
137
|
+
@stderr.puts("#{input_path}:1:1: #{detail}")
|
|
113
138
|
end
|
|
139
|
+
# rubocop:enable Metrics/AbcSize
|
|
114
140
|
|
|
115
141
|
# @rbs (Integer count, String kind) -> String
|
|
116
142
|
def conflict_count(count, kind)
|
|
117
|
-
|
|
143
|
+
plural = @language == "en" && count != 1 ? "s" : ""
|
|
144
|
+
Messages.translate("conflict.count", language: @language, count: count, kind: kind, plural: plural)
|
|
118
145
|
end
|
|
119
146
|
|
|
120
147
|
# @rbs (IR::Automaton automaton, String input_path) -> void
|