ibex 0.2.0 → 0.4.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 (461) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -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 +274 -0
  40. data/docs/development.md +196 -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 +1617 -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/impact.md +101 -0
  56. data/docs/ja/bison-import.md +51 -0
  57. data/docs/ja/stable-api.md +53 -0
  58. data/docs/lexer-construction-profile.md +109 -0
  59. data/docs/lexer-migration.md +5 -0
  60. data/docs/maturity.md +129 -0
  61. data/docs/project-site-strategy.md +54 -0
  62. data/docs/racc-migration-evidence.md +45 -0
  63. data/docs/racc-migration.md +11 -0
  64. data/docs/release-readiness.md +126 -68
  65. data/docs/repair-semantics.md +123 -0
  66. data/docs/runtime-abi-evolution.md +310 -0
  67. data/docs/stability.md +145 -34
  68. data/docs/status.md +43 -0
  69. data/docs/syntax-sessions.md +183 -0
  70. data/docs/table-artifact.md +125 -0
  71. data/docs/test-interactions.md +219 -0
  72. data/docs/verification-report.md +137 -0
  73. data/docs/verifier-trust-boundary.md +199 -0
  74. data/docs/workloads.md +132 -0
  75. data/lib/ibex/analysis/digraph.rb +128 -0
  76. data/lib/ibex/analysis/sets.rb +14 -0
  77. data/lib/ibex/analysis.rb +1 -0
  78. data/lib/ibex/bison_import/importer.rb +516 -0
  79. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  80. data/lib/ibex/bison_import.rb +200 -0
  81. data/lib/ibex/bounded_subprocess.rb +171 -0
  82. data/lib/ibex/cli/ambiguity.rb +14 -5
  83. data/lib/ibex/cli/analysis.rb +134 -0
  84. data/lib/ibex/cli/bison_import.rb +121 -0
  85. data/lib/ibex/cli/config.rb +128 -0
  86. data/lib/ibex/cli/diagnostics.rb +31 -11
  87. data/lib/ibex/cli/documentation.rb +11 -8
  88. data/lib/ibex/cli/equiv.rb +169 -0
  89. data/lib/ibex/cli/error_messages.rb +11 -4
  90. data/lib/ibex/cli/explain.rb +14 -5
  91. data/lib/ibex/cli/fix.rb +203 -0
  92. data/lib/ibex/cli/formatting.rb +11 -10
  93. data/lib/ibex/cli/fuzz.rb +200 -0
  94. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  95. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  96. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  97. data/lib/ibex/cli/grammar_tests.rb +32 -14
  98. data/lib/ibex/cli/impact.rb +393 -0
  99. data/lib/ibex/cli/ir_tools.rb +5 -61
  100. data/lib/ibex/cli/outputs.rb +60 -33
  101. data/lib/ibex/cli/reduce.rb +202 -0
  102. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  103. data/lib/ibex/cli/samples.rb +49 -23
  104. data/lib/ibex/cli/verify.rb +116 -0
  105. data/lib/ibex/cli/watch.rb +6 -5
  106. data/lib/ibex/cli.rb +362 -56
  107. data/lib/ibex/codegen/action_locations.rb +1 -1
  108. data/lib/ibex/codegen/action_method_source.rb +5 -6
  109. data/lib/ibex/codegen/action_source.rb +3 -2
  110. data/lib/ibex/codegen/ambiguity.rb +5 -4
  111. data/lib/ibex/codegen/explain.rb +123 -55
  112. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  113. data/lib/ibex/codegen/rbs.rb +15 -7
  114. data/lib/ibex/codegen/report.rb +25 -13
  115. data/lib/ibex/codegen/ruby.rb +28 -64
  116. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  117. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  118. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  119. data/lib/ibex/configuration/explanation.rb +421 -0
  120. data/lib/ibex/configuration/inspector.rb +232 -0
  121. data/lib/ibex/configuration.rb +580 -0
  122. data/lib/ibex/coverage/collector.rb +21 -11
  123. data/lib/ibex/coverage/event_stream.rb +13 -7
  124. data/lib/ibex/coverage/report.rb +26 -18
  125. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  126. data/lib/ibex/delta_reducer.rb +99 -0
  127. data/lib/ibex/diff.rb +141 -0
  128. data/lib/ibex/equiv/machine.rb +135 -0
  129. data/lib/ibex/equiv.rb +373 -0
  130. data/lib/ibex/fix.rb +557 -0
  131. data/lib/ibex/frontend/ast.rb +22 -2
  132. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  133. data/lib/ibex/frontend/diagnostic.rb +1 -1
  134. data/lib/ibex/frontend/formatter.rb +72 -26
  135. data/lib/ibex/frontend/generated_parser.rb +147 -117
  136. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  137. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  138. data/lib/ibex/frontend/generation.rb +1 -1
  139. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  140. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  141. data/lib/ibex/frontend/regenerator.rb +1 -0
  142. data/lib/ibex/frontend/resolution.rb +2 -1
  143. data/lib/ibex/frontend/resolver.rb +1 -1
  144. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  145. data/lib/ibex/frontend/source_cursor.rb +2 -2
  146. data/lib/ibex/frontend/source_span.rb +9 -1
  147. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  148. data/lib/ibex/frontend/token_adapter.rb +10 -0
  149. data/lib/ibex/frontend.rb +1 -0
  150. data/lib/ibex/fuzz.rb +219 -0
  151. data/lib/ibex/generation_input.rb +1 -1
  152. data/lib/ibex/generation_manifest.rb +36 -25
  153. data/lib/ibex/generation_transaction.rb +1 -1
  154. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  155. data/lib/ibex/generation_transaction_validation.rb +2 -2
  156. data/lib/ibex/grammar_tests.rb +1 -1
  157. data/lib/ibex/impact/action_impact.rb +148 -0
  158. data/lib/ibex/impact/automaton_impact.rb +57 -0
  159. data/lib/ibex/impact/coverage_impact.rb +53 -0
  160. data/lib/ibex/impact/graph.rb +132 -0
  161. data/lib/ibex/impact/propagation.rb +147 -0
  162. data/lib/ibex/impact/report.rb +273 -0
  163. data/lib/ibex/impact/seeds.rb +69 -0
  164. data/lib/ibex/impact/severity.rb +139 -0
  165. data/lib/ibex/impact.rb +16 -0
  166. data/lib/ibex/ir/automaton_ir.rb +61 -19
  167. data/lib/ibex/ir/grammar_ir.rb +76 -45
  168. data/lib/ibex/ir/lexer_ir.rb +5 -5
  169. data/lib/ibex/ir/parser_contract.rb +135 -0
  170. data/lib/ibex/ir/serialize.rb +203 -76
  171. data/lib/ibex/ir/validator/automaton.rb +64 -34
  172. data/lib/ibex/ir/validator/base.rb +51 -33
  173. data/lib/ibex/ir/validator/grammar.rb +113 -83
  174. data/lib/ibex/ir/validator/lexer.rb +2 -2
  175. data/lib/ibex/ir/validator.rb +2 -1
  176. data/lib/ibex/ir.rb +16 -3
  177. data/lib/ibex/lalr/build_metrics.rb +50 -2
  178. data/lib/ibex/lalr/builder.rb +191 -56
  179. data/lib/ibex/lalr/conflict_search.rb +15 -4
  180. data/lib/ibex/lalr/counterexample.rb +19 -14
  181. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  182. data/lib/ibex/lalr/goto_follows.rb +229 -0
  183. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  184. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  185. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  186. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  187. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  188. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  189. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  190. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  191. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  192. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  193. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  194. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  195. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  196. data/lib/ibex/lalr.rb +52 -1
  197. data/lib/ibex/location.rb +3 -3
  198. data/lib/ibex/lsp/document_handlers.rb +9 -7
  199. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  200. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  201. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  202. data/lib/ibex/lsp/position_codec.rb +6 -2
  203. data/lib/ibex/lsp/request_handlers.rb +3 -2
  204. data/lib/ibex/lsp/request_support.rb +14 -7
  205. data/lib/ibex/lsp/server.rb +13 -11
  206. data/lib/ibex/lsp/symbol_index.rb +13 -13
  207. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  208. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  209. data/lib/ibex/lsp/transport.rb +13 -3
  210. data/lib/ibex/lsp/workspace.rb +5 -4
  211. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  212. data/lib/ibex/lsp.rb +1 -0
  213. data/lib/ibex/messages/en.yml +28 -0
  214. data/lib/ibex/messages/ja.yml +28 -0
  215. data/lib/ibex/messages.rb +68 -0
  216. data/lib/ibex/metrics.rb +175 -0
  217. data/lib/ibex/normalize/declarations.rb +2 -0
  218. data/lib/ibex/normalize/diagnostics.rb +63 -38
  219. data/lib/ibex/normalize/expander.rb +5 -4
  220. data/lib/ibex/normalize/expression.rb +8 -6
  221. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  222. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  223. data/lib/ibex/normalize/lexer.rb +3 -3
  224. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  225. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  226. data/lib/ibex/normalize/parameters.rb +31 -4
  227. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  228. data/lib/ibex/normalize.rb +10 -20
  229. data/lib/ibex/racc_migration/report.rb +6 -3
  230. data/lib/ibex/rake_task.rb +1 -1
  231. data/lib/ibex/samples.rb +43 -13
  232. data/lib/ibex/table_artifact/builder.rb +266 -0
  233. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  234. data/lib/ibex/table_artifact/document.rb +39 -0
  235. data/lib/ibex/table_artifact/executor.rb +187 -0
  236. data/lib/ibex/table_artifact/serializer.rb +57 -0
  237. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  238. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  239. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  240. data/lib/ibex/table_artifact/validator.rb +253 -0
  241. data/lib/ibex/table_artifact.rb +88 -0
  242. data/lib/ibex/table_simulation/result.rb +6 -2
  243. data/lib/ibex/table_simulation/step.rb +3 -1
  244. data/lib/ibex/tables.rb +14 -0
  245. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  246. data/lib/ibex/verification_report/builder.rb +159 -0
  247. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  248. data/lib/ibex/verification_report/logical_path.rb +68 -0
  249. data/lib/ibex/verification_report/validator.rb +329 -0
  250. data/lib/ibex/verification_report.rb +69 -0
  251. data/lib/ibex/verify/action_correspondence.rb +138 -0
  252. data/lib/ibex/verify/language_witness.rb +295 -0
  253. data/lib/ibex/verify/reference_collection.rb +190 -0
  254. data/lib/ibex/verify/result.rb +73 -0
  255. data/lib/ibex/verify/verifier.rb +598 -0
  256. data/lib/ibex/verify.rb +15 -0
  257. data/lib/ibex/version.rb +1 -1
  258. data/lib/ibex/watch/runner.rb +10 -1
  259. data/lib/ibex/watch/source_snapshot.rb +13 -8
  260. data/lib/ibex.rb +11 -0
  261. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  262. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  263. data/schema/bison-import-v1.schema.json +106 -0
  264. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  265. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  266. data/schema/construction-profile-v1.schema.json +779 -0
  267. data/schema/diff-v1.schema.json +41 -0
  268. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  269. data/schema/equiv-v1.schema.json +64 -0
  270. data/schema/error-ux-review-v1.schema.json +609 -0
  271. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  272. data/schema/error-ux-round2-v1.schema.json +519 -0
  273. data/schema/explain-v1.schema.json +63 -1
  274. data/schema/fix-v1.schema.json +64 -0
  275. data/schema/fix-v2.schema.json +66 -0
  276. data/schema/fix-v3.schema.json +101 -0
  277. data/schema/fuzz-regression-v1.schema.json +79 -0
  278. data/schema/fuzz-v1.schema.json +114 -0
  279. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  280. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  281. data/schema/grammar-ir.schema.json +271 -0
  282. data/schema/ielr-benchmark-v1.schema.json +85 -0
  283. data/schema/impact-v1.schema.json +208 -0
  284. data/schema/lexer-profile-v1.schema.json +491 -0
  285. data/schema/metrics-v1.schema.json +60 -0
  286. data/schema/reduce-v1.schema.json +22 -0
  287. data/schema/reduce-v2.schema.json +63 -0
  288. data/schema/table-artifact-v1.schema.json +377 -0
  289. data/schema/verification-report-v1.schema.json +222 -0
  290. data/schema/verify-v1.schema.json +42 -0
  291. data/sig/ibex/analysis/digraph.rbs +19 -0
  292. data/sig/ibex/analysis/sets.rbs +10 -0
  293. data/sig/ibex/bison_import/importer.rbs +140 -0
  294. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  295. data/sig/ibex/bison_import.rbs +95 -0
  296. data/sig/ibex/bounded_subprocess.rbs +76 -0
  297. data/sig/ibex/cli/ambiguity.rbs +6 -0
  298. data/sig/ibex/cli/analysis.rbs +43 -0
  299. data/sig/ibex/cli/bison_import.rbs +32 -0
  300. data/sig/ibex/cli/config.rbs +35 -0
  301. data/sig/ibex/cli/diagnostics.rbs +16 -3
  302. data/sig/ibex/cli/documentation.rbs +5 -1
  303. data/sig/ibex/cli/equiv.rbs +49 -0
  304. data/sig/ibex/cli/error_messages.rbs +6 -0
  305. data/sig/ibex/cli/explain.rbs +6 -0
  306. data/sig/ibex/cli/fix.rbs +53 -0
  307. data/sig/ibex/cli/formatting.rbs +5 -1
  308. data/sig/ibex/cli/fuzz.rbs +47 -0
  309. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  310. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  311. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  312. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  313. data/sig/ibex/cli/impact.rbs +95 -0
  314. data/sig/ibex/cli/ir_tools.rbs +7 -19
  315. data/sig/ibex/cli/outputs.rbs +9 -3
  316. data/sig/ibex/cli/reduce.rbs +60 -0
  317. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  318. data/sig/ibex/cli/samples.rbs +8 -0
  319. data/sig/ibex/cli/verify.rbs +28 -0
  320. data/sig/ibex/cli/watch.rbs +2 -2
  321. data/sig/ibex/cli.rbs +79 -5
  322. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  323. data/sig/ibex/codegen/explain.rbs +45 -25
  324. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  325. data/sig/ibex/codegen/rbs.rbs +12 -10
  326. data/sig/ibex/codegen/report.rbs +12 -12
  327. data/sig/ibex/codegen/ruby.rbs +18 -15
  328. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  329. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  330. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  331. data/sig/ibex/configuration/explanation.rbs +195 -0
  332. data/sig/ibex/configuration/inspector.rbs +53 -0
  333. data/sig/ibex/configuration.rbs +242 -0
  334. data/sig/ibex/coverage/collector.rbs +16 -14
  335. data/sig/ibex/coverage/event_stream.rbs +12 -10
  336. data/sig/ibex/coverage/report.rbs +20 -18
  337. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  338. data/sig/ibex/delta_reducer.rbs +38 -0
  339. data/sig/ibex/diff.rbs +47 -0
  340. data/sig/ibex/equiv/machine.rbs +54 -0
  341. data/sig/ibex/equiv.rbs +129 -0
  342. data/sig/ibex/fix.rbs +138 -0
  343. data/sig/ibex/frontend/ast.rbs +33 -7
  344. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  345. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  346. data/sig/ibex/frontend/formatter.rbs +38 -30
  347. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  348. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  349. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  350. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  351. data/sig/ibex/frontend/resolution.rbs +3 -2
  352. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  353. data/sig/ibex/frontend/source_span.rbs +5 -2
  354. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  355. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  356. data/sig/ibex/fuzz.rbs +71 -0
  357. data/sig/ibex/generation_input.rbs +2 -2
  358. data/sig/ibex/generation_manifest.rbs +35 -32
  359. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  360. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  361. data/sig/ibex/grammar_tests.rbs +2 -2
  362. data/sig/ibex/impact/action_impact.rbs +58 -0
  363. data/sig/ibex/impact/automaton_impact.rbs +37 -0
  364. data/sig/ibex/impact/coverage_impact.rbs +28 -0
  365. data/sig/ibex/impact/graph.rbs +67 -0
  366. data/sig/ibex/impact/propagation.rbs +56 -0
  367. data/sig/ibex/impact/report.rbs +107 -0
  368. data/sig/ibex/impact/seeds.rbs +36 -0
  369. data/sig/ibex/impact/severity.rbs +55 -0
  370. data/sig/ibex/impact.rbs +6 -0
  371. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  372. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  373. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  374. data/sig/ibex/ir/parser_contract.rbs +69 -0
  375. data/sig/ibex/ir/serialize.rbs +63 -28
  376. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  377. data/sig/ibex/ir/validator/base.rbs +35 -31
  378. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  379. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  380. data/sig/ibex/ir.rbs +9 -3
  381. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  382. data/sig/ibex/lalr/builder.rbs +75 -24
  383. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  384. data/sig/ibex/lalr/counterexample.rbs +17 -14
  385. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  386. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  387. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  388. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  389. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  390. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  391. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  392. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  393. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  394. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  395. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  396. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  397. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  398. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  399. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  400. data/sig/ibex/lalr.rbs +11 -1
  401. data/sig/ibex/location.rbs +6 -6
  402. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  403. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  404. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  405. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  406. data/sig/ibex/lsp/position_codec.rbs +8 -4
  407. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  408. data/sig/ibex/lsp/request_support.rbs +8 -8
  409. data/sig/ibex/lsp/server.rbs +16 -16
  410. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  411. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  412. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  413. data/sig/ibex/lsp/transport.rbs +18 -6
  414. data/sig/ibex/lsp/workspace.rbs +3 -2
  415. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  416. data/sig/ibex/messages.rbs +26 -0
  417. data/sig/ibex/metrics.rbs +50 -0
  418. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  419. data/sig/ibex/normalize/expander.rbs +2 -2
  420. data/sig/ibex/normalize/expression.rbs +10 -8
  421. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  422. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  423. data/sig/ibex/normalize/lexer.rbs +4 -4
  424. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  425. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  426. data/sig/ibex/normalize/parameters.rbs +13 -8
  427. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  428. data/sig/ibex/normalize.rbs +11 -5
  429. data/sig/ibex/racc_migration/report.rbs +8 -6
  430. data/sig/ibex/rake_task.rbs +1 -1
  431. data/sig/ibex/samples.rbs +13 -4
  432. data/sig/ibex/table_artifact/builder.rbs +93 -0
  433. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  434. data/sig/ibex/table_artifact/document.rbs +27 -0
  435. data/sig/ibex/table_artifact/executor.rbs +66 -0
  436. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  437. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  438. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  439. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  440. data/sig/ibex/table_artifact/validator.rbs +66 -0
  441. data/sig/ibex/table_artifact.rbs +32 -0
  442. data/sig/ibex/table_simulation/result.rbs +8 -4
  443. data/sig/ibex/table_simulation/step.rbs +4 -2
  444. data/sig/ibex/tables.rbs +10 -9
  445. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  446. data/sig/ibex/verification_report/builder.rbs +53 -0
  447. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  448. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  449. data/sig/ibex/verification_report/validator.rbs +114 -0
  450. data/sig/ibex/verification_report.rbs +44 -0
  451. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  452. data/sig/ibex/verify/language_witness.rbs +112 -0
  453. data/sig/ibex/verify/reference_collection.rbs +63 -0
  454. data/sig/ibex/verify/result.rbs +52 -0
  455. data/sig/ibex/verify/verifier.rbs +170 -0
  456. data/sig/ibex/verify.rbs +6 -0
  457. data/sig/ibex/watch/runner.rbs +12 -2
  458. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  459. metadata +254 -12
  460. data/lib/ibex/ir/migration.rb +0 -120
  461. data/sig/ibex/ir/migration.rbs +0 -34
