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.
Files changed (437) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +95 -272
  3. data/docs/architecture.md +187 -35
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +3 -4
  12. data/docs/cst.md +24 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +179 -2
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +2 -1
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +54 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +184 -13
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +5 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +11 -0
  63. data/docs/release-readiness.md +126 -68
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +144 -34
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/lib/ibex/analysis/digraph.rb +128 -0
  75. data/lib/ibex/analysis.rb +1 -0
  76. data/lib/ibex/bison_import/importer.rb +516 -0
  77. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  78. data/lib/ibex/bison_import.rb +200 -0
  79. data/lib/ibex/bounded_subprocess.rb +171 -0
  80. data/lib/ibex/cli/ambiguity.rb +14 -5
  81. data/lib/ibex/cli/analysis.rb +134 -0
  82. data/lib/ibex/cli/bison_import.rb +121 -0
  83. data/lib/ibex/cli/config.rb +128 -0
  84. data/lib/ibex/cli/diagnostics.rb +31 -11
  85. data/lib/ibex/cli/documentation.rb +11 -8
  86. data/lib/ibex/cli/equiv.rb +169 -0
  87. data/lib/ibex/cli/error_messages.rb +11 -4
  88. data/lib/ibex/cli/explain.rb +14 -5
  89. data/lib/ibex/cli/fix.rb +203 -0
  90. data/lib/ibex/cli/formatting.rb +11 -10
  91. data/lib/ibex/cli/fuzz.rb +200 -0
  92. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  93. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  94. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  95. data/lib/ibex/cli/grammar_tests.rb +32 -14
  96. data/lib/ibex/cli/ir_tools.rb +5 -61
  97. data/lib/ibex/cli/outputs.rb +60 -33
  98. data/lib/ibex/cli/reduce.rb +202 -0
  99. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  100. data/lib/ibex/cli/samples.rb +49 -23
  101. data/lib/ibex/cli/verify.rb +116 -0
  102. data/lib/ibex/cli/watch.rb +6 -5
  103. data/lib/ibex/cli.rb +357 -56
  104. data/lib/ibex/codegen/action_locations.rb +1 -1
  105. data/lib/ibex/codegen/action_method_source.rb +5 -6
  106. data/lib/ibex/codegen/action_source.rb +3 -2
  107. data/lib/ibex/codegen/ambiguity.rb +5 -4
  108. data/lib/ibex/codegen/explain.rb +123 -55
  109. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  110. data/lib/ibex/codegen/rbs.rb +15 -7
  111. data/lib/ibex/codegen/report.rb +25 -13
  112. data/lib/ibex/codegen/ruby.rb +28 -64
  113. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  114. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  115. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  116. data/lib/ibex/configuration/explanation.rb +421 -0
  117. data/lib/ibex/configuration/inspector.rb +232 -0
  118. data/lib/ibex/configuration.rb +580 -0
  119. data/lib/ibex/coverage/collector.rb +21 -11
  120. data/lib/ibex/coverage/event_stream.rb +13 -7
  121. data/lib/ibex/coverage/report.rb +26 -18
  122. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  123. data/lib/ibex/delta_reducer.rb +99 -0
  124. data/lib/ibex/diff.rb +140 -0
  125. data/lib/ibex/equiv/machine.rb +135 -0
  126. data/lib/ibex/equiv.rb +373 -0
  127. data/lib/ibex/fix.rb +557 -0
  128. data/lib/ibex/frontend/ast.rb +22 -2
  129. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  130. data/lib/ibex/frontend/diagnostic.rb +1 -1
  131. data/lib/ibex/frontend/formatter.rb +72 -26
  132. data/lib/ibex/frontend/generated_parser.rb +147 -117
  133. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  134. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  135. data/lib/ibex/frontend/generation.rb +1 -1
  136. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  137. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  138. data/lib/ibex/frontend/regenerator.rb +1 -0
  139. data/lib/ibex/frontend/resolution.rb +2 -1
  140. data/lib/ibex/frontend/resolver.rb +1 -1
  141. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  142. data/lib/ibex/frontend/source_cursor.rb +2 -2
  143. data/lib/ibex/frontend/source_span.rb +9 -1
  144. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  145. data/lib/ibex/frontend/token_adapter.rb +10 -0
  146. data/lib/ibex/frontend.rb +1 -0
  147. data/lib/ibex/fuzz.rb +219 -0
  148. data/lib/ibex/generation_input.rb +1 -1
  149. data/lib/ibex/generation_manifest.rb +36 -25
  150. data/lib/ibex/generation_transaction.rb +1 -1
  151. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  152. data/lib/ibex/generation_transaction_validation.rb +2 -2
  153. data/lib/ibex/grammar_tests.rb +1 -1
  154. data/lib/ibex/ir/automaton_ir.rb +61 -19
  155. data/lib/ibex/ir/grammar_ir.rb +76 -45
  156. data/lib/ibex/ir/lexer_ir.rb +5 -5
  157. data/lib/ibex/ir/parser_contract.rb +135 -0
  158. data/lib/ibex/ir/serialize.rb +203 -76
  159. data/lib/ibex/ir/validator/automaton.rb +64 -34
  160. data/lib/ibex/ir/validator/base.rb +51 -33
  161. data/lib/ibex/ir/validator/grammar.rb +113 -83
  162. data/lib/ibex/ir/validator/lexer.rb +2 -2
  163. data/lib/ibex/ir/validator.rb +2 -1
  164. data/lib/ibex/ir.rb +16 -3
  165. data/lib/ibex/lalr/build_metrics.rb +50 -2
  166. data/lib/ibex/lalr/builder.rb +191 -56
  167. data/lib/ibex/lalr/conflict_search.rb +15 -4
  168. data/lib/ibex/lalr/counterexample.rb +19 -14
  169. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  170. data/lib/ibex/lalr/goto_follows.rb +229 -0
  171. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  172. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  173. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  174. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  175. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  176. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  177. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  178. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  179. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  180. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  181. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  182. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  183. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  184. data/lib/ibex/lalr.rb +52 -1
  185. data/lib/ibex/location.rb +3 -3
  186. data/lib/ibex/lsp/document_handlers.rb +9 -7
  187. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  188. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  189. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  190. data/lib/ibex/lsp/position_codec.rb +6 -2
  191. data/lib/ibex/lsp/request_handlers.rb +3 -2
  192. data/lib/ibex/lsp/request_support.rb +14 -7
  193. data/lib/ibex/lsp/server.rb +13 -11
  194. data/lib/ibex/lsp/symbol_index.rb +13 -13
  195. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  196. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  197. data/lib/ibex/lsp/transport.rb +13 -3
  198. data/lib/ibex/lsp/workspace.rb +5 -4
  199. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  200. data/lib/ibex/lsp.rb +1 -0
  201. data/lib/ibex/messages/en.yml +28 -0
  202. data/lib/ibex/messages/ja.yml +28 -0
  203. data/lib/ibex/messages.rb +68 -0
  204. data/lib/ibex/metrics.rb +175 -0
  205. data/lib/ibex/normalize/declarations.rb +2 -0
  206. data/lib/ibex/normalize/diagnostics.rb +63 -38
  207. data/lib/ibex/normalize/expander.rb +5 -4
  208. data/lib/ibex/normalize/expression.rb +8 -6
  209. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  210. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  211. data/lib/ibex/normalize/lexer.rb +3 -3
  212. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  213. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  214. data/lib/ibex/normalize/parameters.rb +31 -4
  215. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  216. data/lib/ibex/normalize.rb +10 -20
  217. data/lib/ibex/racc_migration/report.rb +6 -3
  218. data/lib/ibex/rake_task.rb +1 -1
  219. data/lib/ibex/samples.rb +43 -13
  220. data/lib/ibex/table_artifact/builder.rb +266 -0
  221. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  222. data/lib/ibex/table_artifact/document.rb +39 -0
  223. data/lib/ibex/table_artifact/executor.rb +187 -0
  224. data/lib/ibex/table_artifact/serializer.rb +57 -0
  225. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  226. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  227. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  228. data/lib/ibex/table_artifact/validator.rb +253 -0
  229. data/lib/ibex/table_artifact.rb +88 -0
  230. data/lib/ibex/table_simulation/result.rb +6 -2
  231. data/lib/ibex/table_simulation/step.rb +3 -1
  232. data/lib/ibex/tables.rb +14 -0
  233. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  234. data/lib/ibex/verification_report/builder.rb +159 -0
  235. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  236. data/lib/ibex/verification_report/logical_path.rb +68 -0
  237. data/lib/ibex/verification_report/validator.rb +329 -0
  238. data/lib/ibex/verification_report.rb +69 -0
  239. data/lib/ibex/verify/action_correspondence.rb +138 -0
  240. data/lib/ibex/verify/language_witness.rb +295 -0
  241. data/lib/ibex/verify/reference_collection.rb +190 -0
  242. data/lib/ibex/verify/result.rb +73 -0
  243. data/lib/ibex/verify/verifier.rb +598 -0
  244. data/lib/ibex/verify.rb +15 -0
  245. data/lib/ibex/version.rb +1 -1
  246. data/lib/ibex/watch/runner.rb +10 -1
  247. data/lib/ibex/watch/source_snapshot.rb +13 -8
  248. data/lib/ibex.rb +10 -0
  249. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  250. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  251. data/schema/bison-import-v1.schema.json +106 -0
  252. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  253. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  254. data/schema/construction-profile-v1.schema.json +779 -0
  255. data/schema/diff-v1.schema.json +41 -0
  256. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  257. data/schema/equiv-v1.schema.json +64 -0
  258. data/schema/error-ux-review-v1.schema.json +609 -0
  259. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  260. data/schema/error-ux-round2-v1.schema.json +519 -0
  261. data/schema/explain-v1.schema.json +63 -1
  262. data/schema/fix-v1.schema.json +64 -0
  263. data/schema/fix-v2.schema.json +66 -0
  264. data/schema/fix-v3.schema.json +101 -0
  265. data/schema/fuzz-regression-v1.schema.json +79 -0
  266. data/schema/fuzz-v1.schema.json +114 -0
  267. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  268. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  269. data/schema/grammar-ir.schema.json +271 -0
  270. data/schema/ielr-benchmark-v1.schema.json +85 -0
  271. data/schema/lexer-profile-v1.schema.json +491 -0
  272. data/schema/metrics-v1.schema.json +60 -0
  273. data/schema/reduce-v1.schema.json +22 -0
  274. data/schema/reduce-v2.schema.json +63 -0
  275. data/schema/table-artifact-v1.schema.json +377 -0
  276. data/schema/verification-report-v1.schema.json +222 -0
  277. data/schema/verify-v1.schema.json +42 -0
  278. data/sig/ibex/analysis/digraph.rbs +19 -0
  279. data/sig/ibex/bison_import/importer.rbs +140 -0
  280. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  281. data/sig/ibex/bison_import.rbs +95 -0
  282. data/sig/ibex/bounded_subprocess.rbs +76 -0
  283. data/sig/ibex/cli/ambiguity.rbs +6 -0
  284. data/sig/ibex/cli/analysis.rbs +43 -0
  285. data/sig/ibex/cli/bison_import.rbs +32 -0
  286. data/sig/ibex/cli/config.rbs +35 -0
  287. data/sig/ibex/cli/diagnostics.rbs +16 -3
  288. data/sig/ibex/cli/documentation.rbs +5 -1
  289. data/sig/ibex/cli/equiv.rbs +49 -0
  290. data/sig/ibex/cli/error_messages.rbs +6 -0
  291. data/sig/ibex/cli/explain.rbs +6 -0
  292. data/sig/ibex/cli/fix.rbs +53 -0
  293. data/sig/ibex/cli/formatting.rbs +5 -1
  294. data/sig/ibex/cli/fuzz.rbs +47 -0
  295. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  296. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  297. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  298. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  299. data/sig/ibex/cli/ir_tools.rbs +7 -19
  300. data/sig/ibex/cli/outputs.rbs +9 -3
  301. data/sig/ibex/cli/reduce.rbs +60 -0
  302. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  303. data/sig/ibex/cli/samples.rbs +8 -0
  304. data/sig/ibex/cli/verify.rbs +28 -0
  305. data/sig/ibex/cli/watch.rbs +2 -2
  306. data/sig/ibex/cli.rbs +79 -5
  307. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  308. data/sig/ibex/codegen/explain.rbs +45 -25
  309. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  310. data/sig/ibex/codegen/rbs.rbs +12 -10
  311. data/sig/ibex/codegen/report.rbs +12 -12
  312. data/sig/ibex/codegen/ruby.rbs +18 -15
  313. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  314. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  315. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  316. data/sig/ibex/configuration/explanation.rbs +195 -0
  317. data/sig/ibex/configuration/inspector.rbs +53 -0
  318. data/sig/ibex/configuration.rbs +242 -0
  319. data/sig/ibex/coverage/collector.rbs +16 -14
  320. data/sig/ibex/coverage/event_stream.rbs +12 -10
  321. data/sig/ibex/coverage/report.rbs +20 -18
  322. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  323. data/sig/ibex/delta_reducer.rbs +38 -0
  324. data/sig/ibex/diff.rbs +47 -0
  325. data/sig/ibex/equiv/machine.rbs +54 -0
  326. data/sig/ibex/equiv.rbs +129 -0
  327. data/sig/ibex/fix.rbs +138 -0
  328. data/sig/ibex/frontend/ast.rbs +33 -7
  329. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  330. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  331. data/sig/ibex/frontend/formatter.rbs +38 -30
  332. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  333. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  334. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  335. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  336. data/sig/ibex/frontend/resolution.rbs +3 -2
  337. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  338. data/sig/ibex/frontend/source_span.rbs +5 -2
  339. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  340. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  341. data/sig/ibex/fuzz.rbs +71 -0
  342. data/sig/ibex/generation_input.rbs +2 -2
  343. data/sig/ibex/generation_manifest.rbs +35 -32
  344. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  345. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  346. data/sig/ibex/grammar_tests.rbs +2 -2
  347. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  348. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  349. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  350. data/sig/ibex/ir/parser_contract.rbs +69 -0
  351. data/sig/ibex/ir/serialize.rbs +63 -28
  352. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  353. data/sig/ibex/ir/validator/base.rbs +35 -31
  354. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  355. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  356. data/sig/ibex/ir.rbs +9 -3
  357. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  358. data/sig/ibex/lalr/builder.rbs +75 -24
  359. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  360. data/sig/ibex/lalr/counterexample.rbs +17 -14
  361. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  362. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  363. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  364. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  365. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  366. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  367. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  368. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  369. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  370. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  371. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  372. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  373. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  374. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  375. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  376. data/sig/ibex/lalr.rbs +11 -1
  377. data/sig/ibex/location.rbs +6 -6
  378. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  379. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  380. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  381. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  382. data/sig/ibex/lsp/position_codec.rbs +8 -4
  383. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  384. data/sig/ibex/lsp/request_support.rbs +8 -8
  385. data/sig/ibex/lsp/server.rbs +16 -16
  386. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  387. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  388. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  389. data/sig/ibex/lsp/transport.rbs +18 -6
  390. data/sig/ibex/lsp/workspace.rbs +3 -2
  391. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  392. data/sig/ibex/messages.rbs +26 -0
  393. data/sig/ibex/metrics.rbs +50 -0
  394. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  395. data/sig/ibex/normalize/expander.rbs +2 -2
  396. data/sig/ibex/normalize/expression.rbs +10 -8
  397. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  398. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  399. data/sig/ibex/normalize/lexer.rbs +4 -4
  400. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  401. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  402. data/sig/ibex/normalize/parameters.rbs +13 -8
  403. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  404. data/sig/ibex/normalize.rbs +11 -5
  405. data/sig/ibex/racc_migration/report.rbs +8 -6
  406. data/sig/ibex/rake_task.rbs +1 -1
  407. data/sig/ibex/samples.rbs +13 -4
  408. data/sig/ibex/table_artifact/builder.rbs +93 -0
  409. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  410. data/sig/ibex/table_artifact/document.rbs +27 -0
  411. data/sig/ibex/table_artifact/executor.rbs +66 -0
  412. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  413. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  414. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  415. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  416. data/sig/ibex/table_artifact/validator.rbs +66 -0
  417. data/sig/ibex/table_artifact.rbs +32 -0
  418. data/sig/ibex/table_simulation/result.rbs +8 -4
  419. data/sig/ibex/table_simulation/step.rbs +4 -2
  420. data/sig/ibex/tables.rbs +10 -9
  421. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  422. data/sig/ibex/verification_report/builder.rbs +53 -0
  423. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  424. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  425. data/sig/ibex/verification_report/validator.rbs +114 -0
  426. data/sig/ibex/verification_report.rbs +44 -0
  427. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  428. data/sig/ibex/verify/language_witness.rbs +112 -0
  429. data/sig/ibex/verify/reference_collection.rbs +63 -0
  430. data/sig/ibex/verify/result.rbs +52 -0
  431. data/sig/ibex/verify/verifier.rbs +170 -0
  432. data/sig/ibex/verify.rbs +6 -0
  433. data/sig/ibex/watch/runner.rbs +12 -2
  434. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  435. metadata +232 -12
  436. data/lib/ibex/ir/migration.rb +0 -120
  437. data/sig/ibex/ir/migration.rbs +0 -34
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ class Equiv
6
+ # Immutable, semantic-action-free LR state-stack machine.
7
+ class Machine
8
+ class BudgetExceeded < Ibex::Error; end
9
+
10
+ # One product-search component.
11
+ class Configuration
12
+ attr_reader :stack #: Array[Integer]
13
+ attr_reader :status #: Symbol?
14
+ attr_reader :actions #: Integer
15
+ attr_reader :reductions #: Array[Integer]
16
+
17
+ # @rbs (stack: Array[Integer], status: Symbol?, actions: Integer, ?reductions: Array[Integer]) -> void
18
+ def initialize(stack:, status:, actions:, reductions: [])
19
+ @stack = stack.freeze
20
+ @status = status
21
+ @actions = actions
22
+ @reductions = reductions.freeze
23
+ freeze
24
+ end
25
+ end
26
+
27
+ # @rbs (IR::Automaton automaton, max_actions: Integer, max_stack: Integer) -> void
28
+ def initialize(automaton, max_actions:, max_stack:)
29
+ @automaton = automaton
30
+ @max_actions = max_actions
31
+ @max_stack = max_stack
32
+ @terminals = automaton.grammar.terminals.to_h { |symbol| [symbol.name, symbol.id] }
33
+ @eof_id = @terminals.fetch("$eof")
34
+ end
35
+
36
+ # @rbs () -> Configuration
37
+ def start
38
+ entry = @automaton.entry_states.fetch(@automaton.grammar.start)
39
+ Configuration.new(stack: [entry], status: nil, actions: 0, reductions: [])
40
+ end
41
+
42
+ # @rbs (Array[String] tokens) -> Configuration
43
+ def run(tokens)
44
+ tokens.reduce(start) { |configuration, token| push(configuration, token) }
45
+ end
46
+
47
+ # @rbs (Configuration configuration, String token) -> Configuration
48
+ def push(configuration, token)
49
+ return configuration if configuration.status
50
+
51
+ token_id = @terminals[token]
52
+ unless token_id
53
+ return Configuration.new(
54
+ stack: configuration.stack, status: :error, actions: configuration.actions,
55
+ reductions: configuration.reductions
56
+ )
57
+ end
58
+
59
+ consume(configuration, token_id, eof: false)
60
+ end
61
+
62
+ # @rbs (Configuration configuration) -> Configuration
63
+ def finish(configuration)
64
+ return configuration if configuration.status
65
+
66
+ consume(configuration, @eof_id, eof: true)
67
+ end
68
+
69
+ private
70
+
71
+ # @rbs (Configuration configuration, Integer token_id, eof: bool) -> Configuration
72
+ def consume(configuration, token_id, eof:)
73
+ stack = configuration.stack.dup
74
+ actions = configuration.actions
75
+ reductions = configuration.reductions.dup
76
+ loop do
77
+ actions += 1
78
+ enforce_action_budget!(actions)
79
+ state = @automaton.states.fetch(stack.fetch(-1))
80
+ action = state.actions.fetch(token_id, state.default_action || { type: :error })
81
+ case action.fetch(:type)
82
+ when :shift
83
+ shift = action #: IR::shift_action
84
+ stack << shift.fetch(:state)
85
+ enforce_stack_budget!(stack)
86
+ return Configuration.new(stack: stack, status: nil, actions: actions, reductions: reductions) unless eof
87
+ when :reduce
88
+ reduce = action #: IR::reduce_action
89
+ production_id = reduce.fetch(:production)
90
+ apply_reduction!(stack, production_id)
91
+ reductions << production_id
92
+ enforce_stack_budget!(stack)
93
+ when :accept
94
+ return Configuration.new(
95
+ stack: stack, status: :accepted, actions: actions, reductions: reductions
96
+ )
97
+ when :error
98
+ return Configuration.new(stack: stack, status: :error, actions: actions, reductions: reductions)
99
+ else
100
+ raise Ibex::Error, "(equiv):1:1: unknown parser action #{action.inspect}"
101
+ end
102
+ end
103
+ end
104
+
105
+ # @rbs (Array[Integer] stack, Integer production_id) -> void
106
+ def apply_reduction!(stack, production_id)
107
+ production = @automaton.grammar.productions.fetch(production_id)
108
+ if production.rhs.length >= stack.length
109
+ raise Ibex::Error, "(equiv):1:1: production #{production_id} underflows the state stack"
110
+ end
111
+
112
+ stack.pop(production.rhs.length)
113
+ state = @automaton.states.fetch(stack.fetch(-1))
114
+ target = state.gotos[production.lhs]
115
+ raise Ibex::Error, "(equiv):1:1: missing goto after production #{production_id}" unless target
116
+
117
+ stack << target
118
+ end
119
+
120
+ # @rbs (Integer actions) -> void
121
+ def enforce_action_budget!(actions)
122
+ return if actions <= @max_actions
123
+
124
+ raise BudgetExceeded, "(equiv):1:1: simulation exceeded #{@max_actions} actions"
125
+ end
126
+
127
+ # @rbs (Array[Integer] stack) -> void
128
+ def enforce_stack_budget!(stack)
129
+ return if stack.length <= @max_stack
130
+
131
+ raise BudgetExceeded, "(equiv):1:1: simulation exceeded stack depth #{@max_stack}"
132
+ end
133
+ end
134
+ end
135
+ end
data/lib/ibex/equiv.rb ADDED
@@ -0,0 +1,373 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "samples"
5
+ require_relative "verify"
6
+ require_relative "equiv/machine"
7
+
8
+ module Ibex
9
+ # Bounded language comparison over two immutable parser automata.
10
+ # rubocop:disable Metrics/ClassLength -- the three comparison strategies share one report and budget contract.
11
+ class Equiv
12
+ # @rbs!
13
+ # type tree_node_signature = [String, Array[String]]
14
+ # type tree_trace_entry = [String, Array[String], tree_node_signature?]
15
+ # type terminal_signature = [String, [Symbol, Integer]?]
16
+ # type production_signature = [String, Array[String], String?]
17
+
18
+ CAVEAT = "Bounded search is not a proof of equivalence." #: String
19
+ DEFAULT_MAX_ACTIONS = 100_000 #: Integer
20
+ DEFAULT_MAX_STACK = 10_000 #: Integer
21
+
22
+ class Difference < Ibex::Error
23
+ attr_reader :details #: Hash[Symbol, Object?]
24
+
25
+ # @rbs (Hash[Symbol, Object?] details) -> void
26
+ def initialize(details)
27
+ @details = IR.deep_freeze(details)
28
+ label = details[:witness] ? " for #{details.fetch(:witness).inspect}" : ""
29
+ super("(equiv):1:1: comparison found a difference#{label}")
30
+ end
31
+ end
32
+
33
+ class BudgetExceeded < Ibex::Error
34
+ attr_reader :details #: Hash[Symbol, Object?]
35
+
36
+ # @rbs (Hash[Symbol, Object?] details) -> void
37
+ def initialize(details)
38
+ @details = IR.deep_freeze(details)
39
+ super("(equiv):1:1: configured budget was exhausted")
40
+ end
41
+ end
42
+
43
+ # @rbs (IR::Automaton left, IR::Automaton right, ?sample_count: Integer, ?seed: Integer,
44
+ # ?max_tokens: Integer, ?max_configurations: Integer, ?max_actions: Integer, ?max_stack: Integer,
45
+ # ?rule_map: Hash[String, String]) -> void
46
+ def initialize(left, right, sample_count: 100, seed: 0, max_tokens: 8, max_configurations: 50_000,
47
+ max_actions: DEFAULT_MAX_ACTIONS, max_stack: DEFAULT_MAX_STACK, rule_map: {})
48
+ budgets = {
49
+ sample_count: sample_count, max_tokens: max_tokens, max_configurations: max_configurations,
50
+ max_actions: max_actions, max_stack: max_stack
51
+ }
52
+ invalid = budgets.find { |_name, value| !value.positive? }
53
+ raise ArgumentError, "#{invalid.fetch(0)} must be positive" if invalid
54
+
55
+ @left = left
56
+ @right = right
57
+ @sample_count = sample_count
58
+ @seed = seed
59
+ @max_tokens = max_tokens
60
+ @max_configurations = max_configurations
61
+ @max_actions = max_actions
62
+ @max_stack = max_stack
63
+ @rule_map = rule_map.dup.freeze
64
+ validate_rule_map!
65
+ @left_machine = Machine.new(left, max_actions: max_actions, max_stack: max_stack)
66
+ @right_machine = Machine.new(right, max_actions: max_actions, max_stack: max_stack)
67
+ end
68
+
69
+ # @rbs () -> Hash[Symbol, Object?]
70
+ def run
71
+ verify_inputs!
72
+ structural = structural_identity?
73
+ tree_structural = tree_structural_identity?
74
+ return successful_report(structural: true, samples: 0, configurations: 0) if structural && tree_structural
75
+
76
+ samples = compare_samples
77
+ configurations = search_product
78
+ successful_report(structural: false, samples: samples, configurations: configurations)
79
+ rescue Machine::BudgetExceeded => e
80
+ raise BudgetExceeded.new(
81
+ result: "budget_exhausted", phase: "simulation", message: e.message,
82
+ bounds: bounds, statement: CAVEAT
83
+ )
84
+ rescue Ibex::Error => e
85
+ raise if e.is_a?(Difference) || e.is_a?(BudgetExceeded)
86
+ raise unless sample_budget_error?(e)
87
+
88
+ raise BudgetExceeded.new(
89
+ result: "budget_exhausted", phase: "sampling", message: e.message,
90
+ bounds: bounds, statement: CAVEAT
91
+ )
92
+ end
93
+
94
+ private
95
+
96
+ # @rbs () -> Integer
97
+ def compare_samples
98
+ left_samples = generated_samples(@left.grammar, @seed)
99
+ right_samples = generated_samples(@right.grammar, @seed ^ 0x1BE)
100
+ left_samples.each { |tokens| compare_tokens!(tokens, method: "left_to_right_sampling") }
101
+ right_samples.each { |tokens| compare_tokens!(tokens, method: "right_to_left_sampling") }
102
+ left_samples.length + right_samples.length
103
+ end
104
+
105
+ # @rbs (IR::Grammar grammar, Integer seed) -> Array[Array[String]]
106
+ def generated_samples(grammar, seed)
107
+ Samples.new(
108
+ grammar, seed: seed, max_tokens: @max_tokens, max_depth: [@max_tokens * 2, 16].max,
109
+ max_expansions: [Samples::DEFAULT_MAX_EXPANSIONS, @sample_count * @max_tokens * 8].max,
110
+ strategy: :coverage, path_length: 2
111
+ ).generate(count: @sample_count)
112
+ end
113
+
114
+ # @rbs () -> Integer
115
+ def search_product # rubocop:disable Metrics/MethodLength -- BFS lifecycle remains visible as one bounded worklist.
116
+ left_start = @left_machine.start
117
+ right_start = @right_machine.start
118
+ queue = [[left_start, right_start, []]] #: Array[[Machine::Configuration, Machine::Configuration, Array[String]]]
119
+ seen = { product_key(left_start, right_start) => true }
120
+ cursor = 0
121
+ truncated = false
122
+ alphabet = terminal_alphabet
123
+
124
+ while cursor < queue.length
125
+ left, right, tokens = queue.fetch(cursor)
126
+ cursor += 1
127
+ compare_configurations!(left, right, tokens)
128
+ next if tokens.length >= @max_tokens || both_rejected?(left, right)
129
+
130
+ alphabet.each do |token|
131
+ next_left = @left_machine.push(left, token)
132
+ next_right = @right_machine.push(right, token)
133
+ key = product_key(next_left, next_right)
134
+ next if seen[key]
135
+
136
+ if queue.length >= @max_configurations
137
+ truncated = true
138
+ next
139
+ end
140
+ seen[key] = true
141
+ queue << [next_left, next_right, tokens + [token]]
142
+ end
143
+ end
144
+ if truncated
145
+ raise BudgetExceeded.new(
146
+ result: "budget_exhausted", phase: "product_bfs", checked_configurations: cursor,
147
+ bounds: bounds, statement: CAVEAT
148
+ )
149
+ end
150
+
151
+ cursor
152
+ end
153
+
154
+ # @rbs (Array[String] tokens, method: String) -> void
155
+ def compare_tokens!(tokens, method:)
156
+ left = @left_machine.finish(@left_machine.run(tokens))
157
+ right = @right_machine.finish(@right_machine.run(tokens))
158
+ compare_results!(left, right, tokens, method)
159
+ end
160
+
161
+ # @rbs (Machine::Configuration left, Machine::Configuration right, Array[String] tokens) -> void
162
+ def compare_configurations!(left, right, tokens)
163
+ left_result = @left_machine.finish(left)
164
+ right_result = @right_machine.finish(right)
165
+ compare_results!(left_result, right_result, tokens, "bounded_product_bfs")
166
+ end
167
+
168
+ # @rbs (Machine::Configuration left, Machine::Configuration right, Array[String] tokens, String method) -> void
169
+ def compare_results!(left, right, tokens, method)
170
+ raise_language_difference(tokens, left.status, right.status, method) unless left.status == right.status
171
+ return unless left.status == :accepted && tree_requested?
172
+ return if tree_trace(@left, left, map_left: true) == tree_trace(@right, right, map_left: false)
173
+
174
+ raise Difference.new(
175
+ ibex_report: "equiv", schema_version: 1, result: "difference",
176
+ difference_kind: "tree", method: "#{method}_tree_trace", witness: tokens,
177
+ outcomes: { left: left.status, right: right.status },
178
+ bounds: bounds, statement: CAVEAT
179
+ )
180
+ end
181
+
182
+ # @rbs (Array[String] tokens, Symbol? left, Symbol? right, String method) -> bot
183
+ def raise_language_difference(tokens, left, right, method)
184
+ raise Difference.new(
185
+ ibex_report: "equiv", schema_version: 1, result: "difference",
186
+ difference_kind: "language", method: method, witness: tokens, outcomes: { left: left, right: right },
187
+ direction: left == :accepted ? "left_only" : "right_only",
188
+ bounds: bounds, statement: CAVEAT
189
+ )
190
+ end
191
+
192
+ # @rbs () -> bool
193
+ def structural_identity?
194
+ @left.algorithm == @right.algorithm &&
195
+ grammar_signature(@left.grammar, map_left: true) == grammar_signature(@right.grammar, map_left: false)
196
+ end
197
+
198
+ # @rbs () -> bool
199
+ def tree_structural_identity?
200
+ return true unless tree_requested?
201
+
202
+ tree_grammar_signature(@left.grammar) == tree_grammar_signature(@right.grammar)
203
+ end
204
+
205
+ # @rbs (IR::Grammar grammar) -> Array[tree_node_signature]
206
+ def tree_grammar_signature(grammar)
207
+ grammar.productions.filter_map { |production| node_signature(production.node) }
208
+ end
209
+
210
+ # @rbs (IR::node_annotation? node) -> tree_node_signature?
211
+ def node_signature(node)
212
+ node && [node.fetch(:name), node.fetch(:fields)]
213
+ end
214
+
215
+ # @rbs (IR::Automaton automaton, Machine::Configuration configuration, map_left: bool) -> Array[tree_trace_entry]
216
+ def tree_trace(automaton, configuration, map_left:)
217
+ configuration.reductions.map do |production_id|
218
+ production = automaton.grammar.productions.fetch(production_id)
219
+ lhs = symbol_name(automaton.grammar, production.lhs)
220
+ [
221
+ mapped_name(automaton.grammar, lhs, map_left: map_left),
222
+ production.rhs.map do |id|
223
+ mapped_name(automaton.grammar, symbol_name(automaton.grammar, id),
224
+ map_left: map_left)
225
+ end,
226
+ node_signature(production.node)
227
+ ]
228
+ end
229
+ end
230
+
231
+ # @rbs (IR::Grammar grammar, map_left: bool) -> Hash[Symbol, Object?]
232
+ def grammar_signature(grammar, map_left:)
233
+ {
234
+ starts: grammar.starts.map { |name| mapped_name(grammar, name, map_left: map_left) },
235
+ recovery: recovery_signature(grammar, map_left: map_left),
236
+ terminals: terminal_signatures(grammar),
237
+ productions: production_signatures(grammar, map_left: map_left)
238
+ }
239
+ end
240
+
241
+ # @rbs (IR::Grammar grammar, map_left: bool) -> Hash[Symbol, Object?]
242
+ def recovery_signature(grammar, map_left:)
243
+ {
244
+ sync_tokens: grammar.recovery.fetch(:sync_tokens),
245
+ on_error_reduce: grammar.recovery.fetch(:on_error_reduce).map do |group|
246
+ group.map { |name| mapped_name(grammar, name, map_left: map_left) }
247
+ end
248
+ }
249
+ end
250
+
251
+ # @rbs (IR::Grammar grammar) -> Array[terminal_signature]
252
+ def terminal_signatures(grammar)
253
+ grammar.terminals.map do |symbol|
254
+ precedence = symbol.precedence
255
+ [symbol.name, precedence && [precedence.fetch(:associativity), precedence.fetch(:level)]]
256
+ end
257
+ end
258
+
259
+ # @rbs (IR::Grammar grammar, map_left: bool) -> Array[production_signature]
260
+ def production_signatures(grammar, map_left:)
261
+ grammar.productions.map { |production| production_signature(grammar, production, map_left: map_left) }
262
+ end
263
+
264
+ # @rbs (IR::Grammar grammar, IR::Production production, map_left: bool) -> production_signature
265
+ def production_signature(grammar, production, map_left:)
266
+ lhs = symbol_name(grammar, production.lhs)
267
+ rhs = production.rhs.map { |id| symbol_name(grammar, id) }
268
+ precedence = production.precedence_override
269
+ precedence_name = symbol_name(grammar, precedence) if precedence
270
+ [
271
+ mapped_name(grammar, lhs, map_left: map_left),
272
+ rhs.map { |name| mapped_name(grammar, name, map_left: map_left) },
273
+ precedence_name && mapped_name(grammar, precedence_name, map_left: map_left)
274
+ ]
275
+ end
276
+
277
+ # @rbs (IR::Grammar grammar, Integer id) -> String
278
+ def symbol_name(grammar, id)
279
+ symbol = grammar.symbol_by_id(id)
280
+ symbol ? symbol.name : id.to_s
281
+ end
282
+
283
+ # @rbs (IR::Grammar grammar, String name, map_left: bool) -> String
284
+ def mapped_name(grammar, name, map_left:)
285
+ symbol = grammar.symbol(name)
286
+ return name unless map_left && symbol&.nonterminal?
287
+
288
+ @rule_map.fetch(name, name)
289
+ end
290
+
291
+ # @rbs () -> void
292
+ def validate_rule_map!
293
+ left_names = @left.grammar.nonterminals.map(&:name)
294
+ right_names = @right.grammar.nonterminals.map(&:name)
295
+ unknown_left = @rule_map.keys - left_names
296
+ unknown_right = @rule_map.values - right_names
297
+ raise ArgumentError, "unknown left rules: #{unknown_left.join(', ')}" unless unknown_left.empty?
298
+ raise ArgumentError, "unknown right rules: #{unknown_right.join(', ')}" unless unknown_right.empty?
299
+ return if @rule_map.values.uniq.length == @rule_map.length
300
+
301
+ raise ArgumentError, "rule map must be one-to-one"
302
+ end
303
+
304
+ # @rbs () -> void
305
+ def verify_inputs!
306
+ { left: @left, right: @right }.each do |side, automaton|
307
+ result = Verify::Verifier.new(automaton).verify
308
+ next if result.valid?
309
+
310
+ raise Ibex::Error,
311
+ "(equiv):1:1: #{side} automaton failed independent verification: " \
312
+ "#{result.violations.first&.message}"
313
+ end
314
+ end
315
+
316
+ # @rbs () -> Array[String]
317
+ def terminal_alphabet
318
+ [@left, @right].flat_map do |automaton|
319
+ automaton.grammar.terminals.reject(&:reserved).map(&:name)
320
+ end.uniq.sort
321
+ end
322
+
323
+ # @rbs (Machine::Configuration left, Machine::Configuration right) -> Array[Object?]
324
+ def product_key(left, right)
325
+ key = [left.stack, left.status, left.actions, right.stack, right.status, right.actions]
326
+ key.push(left.reductions, right.reductions) if tree_requested?
327
+ key
328
+ end
329
+
330
+ # @rbs (Machine::Configuration left, Machine::Configuration right) -> bool
331
+ def both_rejected?(left, right)
332
+ left.status == :error && right.status == :error
333
+ end
334
+
335
+ # @rbs (structural: bool, samples: Integer, configurations: Integer) -> untyped
336
+ def successful_report(structural:, samples:, configurations:)
337
+ methods = if structural
338
+ ["structural_comparison"]
339
+ else
340
+ %w[structural_comparison bidirectional_sampling bounded_product_bfs]
341
+ end
342
+ IR.deep_freeze(
343
+ ibex_report: "equiv", schema_version: 1, result: "no_difference_within_bounds",
344
+ structural_identity: structural,
345
+ methods: methods,
346
+ rule_map: @rule_map,
347
+ tree: {
348
+ requested: tree_requested?,
349
+ result: tree_requested? ? "no_difference_within_bounds" : "not_checked"
350
+ },
351
+ checked: { samples: samples, product_configurations: configurations },
352
+ bounds: bounds, statement: CAVEAT
353
+ )
354
+ end
355
+
356
+ # @rbs () -> bool
357
+ def tree_requested? = !@rule_map.empty?
358
+
359
+ # @rbs () -> Hash[Symbol, Integer]
360
+ def bounds
361
+ {
362
+ sample_count: @sample_count, seed: @seed, max_tokens: @max_tokens,
363
+ max_configurations: @max_configurations, max_actions: @max_actions, max_stack: @max_stack
364
+ }
365
+ end
366
+
367
+ # @rbs (Ibex::Error error) -> bool
368
+ def sample_budget_error?(error)
369
+ error.message.match?(/(?:limit|maximum|budget).*(?:exceed|exhaust)|needs \d+ tokens/i)
370
+ end
371
+ end
372
+ # rubocop:enable Metrics/ClassLength
373
+ end