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,598 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Verify
6
+ # Checks Automaton IR semantics against an independently derived collection.
7
+ # rubocop:disable Metrics/ClassLength -- V1-V9 share one violation and item-identity model.
8
+ class Verifier
9
+ DEFAULT_CHECKS = %w[V1 V3 V4 V6 V7 V8].freeze #: Array[String]
10
+ STRICT_CHECKS = %w[V2 V5 V9].freeze #: Array[String]
11
+ ERROR_ACTION = { type: :error }.freeze #: IR::error_action
12
+
13
+ # @rbs (IR::Automaton automaton, ?strict: bool, ?max_states: Integer, ?max_items: Integer) -> void
14
+ def initialize(automaton, strict: false, max_states: 100_000, max_items: 1_000_000)
15
+ @automaton = automaton
16
+ @grammar = automaton.grammar
17
+ @strict = strict
18
+ @max_states = max_states
19
+ @max_items = max_items
20
+ @violations = [] #: Array[Violation]
21
+ @sets = Analysis::Sets.new(@grammar)
22
+ end
23
+
24
+ # @rbs () -> Result
25
+ def verify
26
+ @violations.clear
27
+ verify_grammar_digest
28
+ verify_item_collection
29
+ verify_transitions_and_actions
30
+ verify_table_formats
31
+ verify_reachability_and_productivity
32
+ verify_epsilon_termination
33
+ verify_conflict_determinism
34
+ verify_ielr_adequacy if @strict
35
+ Result.new(
36
+ algorithm: @automaton.algorithm, strict: @strict,
37
+ checks: DEFAULT_CHECKS + (@strict ? STRICT_CHECKS : []),
38
+ violations: @violations,
39
+ bounds: { max_states: @max_states, max_items: @max_items }
40
+ )
41
+ end
42
+
43
+ private
44
+
45
+ # V9 is a bounded semantic witness for IELR only. It deliberately does
46
+ # not replace V1/V2 or claim unbounded language equivalence.
47
+ # @rbs () -> void
48
+ def verify_ielr_adequacy
49
+ return unless @automaton.algorithm == "ielr1"
50
+
51
+ witness = LanguageWitness.new(
52
+ @grammar, @automaton, max_tokens: 3, max_cases: 10_000,
53
+ max_states: @max_states, max_items: @max_items
54
+ ).verify
55
+ if witness.truncated
56
+ violation(
57
+ "V9", "$.states",
58
+ "bounded canonical acceptance witness exhausted its budget " \
59
+ "(max_tokens=#{witness.max_tokens}, max_cases=#{witness.max_cases})"
60
+ )
61
+ end
62
+ witness.differences.each do |difference|
63
+ violation(
64
+ "V9", "$.states",
65
+ "canonical/target acceptance differs for entry #{difference.entry.inspect} " \
66
+ "and tokens #{difference.tokens.inspect}: " \
67
+ "canonical=#{difference.canonical}, target=#{difference.target}"
68
+ )
69
+ end
70
+ rescue BudgetExceeded
71
+ raise
72
+ rescue StandardError => e
73
+ violation("V9", "$.states", "bounded canonical acceptance witness failed: #{e.message}")
74
+ end
75
+
76
+ # @rbs () -> void
77
+ def verify_grammar_digest
78
+ require "digest"
79
+ actual = "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(@grammar))}"
80
+ return if actual == @automaton.grammar_digest
81
+
82
+ violation("V1", "$.grammar_digest", "digest does not match the embedded Grammar IR")
83
+ end
84
+
85
+ # @rbs () -> void
86
+ def verify_item_collection
87
+ case @automaton.algorithm
88
+ when "slr" then compare_slr_states(reference.build(:lr0).states.map { |state| visible_state(state) })
89
+ when "lalr1" then compare_expected_states(lalr_states)
90
+ when "lr1" then compare_expected_states(lr1_states)
91
+ when "ielr1" then compare_ielr_states(lalr_states)
92
+ else violation("V1", "$.algorithm", "unsupported algorithm #{@automaton.algorithm.inspect}")
93
+ end
94
+ end
95
+
96
+ # SLR lookaheads affect completed items only. The builder may retain
97
+ # propagation lookaheads on non-completed items, but they cannot select
98
+ # an ACTION cell and therefore are outside the SLR semantic contract.
99
+ # @rbs (Array[Set[Array[Integer]]] expected_states) -> void
100
+ def compare_slr_states(expected_states)
101
+ expected_by_core = expected_states.group_by { |state| state.to_a.sort }
102
+ matched_cores = [] #: Array[Array[Array[Integer]]]
103
+ @automaton.states.each do |state|
104
+ core = verify_slr_state(state, expected_by_core)
105
+ matched_cores << core if core
106
+ end
107
+ return unless @strict
108
+
109
+ expected_cores = expected_states.map { |state| state.to_a.sort }
110
+ return if matched_cores.sort == expected_cores.sort
111
+
112
+ violation("V2", "$.states", "SLR state collection is incomplete or contains duplicate LR(0) cores")
113
+ end
114
+
115
+ # @rbs (IR::AutomatonState state,
116
+ # Hash[Array[Array[Integer]], Array[Set[Array[Integer]]]] expected_by_core) ->
117
+ # Array[Array[Integer]]?
118
+ def verify_slr_state(state, expected_by_core)
119
+ actual_core = state.items.map { |item| [item.production, item.dot] }.uniq.sort
120
+ unless expected_by_core.key?(actual_core)
121
+ violation("V1", state_path(state), "item core is not in the independently derived LR(0) collection")
122
+ return
123
+ end
124
+
125
+ state.items.each { |item| verify_slr_lookaheads(state, item) }
126
+ actual_core
127
+ end
128
+
129
+ # @rbs (IR::AutomatonState state, IR::AutomatonItem item) -> void
130
+ def verify_slr_lookaheads(state, item)
131
+ return unless item.dot == rhs_for(item.production).length
132
+
133
+ expected = item.production.negative? ? [eof_id] : follow_ids(item.production)
134
+ return if item.lookaheads == expected.sort
135
+
136
+ violation("V3", state_path(state), "completed item #{item.production} has invalid SLR lookaheads")
137
+ end
138
+
139
+ # @rbs () -> Array[Set[Array[Integer]]]
140
+ def lr1_states
141
+ canonical_lr1_states.map { |state| visible_state(state) }
142
+ end
143
+
144
+ # @rbs () -> Array[Set[Array[Integer]]]
145
+ def lalr_states
146
+ groups = canonical_lr1_states.group_by { |state| core_key(state) }
147
+ groups.values.map do |states|
148
+ merged = states.each_with_object(Set.new) { |state, result| state.each { |item| result << item } }
149
+ visible_state(merged)
150
+ end
151
+ end
152
+
153
+ # @rbs () -> Array[Set[Array[Integer]]]
154
+ def canonical_lr1_states
155
+ reference.build(:lr1).states
156
+ end
157
+
158
+ # @rbs (Array[Set[Array[Integer]]] expected_states) -> void
159
+ def compare_expected_states(expected_states)
160
+ expected_by_core = expected_states.group_by { |state| core_key(state) }
161
+ matched = [] #: Array[Set[Array[Integer]]]
162
+ @automaton.states.each do |state|
163
+ actual = expanded_items(state)
164
+ candidates = expected_by_core.fetch(core_key(actual), [])
165
+ expected = candidates.find { |candidate| actual.subset?(candidate) }
166
+ unless expected
167
+ violation("V1", state_path(state), "item set is not a subset of an independently derived state")
168
+ next
169
+ end
170
+
171
+ matched << expected
172
+ missing = expected - actual
173
+ violation("V2", state_path(state), "item set is missing #{missing.length} derived items") if
174
+ @strict && !missing.empty?
175
+ end
176
+ return unless @strict
177
+
178
+ expected_counts = multiset(expected_states)
179
+ actual_counts = multiset(matched)
180
+ return if expected_counts == actual_counts
181
+
182
+ violation("V2", "$.states", "state collection is incomplete or contains duplicate derived states")
183
+ end
184
+
185
+ # @rbs (Array[Set[Array[Integer]]] expected_states) -> void
186
+ def compare_ielr_states(expected_states)
187
+ expected_by_core = expected_states.to_h { |state| [core_key(state), state] }
188
+ actual_by_core = {} #: Hash[Array[Array[Integer]], Set[Array[Integer]]]
189
+ @automaton.states.each do |state|
190
+ actual = expanded_items(state)
191
+ core = core_key(actual)
192
+ expected = expected_by_core[core]
193
+ unless expected && actual.subset?(expected)
194
+ violation("V1", state_path(state), "IELR state contains an item outside its canonical-core union")
195
+ next
196
+ end
197
+ merged = actual_by_core[core] ||= Set.new
198
+ actual.each { |item| merged << item }
199
+ end
200
+ return unless @strict
201
+
202
+ expected_by_core.each do |core, expected|
203
+ next if actual_by_core.fetch(core, Set.new) == expected
204
+
205
+ violation("V2", "$.states", "IELR partitions do not cover canonical lookaheads for core #{core.inspect}")
206
+ end
207
+ end
208
+
209
+ # @rbs () -> void
210
+ def verify_transitions_and_actions
211
+ @automaton.states.each do |state|
212
+ verify_transition_items(state)
213
+ verify_state_actions(state)
214
+ end
215
+ end
216
+
217
+ # @rbs (IR::AutomatonState state) -> void
218
+ def verify_transition_items(state)
219
+ state.transitions.each do |symbol_id, target_id|
220
+ symbol = @grammar.symbol_by_id(symbol_id)
221
+ target = @automaton.states[target_id]
222
+ unless symbol && target
223
+ violation("V1", state_path(state), "transition references a missing symbol or state")
224
+ next
225
+ end
226
+
227
+ expected = shifted_items(state, symbol_id)
228
+ actual = expanded_items(target)
229
+ missing = expected.reject { |item| actual.include?(item) }
230
+ unless missing.empty?
231
+ violation("V1", state_path(state),
232
+ "transition on #{symbol.name} loses #{missing.length} shifted items")
233
+ end
234
+ if symbol.nonterminal? && state.gotos[symbol_id] != target_id
235
+ violation("V1", state_path(state), "nonterminal transition #{symbol.name} has no matching goto")
236
+ end
237
+ end
238
+ end
239
+
240
+ # @rbs (IR::AutomatonState state) -> void
241
+ def verify_state_actions(state)
242
+ candidates = action_candidates(state)
243
+ conflict_tokens = state.conflicts.filter_map do |conflict|
244
+ @grammar.symbol(conflict.fetch(:symbol))&.id
245
+ end
246
+ if state.default_action && state.default_action[:type] != :reduce
247
+ violation("V4", state_path(state), "default action must be a reduction")
248
+ end
249
+
250
+ @grammar.terminals.each do |terminal|
251
+ expected = candidates.fetch(terminal.id, []).uniq
252
+ actual = effective_action(state, terminal.id)
253
+ verify_terminal_action(state, terminal, expected, actual, conflict_tokens)
254
+ end
255
+ end
256
+
257
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, Array[IR::parser_action] expected,
258
+ # IR::parser_action actual, Array[Integer] conflict_tokens) -> void
259
+ def verify_terminal_action(state, terminal, expected, actual, conflict_tokens)
260
+ if expected.empty?
261
+ verify_error_cell(state, terminal, actual)
262
+ elsif expected.one?
263
+ verify_single_action(state, terminal, expected.fetch(0), actual)
264
+ else
265
+ verify_conflicted_action(state, terminal, expected, actual, conflict_tokens)
266
+ end
267
+ end
268
+
269
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, IR::parser_action actual) -> void
270
+ def verify_error_cell(state, terminal, actual)
271
+ return if actual[:type] == :error
272
+
273
+ violation("V4", state_path(state), "#{terminal.name} replaces an error cell with #{actual.inspect}")
274
+ end
275
+
276
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, IR::parser_action expected,
277
+ # IR::parser_action actual) -> void
278
+ def verify_single_action(state, terminal, expected, actual)
279
+ return if actual == expected
280
+
281
+ violation("V1", state_path(state),
282
+ "#{terminal.name} selects #{actual.inspect}, expected #{expected.inspect}")
283
+ end
284
+
285
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, Array[IR::parser_action] expected,
286
+ # IR::parser_action actual, Array[Integer] conflict_tokens) -> void
287
+ def verify_conflicted_action(state, terminal, expected, actual, conflict_tokens)
288
+ unless expected.include?(actual) || actual[:type] == :error
289
+ violation("V8", state_path(state), "#{terminal.name} selects an action outside its candidates")
290
+ end
291
+ return if conflict_tokens.include?(terminal.id)
292
+
293
+ violation("V8", state_path(state), "#{terminal.name} has multiple actions without a declared resolver")
294
+ end
295
+
296
+ # @rbs () -> void
297
+ def verify_table_formats
298
+ plain = Tables.build(@automaton, format: :plain)
299
+ compact = Tables.build(@automaton, format: :compact)
300
+ @automaton.states.each do |state|
301
+ unless action_row(plain.actions, state.id) == action_row(compact.actions, state.id)
302
+ violation(@strict ? "V5" : "V4", state_path(state), "plain/compact ACTION rows differ")
303
+ end
304
+ unless goto_row(plain.gotos, state.id) == goto_row(compact.gotos, state.id)
305
+ violation(@strict ? "V5" : "V4", state_path(state), "plain/compact GOTO rows differ")
306
+ end
307
+ next if plain.default_actions.fetch(state.id) == compact.default_actions.fetch(state.id)
308
+
309
+ violation(@strict ? "V5" : "V4", state_path(state), "plain/compact default actions differ")
310
+ end
311
+ rescue StandardError => e
312
+ violation(@strict ? "V5" : "V4", "$.states", "table transformation failed: #{e.message}")
313
+ end
314
+
315
+ # @rbs (Tables::action_table table, Integer row) -> Hash[Integer, IR::runtime_action]
316
+ def action_row(table, row)
317
+ table.is_a?(Array) ? table.fetch(row) : table.row(row)
318
+ end
319
+
320
+ # @rbs (Tables::goto_table table, Integer row) -> Hash[Integer, untyped]
321
+ def goto_row(table, row)
322
+ table.is_a?(Array) ? table.fetch(row) : table.row(row)
323
+ end
324
+
325
+ # @rbs () -> void
326
+ def verify_reachability_and_productivity
327
+ reachable = reachable_states
328
+ @automaton.states.each do |state|
329
+ unless reachable.include?(state.id)
330
+ violation("V6", state_path(state),
331
+ "state is unreachable from every entry")
332
+ end
333
+ end
334
+
335
+ productive = productive_nonterminals
336
+ @grammar.nonterminals.each do |symbol|
337
+ unless productive.include?(symbol.id)
338
+ violation("V6", "$.grammar.symbols[#{symbol.id}]",
339
+ "nonterminal #{symbol.name} derives no terminal sentence")
340
+ end
341
+ end
342
+ end
343
+
344
+ # @rbs () -> void
345
+ def verify_epsilon_termination
346
+ @grammar.terminals.each do |terminal|
347
+ edges = {} #: Hash[Integer, Integer]
348
+ @automaton.states.each do |state|
349
+ action = effective_action(state, terminal.id)
350
+ next unless action[:type] == :reduce
351
+
352
+ production = @grammar.productions[action.fetch(:production)]
353
+ next unless production&.rhs&.empty?
354
+
355
+ target = state.gotos[production.lhs]
356
+ edges[state.id] = target if target
357
+ end
358
+ next unless directed_cycle?(edges)
359
+
360
+ violation("V7", "$.states", "epsilon reductions can cycle without consuming #{terminal.name}")
361
+ end
362
+ end
363
+
364
+ # @rbs () -> void
365
+ def verify_conflict_determinism
366
+ @automaton.states.each do |state|
367
+ grouped = state.conflicts.group_by { |conflict| conflict.fetch(:symbol) }
368
+ grouped.each do |symbol, conflicts|
369
+ violation("V8", state_path(state), "#{symbol} has more than one declared resolver") if conflicts.length > 1
370
+ resolution = conflicts.first&.fetch(:resolution, nil)
371
+ unless resolution&.key?(:by) && resolution.key?(:chose)
372
+ violation("V8", state_path(state), "#{symbol} has an incomplete resolver")
373
+ next
374
+ end
375
+
376
+ verify_conflict_resolution(state, conflicts.first)
377
+ end
378
+ end
379
+ end
380
+
381
+ # @rbs (IR::AutomatonState state, IR::conflict conflict) -> void
382
+ def verify_conflict_resolution(state, conflict)
383
+ terminal = @grammar.symbol(conflict.fetch(:symbol))
384
+ unless terminal&.terminal?
385
+ violation("V8", state_path(state), "resolver references a missing terminal")
386
+ return
387
+ end
388
+
389
+ candidates = action_candidates(state).fetch(terminal.id, []).uniq
390
+ expected = resolved_conflict_action(conflict)
391
+ unless conflict_candidates(conflict).all? { |candidate| candidates.include?(candidate) }
392
+ violation("V8", state_path(state), "#{terminal.name} resolver names actions outside the cell")
393
+ end
394
+ return if expected && effective_action(state, terminal.id) == expected
395
+
396
+ violation("V8", state_path(state), "#{terminal.name} resolver choice does not match the ACTION cell")
397
+ end
398
+
399
+ # @rbs (IR::conflict conflict) -> Array[IR::parser_action]
400
+ def conflict_candidates(conflict)
401
+ if conflict.fetch(:type).to_sym == :shift_reduce
402
+ shift_reduce = conflict #: IR::shift_reduce_conflict
403
+ [
404
+ { type: :shift, state: shift_reduce.fetch(:shift_to) },
405
+ { type: :reduce, production: shift_reduce.fetch(:reduce) }
406
+ ]
407
+ else
408
+ reduce_reduce = conflict #: IR::reduce_reduce_conflict
409
+ reduce_reduce.fetch(:reductions).map { |production| { type: :reduce, production: production } }
410
+ end
411
+ end
412
+
413
+ # @rbs (IR::conflict conflict) -> IR::parser_action?
414
+ def resolved_conflict_action(conflict)
415
+ chosen = conflict.fetch(:resolution).fetch(:chose)
416
+ return { type: :reduce, production: chosen } if chosen.is_a?(Integer) #: IR::reduce_action
417
+
418
+ shift_reduce = conflict #: IR::shift_reduce_conflict
419
+ case chosen.to_sym
420
+ when :shift then { type: :shift, state: shift_reduce.fetch(:shift_to) }
421
+ when :reduce then { type: :reduce, production: shift_reduce.fetch(:reduce) }
422
+ when :error then ERROR_ACTION
423
+ end
424
+ end
425
+
426
+ # @rbs (IR::AutomatonState state) -> Hash[Integer, Array[IR::parser_action]]
427
+ def action_candidates(state)
428
+ candidates = Hash.new { |hash, key| hash[key] = [] } #: Hash[Integer, Array[IR::parser_action]]
429
+ state.transitions.each do |symbol_id, target|
430
+ symbol = @grammar.symbol_by_id(symbol_id)
431
+ candidates[symbol_id] << { type: :shift, state: target } if symbol&.terminal?
432
+ end
433
+ state.items.each do |item|
434
+ next unless item.dot == rhs_for(item.production).length
435
+
436
+ item.lookaheads.each do |lookahead|
437
+ action = if item.production.negative?
438
+ { type: :accept } #: IR::accept_action
439
+ else
440
+ { type: :reduce, production: item.production } #: IR::reduce_action
441
+ end
442
+ candidates[lookahead] << action
443
+ end
444
+ end
445
+ candidates
446
+ end
447
+
448
+ # @rbs (IR::AutomatonState state, Integer symbol_id) -> Array[Array[Integer]]
449
+ def shifted_items(state, symbol_id)
450
+ state.items.filter_map do |item|
451
+ expected_symbol = rhs_for(item.production)[item.dot]
452
+ if item.production.negative? && item.dot.zero?
453
+ next unless start_symbol_ids.include?(symbol_id)
454
+ else
455
+ next unless expected_symbol == symbol_id
456
+ end
457
+
458
+ item.lookaheads.map { |lookahead| [item.production, item.dot + 1, lookahead] }
459
+ end.flatten(1)
460
+ end
461
+
462
+ # @rbs (IR::AutomatonState state, Integer terminal_id) -> IR::parser_action
463
+ def effective_action(state, terminal_id)
464
+ state.actions.fetch(terminal_id, state.default_action || ERROR_ACTION)
465
+ end
466
+
467
+ # @rbs () -> Set[Integer]
468
+ def reachable_states
469
+ found = Set.new
470
+ queue = @automaton.entry_states.values.dup
471
+ until queue.empty?
472
+ id = queue.shift
473
+ next if found.include?(id)
474
+
475
+ found << id
476
+ state = @automaton.states[id]
477
+ next unless state
478
+
479
+ queue.concat(state.transitions.values)
480
+ queue.concat(state.gotos.values)
481
+ state.actions.each_value { |action| queue << action[:state] if action[:type] == :shift }
482
+ end
483
+ found
484
+ end
485
+
486
+ # @rbs () -> Set[Integer]
487
+ def productive_nonterminals
488
+ productive = Set.new
489
+ pending = @grammar.productions.dup
490
+ loop do
491
+ changed = false
492
+ pending.delete_if do |production|
493
+ ready = production.rhs.all? do |id|
494
+ symbol = @grammar.symbol_by_id(id)
495
+ symbol&.terminal? || productive.include?(id)
496
+ end
497
+ productive << production.lhs if ready
498
+ changed ||= ready
499
+ ready
500
+ end
501
+ break unless changed
502
+ end
503
+ productive
504
+ end
505
+
506
+ # @rbs (Hash[Integer, Integer] edges) -> bool
507
+ def directed_cycle?(edges)
508
+ edges.each_key do |start|
509
+ seen = Set.new
510
+ cursor = start
511
+ while cursor && edges.key?(cursor)
512
+ return true if seen.include?(cursor)
513
+
514
+ seen << cursor
515
+ cursor = edges[cursor]
516
+ end
517
+ end
518
+ false
519
+ end
520
+
521
+ # @rbs (IR::AutomatonState state) -> Set[Array[Integer]]
522
+ def expanded_items(state)
523
+ state.items.each_with_object(Set.new) do |item, result|
524
+ item.lookaheads.each { |lookahead| result << [item.production, item.dot, lookahead] }
525
+ end
526
+ end
527
+
528
+ # Automaton IR intentionally exposes every augmented production as -1,
529
+ # even when construction internally assigned one per start symbol.
530
+ # @rbs (Set[Array[Integer]] state) -> Set[Array[Integer]]
531
+ def visible_state(state)
532
+ state.each_with_object(Set.new) do |item, result|
533
+ production = item.fetch(0)
534
+ dot = item.fetch(1)
535
+ lookahead = item[2]
536
+ result << [production.negative? ? -1 : production, dot, lookahead].compact
537
+ end
538
+ end
539
+
540
+ # @rbs (Set[Array[Integer]] state) -> Array[Array[Integer]]
541
+ def core_key(state)
542
+ state.map { |item| [item.fetch(0), item.fetch(1)] }.uniq.sort
543
+ end
544
+
545
+ # @rbs (Array[Set[Array[Integer]]] states) -> Hash[Array[Array[Integer]], Integer]
546
+ def multiset(states)
547
+ states.each_with_object(Hash.new(0)) { |state, counts| counts[state.to_a.sort] += 1 }
548
+ end
549
+
550
+ # @rbs (Integer production_id) -> Array[Integer]
551
+ def follow_ids(production_id)
552
+ production = @grammar.productions.fetch(production_id)
553
+ names = @sets.follow(production.lhs)
554
+ names.map { |name| @grammar.symbol(name)&.id }.compact
555
+ end
556
+
557
+ # @rbs (Integer production_id) -> Array[Integer]
558
+ def rhs_for(production_id)
559
+ return [start_symbol(production_id).id] if production_id.negative?
560
+
561
+ production = @grammar.productions[production_id]
562
+ production&.rhs || []
563
+ end
564
+
565
+ # @rbs (Integer production_id) -> IR::GrammarSymbol
566
+ def start_symbol(production_id)
567
+ name = @grammar.starts.fetch(-production_id - 1)
568
+ @grammar.symbol(name) || raise(Ibex::Error, "(verify):1:1: missing start symbol #{name}")
569
+ end
570
+
571
+ # @rbs () -> Integer
572
+ def eof_id
573
+ @grammar.symbol("$eof")&.id || raise(Ibex::Error, "(verify):1:1: grammar has no $eof terminal")
574
+ end
575
+
576
+ # @rbs () -> Array[Integer]
577
+ def start_symbol_ids
578
+ @start_symbol_ids ||= @grammar.starts.map do |name|
579
+ @grammar.symbol(name)&.id || raise(Ibex::Error, "(verify):1:1: missing start symbol #{name}")
580
+ end
581
+ end
582
+
583
+ # @rbs () -> ReferenceCollection
584
+ def reference
585
+ @reference ||= ReferenceCollection.new(@grammar, max_states: @max_states, max_items: @max_items)
586
+ end
587
+
588
+ # @rbs (String id, String location, String message) -> void
589
+ def violation(id, location, message)
590
+ @violations << Violation.new(id: id, location: location, message: message).freeze
591
+ end
592
+
593
+ # @rbs (IR::AutomatonState state) -> String
594
+ def state_path(state) = "$.states[#{state.id}]"
595
+ end
596
+ # rubocop:enable Metrics/ClassLength
597
+ end
598
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "analysis"
5
+ require_relative "tables"
6
+ require_relative "verify/reference_collection"
7
+ require_relative "verify/result"
8
+ require_relative "verify/verifier"
9
+ require_relative "verify/action_correspondence"
10
+ require_relative "verify/language_witness"
11
+
12
+ module Ibex
13
+ module Verify
14
+ end
15
+ end
data/lib/ibex/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Ibex generates and runs Pure Ruby LR parsers.
4
4
  module Ibex
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
@@ -3,13 +3,22 @@
3
3
 