@@ -3,6 +3,10 @@
3
3
 
4
4
  require "set"
5
5
 
6
+ # This class intentionally owns collection and propagation state so the
7
+ # single-start compatibility path remains byte-identical.
8
+ # rubocop:disable Metrics/AbcSize, Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
9
+
6
10
  module Ibex
7
11
  module LALR
8
12
  # Builds an LR(0) collection and propagates LALR(1) lookaheads directly
@@ -10,53 +14,88 @@ module Ibex
10
14
  class DirectLookaheads
11
15
  AUGMENTED_PRODUCTION = -1 #: Integer
12
16
  EMPTY_PRODUCTIONS = Array.new(0).freeze #: Array[IR::Production]
13
- EMPTY_NODES = Array.new(0).freeze #: Array[lookahead_node]
17
+ EMPTY_NODES = Array.new(0).freeze #: Array[Integer]
14
18
  private_constant :EMPTY_PRODUCTIONS, :EMPTY_NODES
15
19
 
16
- # @rbs @grammar: IR::Grammar
17
- # @rbs @sets: Analysis::Sets
18
- # @rbs @productions_by_lhs: Hash[Integer, Array[IR::Production]]
19
- # @rbs @augmented_rhs: Array[Integer]
20
- # @rbs @production_rhs: Array[Array[Integer]]
21
- # @rbs @augmented_item_cores: Array[item_core]
22
- # @rbs @production_item_cores: Array[Array[item_core]]
23
- # @rbs @terminal_ids: Array[Integer]
24
- # @rbs @terminal_masks: Array[Integer]
25
-
26
- # @rbs (IR::Grammar grammar, Analysis::Sets sets) -> void
27
- def initialize(grammar, sets)
20
+ attr_reader :lr0_state_count #: Integer?
21
+ attr_reader :lr0_item_count #: Integer?
22
+ attr_reader :propagation_edge_count #: Integer?
23
+ attr_reader :states #: Array[core_set]
24
+ attr_reader :transitions #: transitions
25
+
26
+ # @rbs (IR::Grammar grammar, Analysis::Sets sets, ?starts: Array[String]?, ?profile: bool) -> void
27
+ def initialize(grammar, sets, starts: nil, profile: false)
28
28
  @grammar = grammar
