ibex 0.1.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 (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -123
  3. data/docs/architecture.md +477 -31
  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 +106 -0
  12. data/docs/cst.md +237 -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 +306 -0
  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 +26 -0
  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 +114 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +629 -18
  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 +56 -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 +45 -6
  63. data/docs/release-readiness.md +221 -0
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +234 -0
  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/examples/README.md +60 -0
  75. data/examples/calculator.y +44 -0
  76. data/examples/csv.y +32 -0
  77. data/examples/ini.y +70 -0
  78. data/examples/json.y +48 -0
  79. data/examples/tiny_language.y +75 -0
  80. data/lib/ibex/analysis/digraph.rb +128 -0
  81. data/lib/ibex/analysis/sets.rb +5 -4
  82. data/lib/ibex/analysis.rb +1 -0
  83. data/lib/ibex/artifact_set.rb +63 -0
  84. data/lib/ibex/bison_import/importer.rb +516 -0
  85. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  86. data/lib/ibex/bison_import.rb +200 -0
  87. data/lib/ibex/bounded_subprocess.rb +171 -0
  88. data/lib/ibex/cli/ambiguity.rb +75 -0
  89. data/lib/ibex/cli/analysis.rb +134 -0
  90. data/lib/ibex/cli/bison_import.rb +121 -0
  91. data/lib/ibex/cli/config.rb +128 -0
  92. data/lib/ibex/cli/counterexample_options.rb +6 -2
  93. data/lib/ibex/cli/coverage.rb +173 -0
  94. data/lib/ibex/cli/debug.rb +106 -0
  95. data/lib/ibex/cli/diagnostics.rb +136 -0
  96. data/lib/ibex/cli/documentation.rb +70 -0
  97. data/lib/ibex/cli/equiv.rb +169 -0
  98. data/lib/ibex/cli/error_messages.rb +184 -0
  99. data/lib/ibex/cli/explain.rb +85 -0
  100. data/lib/ibex/cli/fix.rb +203 -0
  101. data/lib/ibex/cli/formatting.rb +387 -0
  102. data/lib/ibex/cli/fuzz.rb +200 -0
  103. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  104. data/lib/ibex/cli/generation_artifacts.rb +189 -0
  105. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  106. data/lib/ibex/cli/grammar_tests.rb +140 -0
  107. data/lib/ibex/cli/ir_tools.rb +128 -0
  108. data/lib/ibex/cli/lsp.rb +33 -0
  109. data/lib/ibex/cli/outputs.rb +133 -23
  110. data/lib/ibex/cli/racc_migration.rb +87 -0
  111. data/lib/ibex/cli/reduce.rb +202 -0
  112. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  113. data/lib/ibex/cli/samples.rb +118 -0
  114. data/lib/ibex/cli/verify.rb +116 -0
  115. data/lib/ibex/cli/watch.rb +104 -0
  116. data/lib/ibex/cli.rb +825 -55
  117. data/lib/ibex/codegen/action_locations.rb +103 -0
  118. data/lib/ibex/codegen/action_method_source.rb +209 -0
  119. data/lib/ibex/codegen/action_source.rb +119 -0
  120. data/lib/ibex/codegen/ambiguity.rb +174 -0
  121. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  122. data/lib/ibex/codegen/documentation.rb +170 -0
  123. data/lib/ibex/codegen/explain.rb +380 -0
  124. data/lib/ibex/codegen/generated_action_abi.rb +293 -0
  125. data/lib/ibex/codegen/html.rb +94 -10
  126. data/lib/ibex/codegen/mermaid.rb +43 -0
  127. data/lib/ibex/codegen/railroad.rb +197 -0
  128. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  129. data/lib/ibex/codegen/rbs.rb +331 -2
  130. data/lib/ibex/codegen/report.rb +67 -15
  131. data/lib/ibex/codegen/ruby.rb +305 -66
  132. data/lib/ibex/codegen/ruby_actions.rb +146 -0
  133. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  134. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  135. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  136. data/lib/ibex/codegen/ruby_syntax.rb +96 -0
  137. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  138. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  139. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  140. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  141. data/lib/ibex/configuration/explanation.rb +421 -0
  142. data/lib/ibex/configuration/inspector.rb +232 -0
  143. data/lib/ibex/configuration.rb +580 -0
  144. data/lib/ibex/coverage/collector.rb +198 -0
  145. data/lib/ibex/coverage/event_stream.rb +103 -0
  146. data/lib/ibex/coverage/report.rb +267 -0
  147. data/lib/ibex/coverage/runtime_event_validator.rb +169 -0
  148. data/lib/ibex/coverage.rb +13 -0
  149. data/lib/ibex/delta_reducer.rb +99 -0
  150. data/lib/ibex/diff.rb +140 -0
  151. data/lib/ibex/equiv/machine.rb +135 -0
  152. data/lib/ibex/equiv.rb +373 -0
  153. data/lib/ibex/error_messages/parser.rb +159 -0
  154. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  155. data/lib/ibex/error_messages/renderer.rb +65 -0
  156. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  157. data/lib/ibex/error_messages/update.rb +169 -0
  158. data/lib/ibex/error_messages.rb +165 -0
  159. data/lib/ibex/fix.rb +557 -0
  160. data/lib/ibex/frontend/ast.rb +166 -7
  161. data/lib/ibex/frontend/bootstrap_parser.rb +40 -4
  162. data/lib/ibex/frontend/diagnostic.rb +81 -0
  163. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  164. data/lib/ibex/frontend/dsl.rb +66 -7
  165. data/lib/ibex/frontend/formatter.rb +453 -0
  166. data/lib/ibex/frontend/generated_parser.rb +644 -190
  167. data/lib/ibex/frontend/generated_parser_base.rb +170 -32
  168. data/lib/ibex/frontend/generated_parser_includes.rb +51 -0
  169. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  170. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  171. data/lib/ibex/frontend/generation.rb +33 -0
  172. data/lib/ibex/frontend/lexer.rb +162 -10
  173. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  174. data/lib/ibex/frontend/parser/declarations.rb +262 -11
  175. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  176. data/lib/ibex/frontend/parser/rules.rb +43 -6
  177. data/lib/ibex/frontend/parser.rb +182 -4
  178. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  179. data/lib/ibex/frontend/regenerator.rb +27 -1
  180. data/lib/ibex/frontend/resolution.rb +70 -0
  181. data/lib/ibex/frontend/resolver.rb +217 -0
  182. data/lib/ibex/frontend/rule_documentation.rb +104 -0
  183. data/lib/ibex/frontend/source_cursor.rb +133 -9
  184. data/lib/ibex/frontend/source_document.rb +229 -0
  185. data/lib/ibex/frontend/source_loader.rb +150 -0
  186. data/lib/ibex/frontend/source_span.rb +89 -0
  187. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  188. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  189. data/lib/ibex/frontend/token_adapter/declaration_state.rb +244 -22
  190. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  191. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  192. data/lib/ibex/frontend/token_adapter.rb +18 -3
  193. data/lib/ibex/frontend.rb +14 -2
  194. data/lib/ibex/fuzz.rb +219 -0
  195. data/lib/ibex/generation_input.rb +57 -0
  196. data/lib/ibex/generation_manifest.rb +211 -0
  197. data/lib/ibex/generation_transaction.rb +261 -0
  198. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  199. data/lib/ibex/generation_transaction_validation.rb +196 -0
  200. data/lib/ibex/grammar_tests.rb +206 -0
  201. data/lib/ibex/ir/automaton_ir.rb +86 -11
  202. data/lib/ibex/ir/grammar_ir.rb +170 -26
  203. data/lib/ibex/ir/lexer_ir.rb +76 -0
  204. data/lib/ibex/ir/parser_contract.rb +135 -0
  205. data/lib/ibex/ir/serialize.rb +268 -50
  206. data/lib/ibex/ir/validator/automaton.rb +375 -0
  207. data/lib/ibex/ir/validator/base.rb +147 -0
  208. data/lib/ibex/ir/validator/grammar.rb +634 -0
  209. data/lib/ibex/ir/validator/lexer.rb +113 -0
  210. data/lib/ibex/ir/validator.rb +63 -0
  211. data/lib/ibex/ir.rb +71 -5
  212. data/lib/ibex/lalr/build_metrics.rb +71 -0
  213. data/lib/ibex/lalr/builder.rb +514 -52
  214. data/lib/ibex/lalr/conflict.rb +1 -0
  215. data/lib/ibex/lalr/conflict_search.rb +26 -9
  216. data/lib/ibex/lalr/counterexample.rb +38 -18
  217. data/lib/ibex/lalr/direct_lookaheads.rb +289 -0
  218. data/lib/ibex/lalr/goto_follows.rb +229 -0
  219. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  220. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  221. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  222. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  223. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  224. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  225. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  226. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  227. data/lib/ibex/lalr/ielr_partition.rb +166 -0
  228. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  229. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  230. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  231. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  232. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  233. data/lib/ibex/lalr.rb +59 -1
  234. data/lib/ibex/location.rb +129 -0
  235. data/lib/ibex/lsp/document_handlers.rb +68 -0
  236. data/lib/ibex/lsp/document_store.rb +264 -0
  237. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  238. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  239. data/lib/ibex/lsp/initialization_handlers.rb +80 -0
  240. data/lib/ibex/lsp/navigation_handlers.rb +70 -0
  241. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  242. data/lib/ibex/lsp/position_codec.rb +113 -0
  243. data/lib/ibex/lsp/protocol_error.rb +33 -0
  244. data/lib/ibex/lsp/request_handlers.rb +46 -0
  245. data/lib/ibex/lsp/request_support.rb +94 -0
  246. data/lib/ibex/lsp/server.rb +148 -0
  247. data/lib/ibex/lsp/symbol_index.rb +241 -0
  248. data/lib/ibex/lsp/symbol_index_builder.rb +277 -0
  249. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  250. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  251. data/lib/ibex/lsp/symbol_occurrence.rb +23 -0
  252. data/lib/ibex/lsp/transport.rb +128 -0
  253. data/lib/ibex/lsp/workspace.rb +128 -0
  254. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  255. data/lib/ibex/lsp.rb +33 -0
  256. data/lib/ibex/messages/en.yml +28 -0
  257. data/lib/ibex/messages/ja.yml +28 -0
  258. data/lib/ibex/messages.rb +68 -0
  259. data/lib/ibex/metrics.rb +175 -0
  260. data/lib/ibex/normalize/declarations.rb +142 -7
  261. data/lib/ibex/normalize/diagnostics.rb +94 -24
  262. data/lib/ibex/normalize/expander.rb +64 -59
  263. data/lib/ibex/normalize/expression.rb +63 -40
  264. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  265. data/lib/ibex/normalize/inline_expansion.rb +465 -0
  266. data/lib/ibex/normalize/inline_validation.rb +174 -0
  267. data/lib/ibex/normalize/lexer.rb +131 -0
  268. data/lib/ibex/normalize/named_references.rb +60 -0
  269. data/lib/ibex/normalize/nodes.rb +46 -0
  270. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +70 -0
  271. data/lib/ibex/normalize/parameter_lowering.rb +143 -0
  272. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  273. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  274. data/lib/ibex/normalize/parameters.rb +226 -0
  275. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  276. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  277. data/lib/ibex/normalize.rb +172 -17
  278. data/lib/ibex/racc_migration/checker.rb +122 -0
  279. data/lib/ibex/racc_migration/harness.rb +177 -0
  280. data/lib/ibex/racc_migration/report.rb +97 -0
  281. data/lib/ibex/racc_migration.rb +12 -0
  282. data/lib/ibex/rake_task.rb +116 -0
  283. data/lib/ibex/samples.rb +216 -0
  284. data/lib/ibex/table_artifact/builder.rb +266 -0
  285. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  286. data/lib/ibex/table_artifact/document.rb +39 -0
  287. data/lib/ibex/table_artifact/executor.rb +187 -0
  288. data/lib/ibex/table_artifact/serializer.rb +57 -0
  289. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  290. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  291. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  292. data/lib/ibex/table_artifact/validator.rb +253 -0
  293. data/lib/ibex/table_artifact.rb +88 -0
  294. data/lib/ibex/table_simulation/result.rb +55 -0
  295. data/lib/ibex/table_simulation/simulator.rb +253 -0
  296. data/lib/ibex/table_simulation/step.rb +62 -0
  297. data/lib/ibex/table_simulation/text.rb +31 -0
  298. data/lib/ibex/table_simulation.rb +13 -0
  299. data/lib/ibex/tables.rb +22 -69
  300. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  301. data/lib/ibex/verification_report/builder.rb +159 -0
  302. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  303. data/lib/ibex/verification_report/logical_path.rb +68 -0
  304. data/lib/ibex/verification_report/validator.rb +329 -0
  305. data/lib/ibex/verification_report.rb +69 -0
  306. data/lib/ibex/verify/action_correspondence.rb +138 -0
  307. data/lib/ibex/verify/language_witness.rb +295 -0
  308. data/lib/ibex/verify/reference_collection.rb +190 -0
  309. data/lib/ibex/verify/result.rb +73 -0
  310. data/lib/ibex/verify/verifier.rb +598 -0
  311. data/lib/ibex/verify.rb +15 -0
  312. data/lib/ibex/version.rb +1 -1
  313. data/lib/ibex/watch/runner.rb +181 -0
  314. data/lib/ibex/watch/source_snapshot.rb +98 -0
  315. data/lib/ibex/watch.rb +11 -0
  316. data/lib/ibex.rb +35 -1
  317. data/schema/automaton-ir-definitions.schema.json +401 -0
  318. data/schema/automaton-ir.schema.json +65 -0
  319. data/schema/benchmark-v1.schema.json +212 -0
  320. data/schema/benchmark-v2.schema.json +61 -0
  321. data/schema/bison-import-v1.schema.json +106 -0
  322. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  323. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  324. data/schema/construction-profile-v1.schema.json +779 -0
  325. data/schema/cst-v1.json +170 -0
  326. data/schema/diff-v1.schema.json +41 -0
  327. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  328. data/schema/equiv-v1.schema.json +64 -0
  329. data/schema/error-ux-review-v1.schema.json +609 -0
  330. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  331. data/schema/error-ux-round2-v1.schema.json +519 -0
  332. data/schema/error-ux-v1.schema.json +258 -0
  333. data/schema/explain-v1.schema.json +495 -0
  334. data/schema/fix-v1.schema.json +64 -0
  335. data/schema/fix-v2.schema.json +66 -0
  336. data/schema/fix-v3.schema.json +101 -0
  337. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  338. data/schema/fuzz-regression-v1.schema.json +79 -0
  339. data/schema/fuzz-v1.schema.json +114 -0
  340. data/schema/generation-manifest-v1.schema.json +115 -0
  341. data/schema/grammar-ir-extensions.schema.json +734 -0
  342. data/schema/grammar-ir-foundation.schema.json +426 -0
  343. data/schema/grammar-ir.schema.json +271 -0
  344. data/schema/ielr-benchmark-v1.schema.json +85 -0
  345. data/schema/lexer-ir-v1.schema.json +215 -0
  346. data/schema/lexer-profile-v1.schema.json +491 -0
  347. data/schema/metrics-v1.schema.json +60 -0
  348. data/schema/migration-check-v1.schema.json +60 -0
  349. data/schema/performance-comparison-v1.schema.json +395 -0
  350. data/schema/public-performance-comparison-v1.schema.json +506 -0
  351. data/schema/public-performance-profile-v1.schema.json +360 -0
  352. data/schema/reduce-v1.schema.json +22 -0
  353. data/schema/reduce-v2.schema.json +63 -0
  354. data/schema/runtime-coverage-v1.schema.json +86 -0
  355. data/schema/runtime-event-v1.schema.json +308 -0
  356. data/schema/table-artifact-v1.schema.json +377 -0
  357. data/schema/table-simulation-v1.schema.json +70 -0
  358. data/schema/verification-report-v1.schema.json +222 -0
  359. data/schema/verify-v1.schema.json +42 -0
  360. data/sig/ibex/analysis/digraph.rbs +19 -0
  361. data/sig/ibex/artifact_set.rbs +37 -0
  362. data/sig/ibex/bison_import/importer.rbs +140 -0
  363. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  364. data/sig/ibex/bison_import.rbs +95 -0
  365. data/sig/ibex/bounded_subprocess.rbs +76 -0
  366. data/sig/ibex/cli/ambiguity.rbs +28 -0
  367. data/sig/ibex/cli/analysis.rbs +43 -0
  368. data/sig/ibex/cli/bison_import.rbs +32 -0
  369. data/sig/ibex/cli/config.rbs +35 -0
  370. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  371. data/sig/ibex/cli/coverage.rbs +53 -0
  372. data/sig/ibex/cli/debug.rbs +28 -0
  373. data/sig/ibex/cli/diagnostics.rbs +51 -0
  374. data/sig/ibex/cli/documentation.rbs +29 -0
  375. data/sig/ibex/cli/equiv.rbs +49 -0
  376. data/sig/ibex/cli/error_messages.rbs +63 -0
  377. data/sig/ibex/cli/explain.rbs +31 -0
  378. data/sig/ibex/cli/fix.rbs +53 -0
  379. data/sig/ibex/cli/formatting.rbs +107 -0
  380. data/sig/ibex/cli/fuzz.rbs +47 -0
  381. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  382. data/sig/ibex/cli/generation_artifacts.rbs +69 -0
  383. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  384. data/sig/ibex/cli/grammar_tests.rbs +48 -0
  385. data/sig/ibex/cli/ir_tools.rbs +39 -0
  386. data/sig/ibex/cli/lsp.rbs +14 -0
  387. data/sig/ibex/cli/outputs.rbs +26 -3
  388. data/sig/ibex/cli/racc_migration.rbs +30 -0
  389. data/sig/ibex/cli/reduce.rbs +60 -0
  390. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  391. data/sig/ibex/cli/samples.rbs +38 -0
  392. data/sig/ibex/cli/verify.rbs +28 -0
  393. data/sig/ibex/cli/watch.rbs +43 -0
  394. data/sig/ibex/cli.rbs +182 -9
  395. data/sig/ibex/codegen/action_locations.rbs +45 -0
  396. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  397. data/sig/ibex/codegen/action_source.rbs +50 -0
  398. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  399. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  400. data/sig/ibex/codegen/documentation.rbs +50 -0
  401. data/sig/ibex/codegen/explain.rbs +105 -0
  402. data/sig/ibex/codegen/generated_action_abi.rbs +105 -0
  403. data/sig/ibex/codegen/html.rbs +18 -2
  404. data/sig/ibex/codegen/mermaid.rbs +16 -0
  405. data/sig/ibex/codegen/railroad.rbs +82 -0
  406. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  407. data/sig/ibex/codegen/rbs.rbs +86 -4
  408. data/sig/ibex/codegen/report.rbs +16 -8
  409. data/sig/ibex/codegen/ruby.rbs +105 -28
  410. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  411. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  412. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  413. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  414. data/sig/ibex/codegen/ruby_syntax.rbs +27 -0
  415. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  416. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  417. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  418. data/sig/ibex/configuration/explanation.rbs +195 -0
  419. data/sig/ibex/configuration/inspector.rbs +53 -0
  420. data/sig/ibex/configuration.rbs +242 -0
  421. data/sig/ibex/coverage/collector.rbs +78 -0
  422. data/sig/ibex/coverage/event_stream.rbs +44 -0
  423. data/sig/ibex/coverage/report.rbs +102 -0
  424. data/sig/ibex/coverage/runtime_event_validator.rbs +70 -0
  425. data/sig/ibex/coverage.rbs +7 -0
  426. data/sig/ibex/delta_reducer.rbs +38 -0
  427. data/sig/ibex/diff.rbs +47 -0
  428. data/sig/ibex/equiv/machine.rbs +54 -0
  429. data/sig/ibex/equiv.rbs +129 -0
  430. data/sig/ibex/error_messages/parser.rbs +58 -0
  431. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  432. data/sig/ibex/error_messages/renderer.rbs +23 -0
  433. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  434. data/sig/ibex/error_messages/update.rbs +43 -0
  435. data/sig/ibex/error_messages.rbs +85 -0
  436. data/sig/ibex/fix.rbs +138 -0
  437. data/sig/ibex/frontend/ast.rbs +237 -22
  438. data/sig/ibex/frontend/bootstrap_parser.rbs +13 -0
  439. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  440. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  441. data/sig/ibex/frontend/dsl.rbs +33 -4
  442. data/sig/ibex/frontend/formatter.rbs +143 -0
  443. data/sig/ibex/frontend/generated_parser.rbs +228 -68
  444. data/sig/ibex/frontend/generated_parser_base.rbs +65 -12
  445. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  446. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  447. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  448. data/sig/ibex/frontend/generation.rbs +6 -0
  449. data/sig/ibex/frontend/lexer.rbs +49 -2
  450. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  451. data/sig/ibex/frontend/parser/declarations.rbs +57 -0
  452. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  453. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  454. data/sig/ibex/frontend/parser.rbs +66 -0
  455. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  456. data/sig/ibex/frontend/regenerator.rbs +11 -0
  457. data/sig/ibex/frontend/resolution.rbs +34 -0
  458. data/sig/ibex/frontend/resolver.rbs +91 -0
  459. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  460. data/sig/ibex/frontend/source_cursor.rbs +38 -5
  461. data/sig/ibex/frontend/source_document.rbs +119 -0
  462. data/sig/ibex/frontend/source_loader.rbs +66 -0
  463. data/sig/ibex/frontend/source_span.rbs +56 -0
  464. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  465. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  466. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +83 -3
  467. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  468. data/sig/ibex/frontend/token_adapter.rbs +11 -2
  469. data/sig/ibex/frontend.rbs +1 -1
  470. data/sig/ibex/fuzz.rbs +71 -0
  471. data/sig/ibex/generation_input.rbs +37 -0
  472. data/sig/ibex/generation_manifest.rbs +70 -0
  473. data/sig/ibex/generation_transaction.rbs +82 -0
  474. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  475. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  476. data/sig/ibex/grammar_tests.rbs +93 -0
  477. data/sig/ibex/ir/automaton_ir.rbs +35 -10
  478. data/sig/ibex/ir/grammar_ir.rbs +96 -22
  479. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  480. data/sig/ibex/ir/parser_contract.rbs +69 -0
  481. data/sig/ibex/ir/serialize.rbs +75 -23
  482. data/sig/ibex/ir/validator/automaton.rbs +112 -0
  483. data/sig/ibex/ir/validator/base.rbs +69 -0
  484. data/sig/ibex/ir/validator/grammar.rbs +195 -0
  485. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  486. data/sig/ibex/ir/validator.rbs +16 -0
  487. data/sig/ibex/ir.rbs +45 -5
  488. data/sig/ibex/lalr/build_metrics.rbs +60 -0
  489. data/sig/ibex/lalr/builder.rbs +150 -19
  490. data/sig/ibex/lalr/conflict_search.rbs +12 -5
  491. data/sig/ibex/lalr/counterexample.rbs +20 -14
  492. data/sig/ibex/lalr/direct_lookaheads.rbs +93 -0
  493. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  494. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  495. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  496. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  497. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  498. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  499. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  500. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  501. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  502. data/sig/ibex/lalr/ielr_partition.rbs +71 -0
  503. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  504. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  505. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  506. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  507. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  508. data/sig/ibex/lalr.rbs +17 -1
  509. data/sig/ibex/location.rbs +67 -0
  510. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  511. data/sig/ibex/lsp/document_store.rbs +94 -0
  512. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  513. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  514. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  515. data/sig/ibex/lsp/navigation_handlers.rbs +33 -0
  516. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  517. data/sig/ibex/lsp/position_codec.rbs +44 -0
  518. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  519. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  520. data/sig/ibex/lsp/request_support.rbs +40 -0
  521. data/sig/ibex/lsp/server.rbs +55 -0
  522. data/sig/ibex/lsp/symbol_index.rbs +77 -0
  523. data/sig/ibex/lsp/symbol_index_builder.rbs +88 -0
  524. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  525. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  526. data/sig/ibex/lsp/symbol_occurrence.rbs +31 -0
  527. data/sig/ibex/lsp/transport.rbs +47 -0
  528. data/sig/ibex/lsp/workspace.rbs +42 -0
  529. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  530. data/sig/ibex/lsp.rbs +7 -0
  531. data/sig/ibex/messages.rbs +26 -0
  532. data/sig/ibex/metrics.rbs +50 -0
  533. data/sig/ibex/normalize/declarations.rbs +31 -0
  534. data/sig/ibex/normalize/diagnostics.rbs +13 -4
  535. data/sig/ibex/normalize/expander.rbs +22 -16
  536. data/sig/ibex/normalize/expression.rbs +11 -9
  537. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  538. data/sig/ibex/normalize/inline_expansion.rbs +129 -0
  539. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  540. data/sig/ibex/normalize/lexer.rbs +37 -0
  541. data/sig/ibex/normalize/named_references.rbs +20 -0
  542. data/sig/ibex/normalize/nodes.rbs +14 -0
  543. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +33 -0
  544. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  545. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  546. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  547. data/sig/ibex/normalize/parameters.rbs +51 -0
  548. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  549. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  550. data/sig/ibex/normalize.rbs +129 -18
  551. data/sig/ibex/racc_migration/checker.rbs +36 -0
  552. data/sig/ibex/racc_migration/harness.rbs +16 -0
  553. data/sig/ibex/racc_migration/report.rbs +55 -0
  554. data/sig/ibex/racc_migration.rbs +8 -0
  555. data/sig/ibex/rake_task.rbs +51 -0
  556. data/sig/ibex/samples.rbs +60 -0
  557. data/sig/ibex/table_artifact/builder.rbs +93 -0
  558. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  559. data/sig/ibex/table_artifact/document.rbs +27 -0
  560. data/sig/ibex/table_artifact/executor.rbs +66 -0
  561. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  562. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  563. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  564. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  565. data/sig/ibex/table_artifact/validator.rbs +66 -0
  566. data/sig/ibex/table_artifact.rbs +32 -0
  567. data/sig/ibex/table_simulation/result.rbs +36 -0
  568. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  569. data/sig/ibex/table_simulation/step.rbs +42 -0
  570. data/sig/ibex/table_simulation/text.rbs +14 -0
  571. data/sig/ibex/table_simulation.rbs +7 -0
  572. data/sig/ibex/tables.rbs +10 -35
  573. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  574. data/sig/ibex/verification_report/builder.rbs +53 -0
  575. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  576. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  577. data/sig/ibex/verification_report/validator.rbs +114 -0
  578. data/sig/ibex/verification_report.rbs +44 -0
  579. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  580. data/sig/ibex/verify/language_witness.rbs +112 -0
  581. data/sig/ibex/verify/reference_collection.rbs +63 -0
  582. data/sig/ibex/verify/result.rbs +52 -0
  583. data/sig/ibex/verify/verifier.rbs +170 -0
  584. data/sig/ibex/verify.rbs +6 -0
  585. data/sig/ibex/watch/runner.rbs +58 -0
  586. data/sig/ibex/watch/source_snapshot.rbs +46 -0
  587. data/sig/ibex/watch.rbs +7 -0
  588. data/sig/ibex.rbs +2 -0
  589. metadata +524 -19
  590. data/.rubocop.yml +0 -43
  591. data/CHANGELOG.md +0 -30
  592. data/Rakefile +0 -25
  593. data/Steepfile +0 -10
  594. data/docs/compat-notes.md +0 -37
  595. data/docs/lexer-coverage.md +0 -14
  596. data/docs/phase10-extensions.md +0 -27
  597. data/gemfiles/Gemfile +0 -7
  598. data/gemfiles/Gemfile.lock +0 -98
  599. data/lib/ibex/frontend/grammar.y +0 -156
  600. data/lib/ibex/runtime/parser.rb +0 -360
  601. data/lib/ibex/runtime.rb +0 -8
  602. data/sig/ibex/runtime/parser.rbs +0 -167
  603. data/sig/ibex/runtime.rbs +0 -6
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
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser_v2"
4
+
5
+ module Ibex
6
+ module ErrorMessages
7
+ # Strict line-oriented parser for the ibex-messages v1 and v2 formats.
8
+ class Parser
9
+ include ParserV2
10
+
11
+ ESCAPES = { "\\" => "\\", "n" => "\n", "t" => "\t", "r" => "\r" }.freeze
12
+
13
+ # @rbs @file: String
14
+ # @rbs @lines: Array[String]
15
+
16
+ # @rbs (String source, file: String) -> void
17
+ def initialize(source, file:)
18
+ @file = file
19
+ text = source.dup.force_encoding(Encoding::UTF_8)
20
+ fail_at(1, 1, "messages file must be valid UTF-8") unless text.valid_encoding?
21
+
22
+ @lines = text.lines(chomp: true)
23
+ end
24
+
25
+ # @rbs () -> Document
26
+ def parse
27
+ version = validate_header
28
+ return parse_v2 if version == 2
29
+
30
+ parse_v1
31
+ end
32
+
33
+ private
34
+
35
+ # @rbs () -> Document
36
+ def parse_v1
37
+ entries = [] #: Array[Entry]
38
+ declarations = {} #: Hash[Integer, Integer]
39
+ index = 1
40
+ while index < @lines.length
41
+ line = @lines.fetch(index)
42
+ if ignorable?(line)
43
+ index += 1
44
+ next
45
+ end
46
+
47
+ match = line.strip.match(/\A(state|removed)\s+([0-9]+)\z/)
48
+ fail_at(index + 1, column(line), top_level_expectation) unless match
49
+ state_text = match[2] || raise(Ibex::Error, "missing state number")
50
+ state = Integer(state_text, 10)
51
+ reject_duplicate(declarations, state, index, line)
52
+ declarations[state] = index + 1
53
+ entry, index = parse_entry(index, state, match[1] == "state" ? :active : :removed)
54
+ entries << entry
55
+ end
56
+ Document.new(version: 1, entries: entries)
57
+ end
58
+
59
+ # @rbs () -> Integer
60
+ def validate_header
61
+ header = @lines.first&.delete_prefix("\uFEFF")
62
+ return 1 if header == HEADER_V1
63
+ return 2 if header == HEADER
64
+
65
+ fail_at(1, 1, "expected #{HEADER.inspect} or #{HEADER_V1.inspect}")
66
+ end
67
+
68
+ # @rbs (Hash[Integer, Integer] declarations, Integer state, Integer index, String line) -> void
69
+ def reject_duplicate(declarations, state, index, line)
70
+ return unless declarations.key?(state)
71
+
72
+ fail_at(index + 1, column(line),
73
+ "duplicate state #{state}; first declared at line #{declarations.fetch(state)}")
74
+ end
75
+
76
+ # @rbs (Integer opening_index, Integer state, :active | :removed status) -> [Entry, Integer]
77
+ def parse_entry(opening_index, state, status)
78
+ message_lines = [] #: Array[String]
79
+ index = opening_index + 1
80
+ while index < @lines.length
81
+ line = @lines.fetch(index)
82
+ return close_entry(message_lines, opening_index, index, state, status) if line.strip == "end"
83
+
84
+ read_entry_line(message_lines, line, index)
85
+ index += 1
86
+ end
87
+ label = status == :active ? "state" : "removed"
88
+ fail_at(opening_index + 1, 1, "unterminated #{label} #{state} entry")
89
+ end
90
+
91
+ # @rbs (Array[String] message_lines, Integer opening_index, Integer index, Integer state,
92
+ # :active | :removed status) -> [Entry, Integer]
93
+ def close_entry(message_lines, opening_index, index, state, status)
94
+ message = message_lines.empty? ? nil : message_lines.join("\n")
95
+ fail_at(opening_index + 1, 1, "message for state #{state} must not be empty") if message&.strip&.empty?
96
+
97
+ entry = Entry.new(state: state, status: status, message: message, line: opening_index + 1)
98
+ [entry, index + 1]
99
+ end
100
+
101
+ # @rbs (Array[String] message_lines, String line, Integer index) -> void
102
+ def read_entry_line(message_lines, line, index)
103
+ return if ignorable?(line)
104
+
105
+ unless line.start_with?("|")
106
+ fail_at(index + 1, column(line), "expected a `| ` message line, a comment, a blank line, or `end`")
107
+ end
108
+
109
+ content = line.delete_prefix("|")
110
+ content = content.delete_prefix(" ")
111
+ content_column = line.start_with?("| ") ? 3 : 2
112
+ message_lines << decode_line(content, index + 1, content_column)
113
+ end
114
+
115
+ # @rbs (String content, Integer line, Integer content_column) -> String
116
+ def decode_line(content, line, content_column)
117
+ decoded = +""
118
+ index = 0
119
+ while index < content.length
120
+ character = content[index] || raise(Ibex::Error, "missing message character")
121
+ unless character == "\\"
122
+ decoded << character
123
+ index += 1
124
+ next
125
+ end
126
+
127
+ escaped = content[index + 1]
128
+ fail_at(line, content_column + index, "trailing backslash in message line") unless escaped
129
+ replacement = ESCAPES[escaped]
130
+ fail_at(line, content_column + index, "unknown escape \\#{escaped}") unless replacement
131
+
132
+ decoded << replacement
133
+ index += 2
134
+ end
135
+ decoded
136
+ end
137
+
138
+ # @rbs (String line) -> bool
139
+ def ignorable?(line)
140
+ line.strip.empty? || line.lstrip.start_with?("#")
141
+ end
142
+
143
+ # @rbs () -> String
144
+ def top_level_expectation
145
+ "expected `state N`, `removed N`, a comment, or a blank line"
146
+ end
147
+
148
+ # @rbs (String line) -> Integer
149
+ def column(line)
150
+ (line.index(/\S/) || 0) + 1
151
+ end
152
+
153
+ # @rbs (Integer line, Integer column, String message) -> bot
154
+ def fail_at(line, column, message)
155
+ raise Ibex::Error, "#{@file}:#{line}:#{column}: #{message}"
156
+ end
157
+ end
158
+ end
159
+ end