4
4
  module Ibex
5
5
  module Watch
6
+ # @rbs!
7
+ # interface _BuildResult
8
+ # def source_paths: () -> Array[String]
9
+ # def attempted_paths: () -> Array[String]
10
+ # end
11
+ # interface _Output
12
+ # def puts: (*Object?) -> Object?
13
+ # end
14
+
6
15
  # Polls source fingerprints, debounces changes, and publishes stable builds.
7
16
  class Runner
8
17
  DEFAULT_INTERVAL = 0.25 #: Float
9
18
  DEFAULT_DEBOUNCE = 0.05 #: Float
10
19
 
11
20
  # rubocop:disable Layout/LineLength
12
- # @rbs (paths: Array[String], build: ^() -> untyped, publish: ^(untyped, SourceSnapshot, ^() -> bool) -> void, failure_paths: ^() -> Array[String], stderr: untyped, clock: ^() -> Float, sleeper: ^(Float) -> void, iteration_hook: ^(Symbol, Integer, Array[String]) -> (Integer | Symbol | nil), ?interval: Float, ?debounce: Float) -> void
21
+ # @rbs (paths: Array[String], build: ^() -> _BuildResult, publish: ^(_BuildResult, SourceSnapshot, ^() -> bool) -> void, failure_paths: ^() -> Array[String], stderr: _Output, clock: ^() -> Float, sleeper: ^(Float) -> void, iteration_hook: ^(Symbol, Integer, Array[String]) -> (Integer | Symbol | nil), ?interval: Float, ?debounce: Float) -> void
13
22
  def initialize(paths:, build:, publish:, failure_paths:, stderr:, clock:, sleeper:, iteration_hook:,
14
23
  interval: DEFAULT_INTERVAL, debounce: DEFAULT_DEBOUNCE)
15
24
  @fixed_paths = normalize_paths(paths)