29
29
  @sets = sets
30
+ @grammar_starts = grammar_starts.freeze
31
+ @starts = (starts || @grammar_starts).dup
32
+ raise ArgumentError, "starts must be a nonempty subset of grammar starts" if
33
+ @starts.empty? || (@starts - @grammar_starts).any?
34
+
30
35
  @productions_by_lhs = grammar.productions.group_by(&:lhs)
31
- start = grammar.symbol(grammar.start) || raise(Ibex::Error, "missing start symbol")
32
- @augmented_rhs = [start.id].freeze
36
+ @augmented_production_ids = @starts.map { |name| AUGMENTED_PRODUCTION - start_index(name) }
37
+ @augmented_rhs = @starts.each_with_index.to_h do |name, index|
38
+ symbol = grammar.symbol(name) || raise(Ibex::Error, "missing start symbol #{name}")
39
+ [@augmented_production_ids.fetch(index), [symbol.id].freeze]
40
+ end.freeze
33
41
  @production_rhs = grammar.productions.map(&:rhs).freeze
34
- @augmented_item_cores = item_cores_for(AUGMENTED_PRODUCTION, @augmented_rhs.length)
42
+ @augmented_item_cores = @augmented_rhs.to_h do |production_id, rhs|
43
+ [production_id, item_cores_for(production_id, rhs.length)]
44
+ end.freeze
35
45
  @production_item_cores = grammar.productions.map do |production|
36
46
  item_cores_for(production.id, production.rhs.length)
37
47
  end.freeze
48
+ initialize_item_encoding
38
49
  @terminal_ids = grammar.terminals.map(&:id).freeze
39
50
  @terminal_masks = @terminal_ids.map { |id| 1 << id }.freeze
51
+ @terminal_ids_by_bits = {} #: Hash[Integer, Array[Integer]]
52
+ @lr0_state_count = nil
53
+ @lr0_item_count = nil
54
+ @propagation_edge_count = nil
55
+ @profile = profile
40
56
  end
41
57
 
42
58
  # @rbs () -> [Array[packed_items], transitions]
43
59
  def build
44
60
  states, transitions = lr0_collection
61
+ @states = states
62
+ @transitions = transitions
45
63
  lookaheads = empty_lookaheads(states)
46
64
  propagation = propagation_graph(states, transitions, lookaheads)
47
- lookaheads.fetch(0).fetch(item_core(AUGMENTED_PRODUCTION, 0)) << 0
65
+ if @profile
66
+ @lr0_state_count = states.length
67
+ @lr0_item_count = states.sum(&:length)
68
+ @propagation_edge_count = propagation.values.sum(&:length)
69
+ end
70
+ @starts.each_with_index do |_name, index|
71
+ lookaheads.fetch(index).fetch(item_core(augmented_production(index), 0)) << 0
72
+ end
48
73
  propagate(lookaheads, propagation)
49
74
  [lookaheads, transitions]
50
75
  end
51
76
 
77
+ # @rbs (Integer index) -> Integer
78
+ def augmented_production(index)
79
+ @augmented_production_ids.fetch(index)
80
+ end
81
+
52
82
  private
53
83
 
84
+ # @rbs () -> void
85
+ def initialize_item_encoding
86
+ @item_key_stride = [*@augmented_rhs.values, *@production_rhs].map(&:length).max.to_i + 1
87
+ # Negative augmented ids are global to Grammar#starts. A builder
88
+ # may isolate a non-first entry, so reserve the full global range.
89
+ @production_offset = @grammar_starts.length
90
+ @node_stride = (@production_offset + @production_rhs.length) * @item_key_stride
91
+ end
92
+
54
93
  # @rbs () -> [Array[core_set], transitions]
55
94
  def lr0_collection
56
- seed = Set[item_core(AUGMENTED_PRODUCTION, 0)] #: core_set
57
- states = [closure(seed)]
95
+ states = @starts.each_index.map { |index| closure(Set[item_core(augmented_production(index), 0)]) }
58
96
  transitions = [] #: transitions
59
- indexes = { item_key(states.first) => 0 }
97
+ indexes = {} #: Hash[Array[Integer], Integer]
98
+ states.each_with_index { |items, index| indexes[item_key(items)] = index }
60
99
  cursor = 0
61
100
  while cursor < states.length
62
101
  transitions[cursor] = {}
@@ -108,15 +147,13 @@ module Ibex
108
147
 
109
148
  # @rbs (Array[core_set] states) -> Array[packed_items]
110
149
  def empty_lookaheads(states)
111
- states.map do |items|
112
- items.to_h { |item| [item, Set.new] } #: packed_items
113
- end
150
+ states.map { |items| items.to_h { |item| [item, Set.new] } }
114
151
  end
115
152
 
116
153
  # @rbs (Array[core_set] states, transitions transitions, Array[packed_items] lookaheads) ->
117
- # Hash[lookahead_node, Array[lookahead_node]]
154
+ # Hash[Integer, Array[Integer]]
118
155
  def propagation_graph(states, transitions, lookaheads)
119
- edges = Hash.new { |hash, key| hash[key] = [] } #: Hash[lookahead_node, Array[lookahead_node]]
156
+ edges = Hash.new { |hash, key| hash[key] = [] } #: Hash[Integer, Array[Integer]]
120
157
  states.each_with_index do |items, state_id|
121
158
  items.each do |production_id, dot|
122
159
  add_transition_edge(edges, transitions, state_id, production_id, dot)
@@ -127,19 +164,17 @@ module Ibex
127
164
  edges
128
165
  end
129
166
 
130
- # @rbs (Hash[lookahead_node, Array[lookahead_node]] edges, transitions transitions,
167
+ # @rbs (Hash[Integer, Array[Integer]] edges, transitions transitions,
131
168
  # Integer state_id, Integer production_id, Integer dot) -> void
132
169
  def add_transition_edge(edges, transitions, state_id, production_id, dot)
133
170
  symbol_id = rhs_for(production_id)[dot]
134
171
  return unless symbol_id
135
172
 
136
173
  target_state = transitions.fetch(state_id).fetch(symbol_id)
137
- source = [state_id, production_id, dot] #: lookahead_node
138
- target = [target_state, production_id, dot + 1] #: lookahead_node
139
- edges[source] << target
174
+ edges[node_id(state_id, production_id, dot)] << node_id(target_state, production_id, dot + 1)
140
175
  end
141
176
 
142
- # @rbs (Hash[lookahead_node, Array[lookahead_node]] edges, Array[packed_items] lookaheads,
177
+ # @rbs (Hash[Integer, Array[Integer]] edges, Array[packed_items] lookaheads,
143
178
  # Integer state_id, Integer production_id, Integer dot) -> void
144
179
  def add_closure_relations(edges, lookaheads, state_id, production_id, dot)
145
180
  rhs = rhs_for(production_id)
@@ -148,27 +183,22 @@ module Ibex
148
183
 
149
184
  suffix = rhs.drop(dot + 1)
150
185
  spontaneous = terminal_ids(@sets.first_of_sequence(suffix))
151
- source = [state_id, production_id, dot] #: lookahead_node
186
+ source = node_id(state_id, production_id, dot)
152
187
  @productions_by_lhs.fetch(symbol.id, EMPTY_PRODUCTIONS).each do |production|
153
188
  target_item = item_core(production.id, 0)
154
189
  lookaheads.fetch(state_id).fetch(target_item).merge(spontaneous)
155
- if @sets.sequence_nullable?(suffix)
156
- target = [state_id, production.id, 0] #: lookahead_node
157
- edges[source] << target
158
- end
190
+ edges[source] << node_id(state_id, production.id, 0) if @sets.sequence_nullable?(suffix)
159
191
  end
160
192
  end
161
193
 
162
- # @rbs (Array[packed_items] lookaheads, Hash[lookahead_node, Array[lookahead_node]] edges) -> void
194
+ # @rbs (Array[packed_items] lookaheads, Hash[Integer, Array[Integer]] edges) -> void
163
195
  def propagate(lookaheads, edges)
164
196
  queue = lookaheads.each_with_index.flat_map do |items, state_id|
165
197
  items.filter_map do |(production_id, dot), tokens|
166
- next if tokens.empty?
167
-
168
- [state_id, production_id, dot] #: lookahead_node
198
+ node_id(state_id, production_id, dot) unless tokens.empty?
169
199
  end
170
- end #: Array[lookahead_node]
171
- queued = queue.to_h { |node| [node, true] } #: Hash[lookahead_node, bool]
200
+ end
201
+ queued = queue.to_h { |node| [node, true] }
172
202
  cursor = 0
173
203
  while cursor < queue.length
174
204
  source = queue.fetch(cursor)
@@ -187,50 +217,73 @@ module Ibex
187
217
  end
188
218
  end
189
219
 
190
- # @rbs (Array[packed_items] lookaheads, lookahead_node node) -> Set[Integer]
220
+ # @rbs (Array[packed_items] lookaheads, Integer node) -> Set[Integer]
191
221
  def node_set(lookaheads, node)
192
- state_id, production_id, dot = node
222
+ state_id = node / @node_stride
223
+ item = node % @node_stride
224
+ production_id = ((item / @item_key_stride) - @production_offset).to_i
225
+ dot = (item % @item_key_stride).to_i
193
226
  lookaheads.fetch(state_id).fetch(item_core(production_id, dot))
194
227
  end
195
228
 
229
+ # @rbs (Integer state_id, Integer production_id, Integer dot) -> Integer
230
+ def node_id(state_id, production_id, dot)
231
+ ((state_id * @node_stride) + ((production_id + @production_offset) * @item_key_stride) + dot).to_i
232
+ end
233
+
196
234
  # @rbs (Integer bits) -> Array[Integer]
197
235
  def terminal_ids(bits)
198
- selected = [] #: Array[Integer]
199
- index = 0
200
- while index < @terminal_ids.length
201
- selected << @terminal_ids[index] if bits.anybits?(@terminal_masks[index])
202
- index += 1
236
+ @terminal_ids_by_bits.fetch(bits) do
237
+ selected = @terminal_ids.select { |id| bits.anybits?(@terminal_masks[@terminal_ids.index(id)]) }
238
+ @terminal_ids_by_bits[bits] = selected.freeze
203
239
  end
204
- selected
205
240
  end
206
241
 
207
242
  # @rbs (Integer production_id) -> Array[Integer]
208
243
  def rhs_for(production_id)
209
- return @augmented_rhs if production_id == AUGMENTED_PRODUCTION
244
+ return @augmented_rhs.fetch(production_id) if production_id.negative?
210
245
 
211
246
  @production_rhs.fetch(production_id)
212
247
  end
213
248
 
214
249
  # @rbs (Integer production_id, Integer rhs_length) -> Array[item_core]
215
250
  def item_cores_for(production_id, rhs_length)
216
- Array.new(rhs_length + 1) do |dot|
217
- [production_id, dot].freeze #: item_core
218
- end.freeze
251
+ result = Array.new(rhs_length + 1) #: Array[item_core]
252
+ rhs_length.next.times do |dot|
253
+ core = [production_id, dot].freeze #: item_core
254
+ result[dot] = core
255
+ end
256
+ result.freeze
219
257
  end
220
258
 
221
259
  # @rbs (Integer production_id, Integer dot) -> item_core
222
260
  def item_core(production_id, dot)
223
261
  raise IndexError, "invalid item dot: #{dot}" if dot.negative?
224
- return @augmented_item_cores.fetch(dot) if production_id == AUGMENTED_PRODUCTION
225
- raise IndexError, "invalid production id: #{production_id}" if production_id.negative?
262
+ return @augmented_item_cores.fetch(production_id).fetch(dot) if production_id.negative?
226
263
 
227
264
  @production_item_cores.fetch(production_id).fetch(dot)
228
265
  end
229
266
 
230
- # @rbs (core_set items) -> Array[item_core]
267
+ # @rbs (core_set items) -> Array[Integer]
231
268
  def item_key(items)
232
- items.to_a.sort
269
+ items.map do |production_id, dot|
270
+ (((production_id + @production_offset) * @item_key_stride) + dot).to_i
271
+ end.sort!
272
+ end
273
+
274
+ # @rbs (String name) -> Integer
275
+ def start_index(name)
276
+ @grammar_starts.index(name) || raise(Ibex::Error, "missing start symbol #{name}")
277
+ end
278
+
279
+ # @rbs () -> Array[String]
280
+ def grammar_starts
281
+ return @grammar.starts if @grammar.respond_to?(:starts)
282
+
283
+ [@grammar.start]
233
284
  end
234
285
  end
235
286
  end
236
287
  end
288
+
289
+ # rubocop:enable Metrics/AbcSize, Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ # steep:ignore:start
5
+
6
+ # Dependency construction mirrors the three formal relation definitions.
7
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
8
+
9
+ require_relative "lookahead_propagation"
10
+
11
+ module Ibex
12
+ module LALR
13
+ # Computes DeRemer–Pennello goto-follow sets over an LR(0) collection.
14
+ # The three dependency closures are kept separately because IELR relies on
15
+ # the distinction between stable (successor/internal) and predecessor
16
+ # dependencies when a state is split.
17
+ class GotoFollows
18
+ # @rbs @grammar: IR::Grammar
19
+ # @rbs @sets: Analysis::Sets
20
+ # @rbs @states: Array[core_set]
21
+ # @rbs @transitions: transitions
22
+ # @rbs @kernel_cores: Array[Array[item_core]]
23
+
24
+ attr_reader :from_state #: Array[Integer]
25
+ attr_reader :to_state #: Array[Integer]
26
+ attr_reader :goto_index #: Hash[[Integer, Integer], Integer]
27
+ attr_reader :direct_reads #: Array[Integer]
28
+ attr_reader :successor_edges #: Array[Array[Integer]]
29
+ attr_reader :internal_edges #: Array[Array[Integer]]
30
+ attr_reader :includes_edges #: Array[Array[Integer]]
31
+ attr_reader :predecessors #: Array[Array[Integer]]
32
+ attr_reader :successor_follows #: Array[Integer]
33
+ attr_reader :always_follows #: Array[Integer]
34
+ attr_reader :goto_follows #: Array[Integer]
35
+ attr_reader :follow_kernel_items #: Array[Integer]
36
+ attr_reader :kernel_cores #: Array[Array[item_core]]
37
+
38
+ # @rbs (IR::Grammar grammar, Analysis::Sets sets, Array[core_set] states, transitions transitions,
39
+ # Array[Integer] start_states, ?start_names: Array[String]?) -> void
40
+ def initialize(grammar, sets, states, transitions, start_states, start_names: nil)
41
+ @grammar = grammar
42
+ @sets = sets
43
+ @states = states
44
+ @transitions = transitions
45
+ @start_states = start_states
46
+ @start_names = start_names || grammar.starts
47
+ @kernel_cores = states.map { |items| kernel_items(items) }
48
+ build_gotos
49
+ build_dependencies
50
+ build_follow_sets
51
+ @reduction_lookaheads = nil
52
+ end
53
+
54
+ # @rbs (Integer state_id, Integer nonterminal_id) -> Integer?
55
+ def goto_for(state_id, nonterminal_id)
56
+ @goto_index[[state_id, nonterminal_id]]
57
+ end
58
+
59
+ # @rbs () -> Array[Hash[Integer, Integer]]
60
+ def reduction_lookaheads
61
+ return @reduction_lookaheads if @reduction_lookaheads
62
+
63
+ result = Array.new(@states.length) { {} }
64
+ seeds = @start_states.each_with_index.map do |state_id, index|
65
+ augmented = -1 - @grammar.starts.index(@start_names.fetch(index))
66
+ [state_id, [augmented, 0], eof_id]
67
+ end
68
+ propagated = LookaheadPropagation.new(
69
+ @grammar, @sets, @states, @transitions, seeds: seeds
70
+ ).build
71
+ @states.each_with_index do |items, state_id|
72
+ items.each do |production_id, dot|
73
+ next unless dot == rhs_for(production_id).length
74
+
75
+ values = propagated.fetch(state_id).fetch([production_id, dot], [])
76
+ result.fetch(state_id)[production_id] = values.reduce(0) do |bits, token|
77
+ bits | (1 << token)
78
+ end
79
+ end
80
+ end
81
+ @reduction_lookaheads = result
82
+ end
83
+
84
+ private
85
+
86
+ # @rbs () -> void
87
+ def build_gotos
88
+ @from_state = []
89
+ @to_state = []
90
+ @goto_symbols = []
91
+ @goto_index = {}
92
+ @transitions.each_with_index do |edges, state_id|
93
+ edges.keys.sort.each do |symbol_id|
94
+ symbol = @grammar.symbol_by_id(symbol_id)
95
+ next unless symbol&.nonterminal?
96
+
97
+ goto_id = @from_state.length
98
+ @from_state << state_id
99
+ @to_state << edges.fetch(symbol_id)
100
+ @goto_symbols << symbol_id
101
+ @goto_index[[state_id, symbol_id]] = goto_id
102
+ end
103
+ end
104
+ end
105
+
106
+ # @rbs () -> void
107
+ def build_dependencies
108
+ count = @from_state.length
109
+ @direct_reads = Array.new(count, 0)
110
+ @successor_edges = Array.new(count) { [] }
111
+ @internal_edges = Array.new(count) { [] }
112
+ @includes_edges = Array.new(count) { [] }
113
+ @predecessors = Array.new(@states.length) { [] }
114
+ @transitions.each_with_index do |edges, state_id|
115
+ edges.each_value { |target| @predecessors.fetch(target) << state_id }
116
+ end
117
+
118
+ count.times do |goto_id|
119
+ destination = @to_state.fetch(goto_id)
120
+ @transitions.fetch(destination).each_key do |symbol_id|
121
+ symbol = @grammar.symbol_by_id(symbol_id)
122
+ if symbol&.terminal?
123
+ @direct_reads[goto_id] |= 1 << symbol_id
124
+ elsif symbol&.nonterminal? && @sets.nullable?(symbol_id)
125
+ successor = @goto_index.fetch([destination, symbol_id])
126
+ @successor_edges.fetch(goto_id) << successor
127
+ end
128
+ end
129
+ end
130
+
131
+ @start_states.each_with_index do |state_id, index|
132
+ start_name = @start_names.fetch(index)
133
+ start_symbol = @grammar.symbol(start_name) || raise(Ibex::Error, "missing start symbol #{start_name}")
134
+ goto_id = @goto_index[[state_id, start_symbol.id]]
135
+ @direct_reads[goto_id] |= 1 << eof_id if goto_id
136
+ end
137
+
138
+ @goto_index.each do |(inner_source, inner_symbol), inner_goto_id|
139
+ @grammar.productions.each do |production|
140
+ production.rhs.each_with_index do |symbol_id, position|
141
+ next unless symbol_id == inner_symbol
142
+ next unless @grammar.symbol_by_id(symbol_id)&.nonterminal?
143
+
144
+ suffix = production.rhs[(position + 1)..] || []
145
+ next unless @sets.sequence_nullable?(suffix)
146
+
147
+ @goto_index.each do |(outer_source, outer_symbol), outer_goto_id|
148
+ next unless outer_symbol == production.lhs
149
+
150
+ reached = advance(outer_source, production.rhs.take(position))
151
+ next unless reached == inner_source
152
+
153
+ @includes_edges.fetch(inner_goto_id) << outer_goto_id
154
+ @internal_edges.fetch(inner_goto_id) << outer_goto_id if position.zero?
155
+ end
156
+ end
157
+ end
158
+ end
159
+ @successor_edges.each(&:uniq!)
160
+ @internal_edges.each(&:uniq!)
161
+ @includes_edges.each(&:uniq!)
162
+ @predecessors.each(&:uniq!)
163
+ end
164
+
165
+ # @rbs () -> void
166
+ def build_follow_sets
167
+ @successor_follows = Analysis::Digraph.closure(@direct_reads, @successor_edges)
168
+ internal_initial = @from_state.each_index.map do |goto_id|
169
+ 1 << symbol_for_goto(goto_id)
170
+ end
171
+ @internal_reachable_symbols = Analysis::Digraph.closure(internal_initial, @internal_edges)
172
+ @always_follows = Analysis::Digraph.closure(@direct_reads, merged_edges(@successor_edges, @internal_edges))
173
+ @goto_follows = Analysis::Digraph.closure(@always_follows, @includes_edges)
174
+ @follow_kernel_items = @from_state.each_index.map { |goto_id| compute_follow_kernel_items(goto_id) }
175
+ end
176
+
177
+ # @rbs (Array[Array[Integer]], Array[Array[Integer]]) -> Array[Array[Integer]]
178
+ def merged_edges(left, right)
179
+ left.each_index.map { |index| (left.fetch(index) + right.fetch(index)).uniq }
180
+ end
181
+
182
+ # @rbs (Integer goto_id) -> Integer
183
+ def compute_follow_kernel_items(goto_id)
184
+ state_id = @from_state.fetch(goto_id)
185
+ reachable = @internal_reachable_symbols.fetch(goto_id)
186
+ @kernel_cores.fetch(state_id).each_with_index.reduce(0) do |mask, ((production_id, dot), index)|
187
+ rhs = rhs_for(production_id)
188
+ symbol_id = rhs[dot]
189
+ next mask unless symbol_id && reachable.anybits?(1 << symbol_id)
190
+ next mask unless @sets.sequence_nullable?(rhs[(dot + 1)..] || [])
191
+
192
+ mask | (1 << index)
193
+ end
194
+ end
195
+
196
+ # @rbs (core_set items) -> Array[item_core]
197
+ def kernel_items(items)
198
+ items.select { |production_id, dot| production_id.negative? || dot.positive? }.to_a.sort
199
+ end
200
+
201
+ # @rbs (Integer goto_id) -> Integer
202
+ def symbol_for_goto(goto_id)
203
+ @goto_symbols.fetch(goto_id)
204
+ end
205
+
206
+ # @rbs (Integer state_id, Array[Integer]) -> Integer?
207
+ def advance(state_id, symbols)
208
+ symbols.reduce(state_id) do |current, symbol_id|
209
+ @transitions.fetch(current).fetch(symbol_id, nil) || break
210
+ end
211
+ end
212
+
213
+ # @rbs (Integer production_id) -> Array[Integer]
214
+ def rhs_for(production_id)
215
+ return [@grammar.symbol(@grammar.starts.fetch(-production_id - 1)).id] if production_id.negative?
216
+
217
+ @grammar.productions.fetch(production_id).rhs
218
+ end
219
+
220
+ # @rbs () -> Integer
221
+ def eof_id
222
+ 0
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
229
+ # steep:ignore:end