ibex 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -123
  3. data/docs/architecture.md +477 -31
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +106 -0
  12. data/docs/cst.md +237 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +306 -0
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +26 -0
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +114 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +629 -18
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +56 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +45 -6
  63. data/docs/release-readiness.md +221 -0
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +234 -0
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/examples/README.md +60 -0
  75. data/examples/calculator.y +44 -0
  76. data/examples/csv.y +32 -0
  77. data/examples/ini.y +70 -0
  78. data/examples/json.y +48 -0
  79. data/examples/tiny_language.y +75 -0
  80. data/lib/ibex/analysis/digraph.rb +128 -0
  81. data/lib/ibex/analysis/sets.rb +5 -4
  82. data/lib/ibex/analysis.rb +1 -0
  83. data/lib/ibex/artifact_set.rb +63 -0
  84. data/lib/ibex/bison_import/importer.rb +516 -0
  85. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  86. data/lib/ibex/bison_import.rb +200 -0
  87. data/lib/ibex/bounded_subprocess.rb +171 -0
  88. data/lib/ibex/cli/ambiguity.rb +75 -0
  89. data/lib/ibex/cli/analysis.rb +134 -0
  90. data/lib/ibex/cli/bison_import.rb +121 -0
  91. data/lib/ibex/cli/config.rb +128 -0
  92. data/lib/ibex/cli/counterexample_options.rb +6 -2
  93. data/lib/ibex/cli/coverage.rb +173 -0
  94. data/lib/ibex/cli/debug.rb +106 -0
  95. data/lib/ibex/cli/diagnostics.rb +136 -0
  96. data/lib/ibex/cli/documentation.rb +70 -0
  97. data/lib/ibex/cli/equiv.rb +169 -0
  98. data/lib/ibex/cli/error_messages.rb +184 -0
  99. data/lib/ibex/cli/explain.rb +85 -0
  100. data/lib/ibex/cli/fix.rb +203 -0
  101. data/lib/ibex/cli/formatting.rb +387 -0
  102. data/lib/ibex/cli/fuzz.rb +200 -0
  103. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  104. data/lib/ibex/cli/generation_artifacts.rb +189 -0
  105. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  106. data/lib/ibex/cli/grammar_tests.rb +140 -0
  107. data/lib/ibex/cli/ir_tools.rb +128 -0
  108. data/lib/ibex/cli/lsp.rb +33 -0
  109. data/lib/ibex/cli/outputs.rb +133 -23
  110. data/lib/ibex/cli/racc_migration.rb +87 -0
  111. data/lib/ibex/cli/reduce.rb +202 -0
  112. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  113. data/lib/ibex/cli/samples.rb +118 -0
  114. data/lib/ibex/cli/verify.rb +116 -0
  115. data/lib/ibex/cli/watch.rb +104 -0
  116. data/lib/ibex/cli.rb +825 -55
  117. data/lib/ibex/codegen/action_locations.rb +103 -0
  118. data/lib/ibex/codegen/action_method_source.rb +209 -0
  119. data/lib/ibex/codegen/action_source.rb +119 -0
  120. data/lib/ibex/codegen/ambiguity.rb +174 -0
  121. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  122. data/lib/ibex/codegen/documentation.rb +170 -0
  123. data/lib/ibex/codegen/explain.rb +380 -0
  124. data/lib/ibex/codegen/generated_action_abi.rb +293 -0
  125. data/lib/ibex/codegen/html.rb +94 -10
  126. data/lib/ibex/codegen/mermaid.rb +43 -0
  127. data/lib/ibex/codegen/railroad.rb +197 -0
  128. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  129. data/lib/ibex/codegen/rbs.rb +331 -2
  130. data/lib/ibex/codegen/report.rb +67 -15
  131. data/lib/ibex/codegen/ruby.rb +305 -66
  132. data/lib/ibex/codegen/ruby_actions.rb +146 -0
  133. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  134. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  135. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  136. data/lib/ibex/codegen/ruby_syntax.rb +96 -0
  137. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  138. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  139. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  140. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  141. data/lib/ibex/configuration/explanation.rb +421 -0
  142. data/lib/ibex/configuration/inspector.rb +232 -0
  143. data/lib/ibex/configuration.rb +580 -0
  144. data/lib/ibex/coverage/collector.rb +198 -0
  145. data/lib/ibex/coverage/event_stream.rb +103 -0
  146. data/lib/ibex/coverage/report.rb +267 -0
  147. data/lib/ibex/coverage/runtime_event_validator.rb +169 -0
  148. data/lib/ibex/coverage.rb +13 -0
  149. data/lib/ibex/delta_reducer.rb +99 -0
  150. data/lib/ibex/diff.rb +140 -0
  151. data/lib/ibex/equiv/machine.rb +135 -0
  152. data/lib/ibex/equiv.rb +373 -0
  153. data/lib/ibex/error_messages/parser.rb +159 -0
  154. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  155. data/lib/ibex/error_messages/renderer.rb +65 -0
  156. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  157. data/lib/ibex/error_messages/update.rb +169 -0
  158. data/lib/ibex/error_messages.rb +165 -0
  159. data/lib/ibex/fix.rb +557 -0
  160. data/lib/ibex/frontend/ast.rb +166 -7
  161. data/lib/ibex/frontend/bootstrap_parser.rb +40 -4
  162. data/lib/ibex/frontend/diagnostic.rb +81 -0
  163. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  164. data/lib/ibex/frontend/dsl.rb +66 -7
  165. data/lib/ibex/frontend/formatter.rb +453 -0
  166. data/lib/ibex/frontend/generated_parser.rb +644 -190
  167. data/lib/ibex/frontend/generated_parser_base.rb +170 -32
  168. data/lib/ibex/frontend/generated_parser_includes.rb +51 -0
  169. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  170. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  171. data/lib/ibex/frontend/generation.rb +33 -0
  172. data/lib/ibex/frontend/lexer.rb +162 -10
  173. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  174. data/lib/ibex/frontend/parser/declarations.rb +262 -11
  175. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  176. data/lib/ibex/frontend/parser/rules.rb +43 -6
  177. data/lib/ibex/frontend/parser.rb +182 -4
  178. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  179. data/lib/ibex/frontend/regenerator.rb +27 -1
  180. data/lib/ibex/frontend/resolution.rb +70 -0
  181. data/lib/ibex/frontend/resolver.rb +217 -0
  182. data/lib/ibex/frontend/rule_documentation.rb +104 -0
  183. data/lib/ibex/frontend/source_cursor.rb +133 -9
  184. data/lib/ibex/frontend/source_document.rb +229 -0
  185. data/lib/ibex/frontend/source_loader.rb +150 -0
  186. data/lib/ibex/frontend/source_span.rb +89 -0
  187. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  188. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  189. data/lib/ibex/frontend/token_adapter/declaration_state.rb +244 -22
  190. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  191. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  192. data/lib/ibex/frontend/token_adapter.rb +18 -3
  193. data/lib/ibex/frontend.rb +14 -2
  194. data/lib/ibex/fuzz.rb +219 -0
  195. data/lib/ibex/generation_input.rb +57 -0
  196. data/lib/ibex/generation_manifest.rb +211 -0
  197. data/lib/ibex/generation_transaction.rb +261 -0
  198. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  199. data/lib/ibex/generation_transaction_validation.rb +196 -0
  200. data/lib/ibex/grammar_tests.rb +206 -0
  201. data/lib/ibex/ir/automaton_ir.rb +86 -11
  202. data/lib/ibex/ir/grammar_ir.rb +170 -26
  203. data/lib/ibex/ir/lexer_ir.rb +76 -0
  204. data/lib/ibex/ir/parser_contract.rb +135 -0
  205. data/lib/ibex/ir/serialize.rb +268 -50
  206. data/lib/ibex/ir/validator/automaton.rb +375 -0
  207. data/lib/ibex/ir/validator/base.rb +147 -0
  208. data/lib/ibex/ir/validator/grammar.rb +634 -0
  209. data/lib/ibex/ir/validator/lexer.rb +113 -0
  210. data/lib/ibex/ir/validator.rb +63 -0
  211. data/lib/ibex/ir.rb +71 -5
  212. data/lib/ibex/lalr/build_metrics.rb +71 -0
  213. data/lib/ibex/lalr/builder.rb +514 -52
  214. data/lib/ibex/lalr/conflict.rb +1 -0
  215. data/lib/ibex/lalr/conflict_search.rb +26 -9
  216. data/lib/ibex/lalr/counterexample.rb +38 -18
  217. data/lib/ibex/lalr/direct_lookaheads.rb +289 -0
  218. data/lib/ibex/lalr/goto_follows.rb +229 -0
  219. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  220. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  221. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  222. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  223. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  224. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  225. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  226. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  227. data/lib/ibex/lalr/ielr_partition.rb +166 -0
  228. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  229. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  230. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  231. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  232. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  233. data/lib/ibex/lalr.rb +59 -1
  234. data/lib/ibex/location.rb +129 -0
  235. data/lib/ibex/lsp/document_handlers.rb +68 -0
  236. data/lib/ibex/lsp/document_store.rb +264 -0
  237. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  238. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  239. data/lib/ibex/lsp/initialization_handlers.rb +80 -0
  240. data/lib/ibex/lsp/navigation_handlers.rb +70 -0
  241. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  242. data/lib/ibex/lsp/position_codec.rb +113 -0
  243. data/lib/ibex/lsp/protocol_error.rb +33 -0
  244. data/lib/ibex/lsp/request_handlers.rb +46 -0
  245. data/lib/ibex/lsp/request_support.rb +94 -0
  246. data/lib/ibex/lsp/server.rb +148 -0
  247. data/lib/ibex/lsp/symbol_index.rb +241 -0
  248. data/lib/ibex/lsp/symbol_index_builder.rb +277 -0
  249. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  250. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  251. data/lib/ibex/lsp/symbol_occurrence.rb +23 -0
  252. data/lib/ibex/lsp/transport.rb +128 -0
  253. data/lib/ibex/lsp/workspace.rb +128 -0
  254. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  255. data/lib/ibex/lsp.rb +33 -0
  256. data/lib/ibex/messages/en.yml +28 -0
  257. data/lib/ibex/messages/ja.yml +28 -0
  258. data/lib/ibex/messages.rb +68 -0
  259. data/lib/ibex/metrics.rb +175 -0
  260. data/lib/ibex/normalize/declarations.rb +142 -7
  261. data/lib/ibex/normalize/diagnostics.rb +94 -24
  262. data/lib/ibex/normalize/expander.rb +64 -59
  263. data/lib/ibex/normalize/expression.rb +63 -40
  264. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  265. data/lib/ibex/normalize/inline_expansion.rb +465 -0
  266. data/lib/ibex/normalize/inline_validation.rb +174 -0
  267. data/lib/ibex/normalize/lexer.rb +131 -0
  268. data/lib/ibex/normalize/named_references.rb +60 -0
  269. data/lib/ibex/normalize/nodes.rb +46 -0
  270. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +70 -0
  271. data/lib/ibex/normalize/parameter_lowering.rb +143 -0
  272. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  273. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  274. data/lib/ibex/normalize/parameters.rb +226 -0
  275. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  276. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  277. data/lib/ibex/normalize.rb +172 -17
  278. data/lib/ibex/racc_migration/checker.rb +122 -0
  279. data/lib/ibex/racc_migration/harness.rb +177 -0
  280. data/lib/ibex/racc_migration/report.rb +97 -0
  281. data/lib/ibex/racc_migration.rb +12 -0
  282. data/lib/ibex/rake_task.rb +116 -0
  283. data/lib/ibex/samples.rb +216 -0
  284. data/lib/ibex/table_artifact/builder.rb +266 -0
  285. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  286. data/lib/ibex/table_artifact/document.rb +39 -0
  287. data/lib/ibex/table_artifact/executor.rb +187 -0
  288. data/lib/ibex/table_artifact/serializer.rb +57 -0
  289. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  290. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  291. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  292. data/lib/ibex/table_artifact/validator.rb +253 -0
  293. data/lib/ibex/table_artifact.rb +88 -0
  294. data/lib/ibex/table_simulation/result.rb +55 -0
  295. data/lib/ibex/table_simulation/simulator.rb +253 -0
  296. data/lib/ibex/table_simulation/step.rb +62 -0
  297. data/lib/ibex/table_simulation/text.rb +31 -0
  298. data/lib/ibex/table_simulation.rb +13 -0
  299. data/lib/ibex/tables.rb +22 -69
  300. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  301. data/lib/ibex/verification_report/builder.rb +159 -0
  302. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  303. data/lib/ibex/verification_report/logical_path.rb +68 -0
  304. data/lib/ibex/verification_report/validator.rb +329 -0
  305. data/lib/ibex/verification_report.rb +69 -0
  306. data/lib/ibex/verify/action_correspondence.rb +138 -0
  307. data/lib/ibex/verify/language_witness.rb +295 -0
  308. data/lib/ibex/verify/reference_collection.rb +190 -0
  309. data/lib/ibex/verify/result.rb +73 -0
  310. data/lib/ibex/verify/verifier.rb +598 -0
  311. data/lib/ibex/verify.rb +15 -0
  312. data/lib/ibex/version.rb +1 -1
  313. data/lib/ibex/watch/runner.rb +181 -0
  314. data/lib/ibex/watch/source_snapshot.rb +98 -0
  315. data/lib/ibex/watch.rb +11 -0
  316. data/lib/ibex.rb +35 -1
  317. data/schema/automaton-ir-definitions.schema.json +401 -0
  318. data/schema/automaton-ir.schema.json +65 -0
  319. data/schema/benchmark-v1.schema.json +212 -0
  320. data/schema/benchmark-v2.schema.json +61 -0
  321. data/schema/bison-import-v1.schema.json +106 -0
  322. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  323. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  324. data/schema/construction-profile-v1.schema.json +779 -0
  325. data/schema/cst-v1.json +170 -0
  326. data/schema/diff-v1.schema.json +41 -0
  327. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  328. data/schema/equiv-v1.schema.json +64 -0
  329. data/schema/error-ux-review-v1.schema.json +609 -0
  330. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  331. data/schema/error-ux-round2-v1.schema.json +519 -0
  332. data/schema/error-ux-v1.schema.json +258 -0
  333. data/schema/explain-v1.schema.json +495 -0
  334. data/schema/fix-v1.schema.json +64 -0
  335. data/schema/fix-v2.schema.json +66 -0
  336. data/schema/fix-v3.schema.json +101 -0
  337. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  338. data/schema/fuzz-regression-v1.schema.json +79 -0
  339. data/schema/fuzz-v1.schema.json +114 -0
  340. data/schema/generation-manifest-v1.schema.json +115 -0
  341. data/schema/grammar-ir-extensions.schema.json +734 -0
  342. data/schema/grammar-ir-foundation.schema.json +426 -0
  343. data/schema/grammar-ir.schema.json +271 -0
  344. data/schema/ielr-benchmark-v1.schema.json +85 -0
  345. data/schema/lexer-ir-v1.schema.json +215 -0
  346. data/schema/lexer-profile-v1.schema.json +491 -0
  347. data/schema/metrics-v1.schema.json +60 -0
  348. data/schema/migration-check-v1.schema.json +60 -0
  349. data/schema/performance-comparison-v1.schema.json +395 -0
  350. data/schema/public-performance-comparison-v1.schema.json +506 -0
  351. data/schema/public-performance-profile-v1.schema.json +360 -0
  352. data/schema/reduce-v1.schema.json +22 -0
  353. data/schema/reduce-v2.schema.json +63 -0
  354. data/schema/runtime-coverage-v1.schema.json +86 -0
  355. data/schema/runtime-event-v1.schema.json +308 -0
  356. data/schema/table-artifact-v1.schema.json +377 -0
  357. data/schema/table-simulation-v1.schema.json +70 -0
  358. data/schema/verification-report-v1.schema.json +222 -0
  359. data/schema/verify-v1.schema.json +42 -0
  360. data/sig/ibex/analysis/digraph.rbs +19 -0
  361. data/sig/ibex/artifact_set.rbs +37 -0
  362. data/sig/ibex/bison_import/importer.rbs +140 -0
  363. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  364. data/sig/ibex/bison_import.rbs +95 -0
  365. data/sig/ibex/bounded_subprocess.rbs +76 -0
  366. data/sig/ibex/cli/ambiguity.rbs +28 -0
  367. data/sig/ibex/cli/analysis.rbs +43 -0
  368. data/sig/ibex/cli/bison_import.rbs +32 -0
  369. data/sig/ibex/cli/config.rbs +35 -0
  370. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  371. data/sig/ibex/cli/coverage.rbs +53 -0
  372. data/sig/ibex/cli/debug.rbs +28 -0
  373. data/sig/ibex/cli/diagnostics.rbs +51 -0
  374. data/sig/ibex/cli/documentation.rbs +29 -0
  375. data/sig/ibex/cli/equiv.rbs +49 -0
  376. data/sig/ibex/cli/error_messages.rbs +63 -0
  377. data/sig/ibex/cli/explain.rbs +31 -0
  378. data/sig/ibex/cli/fix.rbs +53 -0
  379. data/sig/ibex/cli/formatting.rbs +107 -0
  380. data/sig/ibex/cli/fuzz.rbs +47 -0
  381. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  382. data/sig/ibex/cli/generation_artifacts.rbs +69 -0
  383. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  384. data/sig/ibex/cli/grammar_tests.rbs +48 -0
  385. data/sig/ibex/cli/ir_tools.rbs +39 -0
  386. data/sig/ibex/cli/lsp.rbs +14 -0
  387. data/sig/ibex/cli/outputs.rbs +26 -3
  388. data/sig/ibex/cli/racc_migration.rbs +30 -0
  389. data/sig/ibex/cli/reduce.rbs +60 -0
  390. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  391. data/sig/ibex/cli/samples.rbs +38 -0
  392. data/sig/ibex/cli/verify.rbs +28 -0
  393. data/sig/ibex/cli/watch.rbs +43 -0
  394. data/sig/ibex/cli.rbs +182 -9
  395. data/sig/ibex/codegen/action_locations.rbs +45 -0
  396. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  397. data/sig/ibex/codegen/action_source.rbs +50 -0
  398. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  399. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  400. data/sig/ibex/codegen/documentation.rbs +50 -0
  401. data/sig/ibex/codegen/explain.rbs +105 -0
  402. data/sig/ibex/codegen/generated_action_abi.rbs +105 -0
  403. data/sig/ibex/codegen/html.rbs +18 -2
  404. data/sig/ibex/codegen/mermaid.rbs +16 -0
  405. data/sig/ibex/codegen/railroad.rbs +82 -0
  406. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  407. data/sig/ibex/codegen/rbs.rbs +86 -4
  408. data/sig/ibex/codegen/report.rbs +16 -8
  409. data/sig/ibex/codegen/ruby.rbs +105 -28
  410. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  411. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  412. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  413. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  414. data/sig/ibex/codegen/ruby_syntax.rbs +27 -0
  415. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  416. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  417. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  418. data/sig/ibex/configuration/explanation.rbs +195 -0
  419. data/sig/ibex/configuration/inspector.rbs +53 -0
  420. data/sig/ibex/configuration.rbs +242 -0
  421. data/sig/ibex/coverage/collector.rbs +78 -0
  422. data/sig/ibex/coverage/event_stream.rbs +44 -0
  423. data/sig/ibex/coverage/report.rbs +102 -0
  424. data/sig/ibex/coverage/runtime_event_validator.rbs +70 -0
  425. data/sig/ibex/coverage.rbs +7 -0
  426. data/sig/ibex/delta_reducer.rbs +38 -0
  427. data/sig/ibex/diff.rbs +47 -0
  428. data/sig/ibex/equiv/machine.rbs +54 -0
  429. data/sig/ibex/equiv.rbs +129 -0
  430. data/sig/ibex/error_messages/parser.rbs +58 -0
  431. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  432. data/sig/ibex/error_messages/renderer.rbs +23 -0
  433. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  434. data/sig/ibex/error_messages/update.rbs +43 -0
  435. data/sig/ibex/error_messages.rbs +85 -0
  436. data/sig/ibex/fix.rbs +138 -0
  437. data/sig/ibex/frontend/ast.rbs +237 -22
  438. data/sig/ibex/frontend/bootstrap_parser.rbs +13 -0
  439. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  440. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  441. data/sig/ibex/frontend/dsl.rbs +33 -4
  442. data/sig/ibex/frontend/formatter.rbs +143 -0
  443. data/sig/ibex/frontend/generated_parser.rbs +228 -68
  444. data/sig/ibex/frontend/generated_parser_base.rbs +65 -12
  445. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  446. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  447. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  448. data/sig/ibex/frontend/generation.rbs +6 -0
  449. data/sig/ibex/frontend/lexer.rbs +49 -2
  450. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  451. data/sig/ibex/frontend/parser/declarations.rbs +57 -0
  452. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  453. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  454. data/sig/ibex/frontend/parser.rbs +66 -0
  455. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  456. data/sig/ibex/frontend/regenerator.rbs +11 -0
  457. data/sig/ibex/frontend/resolution.rbs +34 -0
  458. data/sig/ibex/frontend/resolver.rbs +91 -0
  459. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  460. data/sig/ibex/frontend/source_cursor.rbs +38 -5
  461. data/sig/ibex/frontend/source_document.rbs +119 -0
  462. data/sig/ibex/frontend/source_loader.rbs +66 -0
  463. data/sig/ibex/frontend/source_span.rbs +56 -0
  464. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  465. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  466. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +83 -3
  467. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  468. data/sig/ibex/frontend/token_adapter.rbs +11 -2
  469. data/sig/ibex/frontend.rbs +1 -1
  470. data/sig/ibex/fuzz.rbs +71 -0
  471. data/sig/ibex/generation_input.rbs +37 -0
  472. data/sig/ibex/generation_manifest.rbs +70 -0
  473. data/sig/ibex/generation_transaction.rbs +82 -0
  474. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  475. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  476. data/sig/ibex/grammar_tests.rbs +93 -0
  477. data/sig/ibex/ir/automaton_ir.rbs +35 -10
  478. data/sig/ibex/ir/grammar_ir.rbs +96 -22
  479. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  480. data/sig/ibex/ir/parser_contract.rbs +69 -0
  481. data/sig/ibex/ir/serialize.rbs +75 -23
  482. data/sig/ibex/ir/validator/automaton.rbs +112 -0
  483. data/sig/ibex/ir/validator/base.rbs +69 -0
  484. data/sig/ibex/ir/validator/grammar.rbs +195 -0
  485. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  486. data/sig/ibex/ir/validator.rbs +16 -0
  487. data/sig/ibex/ir.rbs +45 -5
  488. data/sig/ibex/lalr/build_metrics.rbs +60 -0
  489. data/sig/ibex/lalr/builder.rbs +150 -19
  490. data/sig/ibex/lalr/conflict_search.rbs +12 -5
  491. data/sig/ibex/lalr/counterexample.rbs +20 -14
  492. data/sig/ibex/lalr/direct_lookaheads.rbs +93 -0
  493. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  494. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  495. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  496. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  497. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  498. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  499. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  500. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  501. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  502. data/sig/ibex/lalr/ielr_partition.rbs +71 -0
  503. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  504. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  505. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  506. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  507. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  508. data/sig/ibex/lalr.rbs +17 -1
  509. data/sig/ibex/location.rbs +67 -0
  510. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  511. data/sig/ibex/lsp/document_store.rbs +94 -0
  512. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  513. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  514. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  515. data/sig/ibex/lsp/navigation_handlers.rbs +33 -0
  516. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  517. data/sig/ibex/lsp/position_codec.rbs +44 -0
  518. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  519. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  520. data/sig/ibex/lsp/request_support.rbs +40 -0
  521. data/sig/ibex/lsp/server.rbs +55 -0
  522. data/sig/ibex/lsp/symbol_index.rbs +77 -0
  523. data/sig/ibex/lsp/symbol_index_builder.rbs +88 -0
  524. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  525. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  526. data/sig/ibex/lsp/symbol_occurrence.rbs +31 -0
  527. data/sig/ibex/lsp/transport.rbs +47 -0
  528. data/sig/ibex/lsp/workspace.rbs +42 -0
  529. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  530. data/sig/ibex/lsp.rbs +7 -0
  531. data/sig/ibex/messages.rbs +26 -0
  532. data/sig/ibex/metrics.rbs +50 -0
  533. data/sig/ibex/normalize/declarations.rbs +31 -0
  534. data/sig/ibex/normalize/diagnostics.rbs +13 -4
  535. data/sig/ibex/normalize/expander.rbs +22 -16
  536. data/sig/ibex/normalize/expression.rbs +11 -9
  537. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  538. data/sig/ibex/normalize/inline_expansion.rbs +129 -0
  539. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  540. data/sig/ibex/normalize/lexer.rbs +37 -0
  541. data/sig/ibex/normalize/named_references.rbs +20 -0
  542. data/sig/ibex/normalize/nodes.rbs +14 -0
  543. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +33 -0
  544. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  545. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  546. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  547. data/sig/ibex/normalize/parameters.rbs +51 -0
  548. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  549. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  550. data/sig/ibex/normalize.rbs +129 -18
  551. data/sig/ibex/racc_migration/checker.rbs +36 -0
  552. data/sig/ibex/racc_migration/harness.rbs +16 -0
  553. data/sig/ibex/racc_migration/report.rbs +55 -0
  554. data/sig/ibex/racc_migration.rbs +8 -0
  555. data/sig/ibex/rake_task.rbs +51 -0
  556. data/sig/ibex/samples.rbs +60 -0
  557. data/sig/ibex/table_artifact/builder.rbs +93 -0
  558. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  559. data/sig/ibex/table_artifact/document.rbs +27 -0
  560. data/sig/ibex/table_artifact/executor.rbs +66 -0
  561. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  562. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  563. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  564. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  565. data/sig/ibex/table_artifact/validator.rbs +66 -0
  566. data/sig/ibex/table_artifact.rbs +32 -0
  567. data/sig/ibex/table_simulation/result.rbs +36 -0
  568. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  569. data/sig/ibex/table_simulation/step.rbs +42 -0
  570. data/sig/ibex/table_simulation/text.rbs +14 -0
  571. data/sig/ibex/table_simulation.rbs +7 -0
  572. data/sig/ibex/tables.rbs +10 -35
  573. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  574. data/sig/ibex/verification_report/builder.rbs +53 -0
  575. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  576. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  577. data/sig/ibex/verification_report/validator.rbs +114 -0
  578. data/sig/ibex/verification_report.rbs +44 -0
  579. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  580. data/sig/ibex/verify/language_witness.rbs +112 -0
  581. data/sig/ibex/verify/reference_collection.rbs +63 -0
  582. data/sig/ibex/verify/result.rbs +52 -0
  583. data/sig/ibex/verify/verifier.rbs +170 -0
  584. data/sig/ibex/verify.rbs +6 -0
  585. data/sig/ibex/watch/runner.rbs +58 -0
  586. data/sig/ibex/watch/source_snapshot.rbs +46 -0
  587. data/sig/ibex/watch.rbs +7 -0
  588. data/sig/ibex.rbs +2 -0
  589. metadata +524 -19
  590. data/.rubocop.yml +0 -43
  591. data/CHANGELOG.md +0 -30
  592. data/Rakefile +0 -25
  593. data/Steepfile +0 -10
  594. data/docs/compat-notes.md +0 -37
  595. data/docs/lexer-coverage.md +0 -14
  596. data/docs/phase10-extensions.md +0 -27
  597. data/gemfiles/Gemfile +0 -7
  598. data/gemfiles/Gemfile.lock +0 -98
  599. data/lib/ibex/frontend/grammar.y +0 -156
  600. data/lib/ibex/runtime/parser.rb +0 -360
  601. data/lib/ibex/runtime.rb +0 -8
  602. data/sig/ibex/runtime/parser.rbs +0 -167
  603. data/sig/ibex/runtime.rbs +0 -6
@@ -1,40 +1,112 @@
1
1
  # frozen_string_literal: true
2
+ # rbs_inline: enabled
2
3
 
3
4
  require "set"
4
5
 
5
6
  module Ibex
6
7
  module LALR
7
- # Builds canonical LR(1) states and merges states with equal LR(0) cores.
8
+ # Builds deterministic SLR, direct LALR(1), or canonical LR(1) automata.
9
+ # rubocop:disable Metrics/ClassLength -- collection strategies share one action/conflict construction path.
8
10
  class Builder
9
11
  AUGMENTED_PRODUCTION = -1 #: Integer
10
12
 
11
- ALGORITHMS = %i[slr lalr lr1].freeze #: Array[Symbol]
13
+ ALGORITHMS = %i[slr lalr ielr lr1].freeze #: Array[Symbol]
14
+ LALR_STRATEGIES = %i[direct canonical_merge].freeze #: Array[Symbol]
15
+ IELR_STRATEGIES = %i[direct partition].freeze #: Array[Symbol]
12
16
 
13
17
  # @rbs @grammar: IR::Grammar
14
18
  # @rbs @algorithm: Symbol
19
+ # @rbs @lalr_strategy: Symbol
20
+ # @rbs @ielr_strategy: Symbol
15
21
  # @rbs @sets: Analysis::Sets
16
22
  # @rbs @productions_by_lhs: Hash[Integer, Array[IR::Production]]
17
23
  # @rbs @resolver: ConflictResolver
18
-
19
- # @rbs (IR::Grammar grammar, ?algorithm: Symbol | String) -> void
20
- def initialize(grammar, algorithm: :lalr)
24
+ # @rbs @metrics: BuildMetrics?
25
+ # @rbs @start_names: Array[String]
26
+ # @rbs @entry_isolation: bool
27
+ # @rbs @attribute_entries: bool
28
+ # @rbs @profile: bool
29
+ # @rbs @remove_unreachable: bool
30
+ # @rbs @canonical_suffix_lookahead_cache: Hash[Integer, Hash[Integer, Hash[Integer, Array[Integer]]]]
31
+ # @rbs @canonical_item_cache: Hash[Integer, Array[Array[lr_item?]?]]?
32
+ # @rbs @canonical_key_radices: [Integer, Integer, Integer]?
33
+
34
+ attr_reader :metrics #: BuildMetrics?
35
+
36
+ # @rbs (IR::Grammar grammar, ?algorithm: Symbol | String, ?lalr_strategy: Symbol | String,
37
+ # ?ielr_strategy: Symbol | String,
38
+ # ?entry_isolation: bool, ?starts: Array[String]?, ?attribute_entries: bool, ?profile: bool,
39
+ # ?remove_unreachable: bool) -> void
40
+ def initialize(grammar, algorithm: :lalr, lalr_strategy: :direct, entry_isolation: false,
41
+ ielr_strategy: :partition, starts: nil, attribute_entries: true, profile: false,
42
+ remove_unreachable: false)
21
43
  unless ALGORITHMS.include?(algorithm.to_sym)
22
44
  raise ArgumentError, "unknown parser algorithm #{algorithm.inspect}"
23
45
  end
46
+ unless LALR_STRATEGIES.include?(lalr_strategy.to_sym)
47
+ raise ArgumentError, "unknown LALR construction strategy #{lalr_strategy.inspect}"
48
+ end
49
+ unless IELR_STRATEGIES.include?(ielr_strategy.to_sym)
50
+ raise ArgumentError, "unknown IELR construction strategy #{ielr_strategy.inspect}"
51
+ end
24
52
 
25
53
  @grammar = grammar
26
54
  @algorithm = algorithm.to_sym
55
+ @lalr_strategy = lalr_strategy.to_sym
56
+ @ielr_strategy = ielr_strategy.to_sym
27
57
  @sets = Analysis::Sets.new(grammar)
28
58
  @productions_by_lhs = grammar.productions.group_by(&:lhs)
29
59
  @resolver = ConflictResolver.new(grammar)
60
+ @metrics = nil
61
+ @canonical_suffix_lookahead_cache = {}
62
+ @canonical_item_cache = nil
63
+ @canonical_key_radices = nil
64
+ @start_names = starts || grammar.starts
65
+ if @start_names.empty? || (@start_names - grammar.starts).any?
66
+ raise ArgumentError, "starts must be a nonempty subset of grammar starts"
67
+ end
68
+
69
+ @entry_isolation = entry_isolation
70
+ @attribute_entries = attribute_entries
71
+ @profile = profile
72
+ @remove_unreachable = remove_unreachable
30
73
  end
31
74
 
32
75
  # @rbs () -> IR::Automaton
33
76
  def build
34
- canonical_states, canonical_transitions = canonical_collection
35
- merged_items, merged_transitions = automaton_items(canonical_states, canonical_transitions)
77
+ return build_isolated_automaton if @entry_isolation && @start_names.length > 1
78
+
79
+ merged_items, merged_transitions, collection = automaton_collection
36
80
  states = build_states(merged_items, merged_transitions)
81
+ states = OnErrorReductions.apply(@grammar, states)
37
82
  states = DefaultReductions.apply(states, terminal_ids: @grammar.terminals.map(&:id))
83
+ entry_states = entry_states_for(merged_items)
84
+ if @remove_unreachable
85
+ states, mapping = UnreachableStates.remove(states, entry_states.values.uniq)
86
+ entry_states = entry_states.transform_values { |state_id| mapping.fetch(state_id) }
87
+ collection[:ielr_unreachable_removed] = mapping.length - states.length if collection.respond_to?(:[]=)
88
+ end
89
+ states = attribute_entry_conflicts(states, entry_states) if @attribute_entries && @start_names.length > 1
90
+ summary = conflict_summary(states)
91
+ final_items, final_lookahead_items = profiled_final_item_counts(merged_items)
92
+ @metrics = build_metrics(collection, states.length, final_items, final_lookahead_items)
93
+ build_output_automaton(states: states, conflict_summary: summary, entry_states: entry_states)
94
+ end
95
+
96
+ private
97
+
98
+ # @rbs (states: Array[IR::AutomatonState], conflict_summary: IR::conflict_summary,
99
+ # entry_states: Hash[String, Integer]) -> IR::Automaton
100
+ def build_output_automaton(states:, conflict_summary:, entry_states:)
101
+ IR::Automaton.new(
102
+ grammar: @grammar, states: states, conflict_summary: conflict_summary,
103
+ algorithm: algorithm_name, entry_states: entry_states,
104
+ entry_construction: output_entry_construction || raise(Ibex::Error, "missing entry construction")
105
+ )
106
+ end
107
+
108
+ # @rbs (Array[IR::AutomatonState] states) -> IR::conflict_summary
109
+ def conflict_summary(states)
38
110
  conflicts = states.flat_map(&:conflicts)
39
111
  shift_reduce = conflicts.select { |item| item[:type] == :shift_reduce }
40
112
  counted_shift_reduce = shift_reduce.count { |item| item.dig(:resolution, :by) == :default_shift }
@@ -43,23 +115,142 @@ module Ibex
43
115
  rr: conflicts.count { |item| item[:type] == :reduce_reduce },
44
116
  expected_sr: @grammar.expect,
45
117
  expectation_met: counted_shift_reduce == @grammar.expect } #: IR::conflict_summary
46
- IR::Automaton.new(grammar: @grammar, states: states, conflict_summary: summary,
47
- algorithm: @algorithm == :lalr ? "lalr1" : @algorithm.to_s)
118
+ expected_rr = @grammar.expect_rr
119
+ if expected_rr
120
+ summary[:expected_rr] = expected_rr
121
+ summary[:rr_expectation_met] = summary[:rr] == expected_rr
122
+ end
123
+ summary
48
124
  end
49
125
 
50
- private
126
+ # @rbs () -> [Array[packed_items], transitions, build_collection]
127
+ def automaton_collection
128
+ return ielr_collection if @algorithm == :ielr
129
+ return canonical_lr1_collection if @algorithm == :lr1
130
+ return merged_canonical_collection(:canonical_merge) if @lalr_strategy == :canonical_merge
131
+ return merged_canonical_collection(:canonical_merge_multi_entry) if @grammar.starts.length > 1
132
+
133
+ direct_collection
134
+ ensure
135
+ @canonical_item_cache = nil
136
+ end
137
+
138
+ # @rbs () -> [Array[packed_items], transitions, build_collection]
139
+ def ielr_collection
140
+ return ielr_direct_collection if @ielr_strategy == :direct
141
+
142
+ states, transitions = canonical_collection
143
+ partition = IELRPartition.new(@grammar, states, transitions, profile: @profile)
144
+ items, merged_transitions = partition.build
145
+ profile = canonical_profile(states, strategy: :ielr_partition)
146
+ profile[:ielr_initial_partitions] = partition.initial_partition_count
147
+ profile[:ielr_final_partitions] = partition.final_partition_count
148
+ [items, merged_transitions, profile]
149
+ end
150
+
151
+ # @rbs () -> [Array[packed_items], transitions, build_collection]
152
+ def ielr_direct_collection
153
+ pipeline = IELR::Pipeline.new(@grammar, @sets, starts: @start_names, profile: @profile)
154
+ pipeline.build
155
+ end
156
+
157
+ # @rbs () -> [Array[packed_items], transitions, build_collection]
158
+ def canonical_lr1_collection
159
+ states, transitions = canonical_collection
160
+ [pack_canonical_items(states), transitions, canonical_profile(states, strategy: :canonical_lr1)]
161
+ end
162
+
163
+ # @rbs (Symbol strategy) -> [Array[packed_items], transitions, build_collection]
164
+ def merged_canonical_collection(strategy)
165
+ states, transitions = canonical_collection
166
+ items, merged_transitions = merge_lalr(states, transitions)
167
+ apply_slr_lookaheads(items) if @algorithm == :slr
168
+ [items, merged_transitions, canonical_profile(states, strategy: strategy)]
169
+ end
170
+
171
+ # @rbs () -> untyped
172
+ def direct_collection
173
+ direct = DirectLookaheads.new(@grammar, @sets, profile: @profile)
174
+ items, transitions = direct.build
175
+ apply_slr_lookaheads(items) if @algorithm == :slr
176
+ profile = {
177
+ construction_states: items.length, canonical_states: nil, strategy: :direct_lalr,
178
+ lr0_states: direct.lr0_state_count, lr0_items: direct.lr0_item_count, canonical_items: nil,
179
+ propagation_edges: direct.propagation_edge_count,
180
+ ielr_initial_partitions: nil, ielr_final_partitions: nil,
181
+ ielr_annotations: nil, ielr_annotated_states: nil, ielr_inadequacies: nil,
182
+ ielr_split_stable_discarded: nil, ielr_lalr_states: nil, ielr_split_states: nil,
183
+ ielr_unreachable_removed: nil, ielr_remergeable_candidates: nil
184
+ }
185
+ [items, transitions, profile]
186
+ end
187
+
188
+ # @rbs (Array[item_set] states, strategy: Symbol) -> build_collection
189
+ def canonical_profile(states, strategy:)
190
+ cores = states.to_h { |items| [core_key(items), true] }.keys if @profile
191
+ {
192
+ construction_states: states.length, canonical_states: states.length, strategy: strategy,
193
+ lr0_states: cores&.length, lr0_items: cores&.sum(&:length),
194
+ canonical_items: @profile ? states.sum(&:length) : nil,
195
+ propagation_edges: nil, ielr_initial_partitions: nil, ielr_final_partitions: nil,
196
+ ielr_annotations: nil, ielr_annotated_states: nil, ielr_inadequacies: nil,
197
+ ielr_split_stable_discarded: nil, ielr_lalr_states: nil, ielr_split_states: nil,
198
+ ielr_unreachable_removed: nil, ielr_remergeable_candidates: nil
199
+ }
200
+ end
201
+
202
+ # @rbs (Array[packed_items] items) -> [Integer?, Integer?]
203
+ def profiled_final_item_counts(items)
204
+ return [nil, nil] unless @profile
205
+
206
+ [items.sum(&:length), items.sum { |state| state.values.sum(&:length) }]
207
+ end
208
+
209
+ # @rbs (build_collection collection, Integer final_states, Integer? final_items,
210
+ # Integer? final_lookahead_items) -> BuildMetrics
211
+ def build_metrics(collection, final_states, final_items, final_lookahead_items)
212
+ BuildMetrics.new(
213
+ construction_states: collection.fetch(:construction_states),
214
+ canonical_states: collection.fetch(:canonical_states),
215
+ strategy: collection.fetch(:strategy),
216
+ lr0_states: collection.fetch(:lr0_states),
217
+ lr0_items: collection.fetch(:lr0_items),
218
+ canonical_items: collection.fetch(:canonical_items),
219
+ propagation_edges: collection.fetch(:propagation_edges),
220
+ ielr_initial_partitions: collection.fetch(:ielr_initial_partitions),
221
+ ielr_final_partitions: collection.fetch(:ielr_final_partitions),
222
+ ielr_annotations: collection.fetch(:ielr_annotations, nil),
223
+ ielr_annotated_states: collection.fetch(:ielr_annotated_states, nil),
224
+ ielr_inadequacies: collection.fetch(:ielr_inadequacies, nil),
225
+ ielr_split_stable_discarded: collection.fetch(:ielr_split_stable_discarded, nil),
226
+ ielr_lalr_states: collection.fetch(:ielr_lalr_states, nil),
227
+ ielr_split_states: collection.fetch(:ielr_split_states, nil),
228
+ ielr_unreachable_removed: collection.fetch(:ielr_unreachable_removed, nil),
229
+ ielr_remergeable_candidates: collection.fetch(:ielr_remergeable_candidates, nil),
230
+ final_states: final_states, final_items: final_items, final_lookahead_items: final_lookahead_items
231
+ )
232
+ end
233
+
234
+ # @rbs () -> String
235
+ def algorithm_name
236
+ { lalr: "lalr1", ielr: "ielr1" }.fetch(@algorithm, @algorithm.to_s)
237
+ end
51
238
 
52
239
  # @rbs () -> [Array[item_set], transitions]
53
240
  def canonical_collection
54
- seed = Set[[AUGMENTED_PRODUCTION, 0, 0]] #: item_set
55
- states = [closure(seed)]
241
+ states = @start_names.map do |name|
242
+ seed = Set[canonical_item(augmented_production(name), 0, 0)] #: item_set
243
+ closure(seed)
244
+ end
56
245
  transitions = [] #: transitions
57
- indexes = { item_key(states.first) => 0 }
246
+ indexes = {} #: Hash[Array[Integer], Integer]
247
+ states.each_with_index { |items, index| indexes[item_key(items)] = index }
58
248
  cursor = 0
59
249
  while cursor < states.length
60
250
  transitions[cursor] = {}
61
- next_symbols(states[cursor]).each do |symbol_id|
62
- target = go_to(states[cursor], symbol_id)
251
+ kernels = shifted_kernels(states[cursor])
252
+ kernels.keys.sort.each do |symbol_id|
253
+ target = closure(kernels.fetch(symbol_id))
63
254
  key = item_key(target)
64
255
  target_id = indexes[key] ||= begin
65
256
  states << target
@@ -74,18 +265,27 @@ module Ibex
74
265
 
75
266
  # @rbs (item_set seed) -> item_set
76
267
  def closure(seed)
268
+ cache = (@canonical_item_cache ||= {}) #: Hash[Integer, Array[Array[lr_item?]?]]
77
269
  items = seed.dup
78
270
  queue = seed.to_a
79
- until queue.empty?
80
- production_id, dot, lookahead = queue.shift
271
+ cursor = 0
272
+ while cursor < queue.length
273
+ production_id, dot, lookahead = queue.fetch(cursor)
274
+ cursor += 1
81
275
  rhs = rhs_for(production_id)
82
276
  grammar_symbol = @grammar.symbol_by_id(rhs[dot])
83
277
  next unless grammar_symbol&.nonterminal?
84
278
 
85
- lookaheads = suffix_lookaheads(rhs.drop(dot + 1), lookahead)
279
+ lookaheads = canonical_suffix_lookaheads(production_id, dot, lookahead)
86
280
  @productions_by_lhs.fetch(grammar_symbol.id, Array.new(0)).each do |production|
281
+ production_cache = (cache[production.id] ||= []) #: Array[Array[lr_item?]?]
282
+ item_cache = (production_cache[0] ||= []) #: Array[lr_item?]
87
283
  lookaheads.each do |token_id|
88
- item = [production.id, 0, token_id] #: lr_item
284
+ item = item_cache[token_id]
285
+ unless item
286
+ item = [production.id, 0, token_id].freeze #: lr_item
287
+ item_cache[token_id] = item
288
+ end
89
289
  enqueue_item(items, queue, item)
90
290
  end
91
291
  end
@@ -100,32 +300,57 @@ module Ibex
100
300
  @grammar.terminals.filter_map { |terminal| terminal.id if bits.anybits?(1 << terminal.id) }
101
301
  end
102
302
 
103
- # @rbs (item_set items, Array[lr_item] queue, lr_item item) -> void
104
- def enqueue_item(items, queue, item)
105
- return if items.include?(item)
303
+ # @rbs (Integer production_id, Integer dot, Integer inherited) -> Array[Integer]
304
+ def canonical_suffix_lookaheads(production_id, dot, inherited)
305
+ production_cache = (@canonical_suffix_lookahead_cache[production_id] ||= {})
306
+ inherited_cache = (production_cache[dot] ||= {})
307
+ return inherited_cache.fetch(inherited) if inherited_cache.key?(inherited)
106
308
 
107
- items << item
108
- queue << item
309
+ suffix = rhs_for(production_id).drop(dot + 1)
310
+ inherited_cache[inherited] = suffix_lookaheads(suffix, inherited).freeze
109
311
  end
110
312
 
111
- # @rbs (item_set items) -> Array[Integer]
112
- def next_symbols(items)
113
- items.filter_map { |production_id, dot, _lookahead| rhs_for(production_id)[dot] }.uniq.sort
313
+ # @rbs (Integer production_id, Integer dot, Integer lookahead) -> lr_item
314
+ def canonical_item(production_id, dot, lookahead)
315
+ cache = (@canonical_item_cache ||= {}) #: Hash[Integer, Array[Array[lr_item?]?]]
316
+ production_cache = (cache[production_id] ||= []) #: Array[Array[lr_item?]?]
317
+ dot_cache = (production_cache[dot] ||= []) #: Array[lr_item?]
318
+ cached = dot_cache[lookahead]
319
+ return cached if cached
320
+
321
+ item = [production_id, dot, lookahead].freeze #: lr_item
322
+ dot_cache[lookahead] = item
114
323
  end
115
324
 
116
- # @rbs (item_set items, Integer symbol_id) -> item_set
117
- def go_to(items, symbol_id)
118
- moved = items.filter_map do |production_id, dot, lookahead|
119
- next unless rhs_for(production_id)[dot] == symbol_id
325
+ # @rbs (item_set items, Array[lr_item] queue, lr_item item) -> void
326
+ def enqueue_item(items, queue, item)
327
+ queue << item if items.add?(item)
328
+ end
120
329
 
121
- [production_id, dot + 1, lookahead] #: lr_item
330
+ # @rbs (item_set items) -> Hash[Integer, item_set]
331
+ def shifted_kernels(items)
332
+ cache = (@canonical_item_cache ||= {}) #: Hash[Integer, Array[Array[lr_item?]?]]
333
+ kernels = {} #: Hash[Integer, item_set]
334
+ items.each do |production_id, dot, lookahead|
335
+ symbol_id = rhs_for(production_id)[dot]
336
+ next unless symbol_id
337
+
338
+ production_cache = (cache[production_id] ||= []) #: Array[Array[lr_item?]?]
339
+ shifted_dot = dot + 1
340
+ item_cache = (production_cache[shifted_dot] ||= []) #: Array[lr_item?]
341
+ item = item_cache[lookahead]
342
+ unless item
343
+ item = [production_id, shifted_dot, lookahead].freeze #: lr_item
344
+ item_cache[lookahead] = item
345
+ end
346
+ (kernels[symbol_id] ||= Set.new) << item
122
347
  end
123
- closure(Set.new(moved))
348
+ kernels
124
349
  end
125
350
 
126
351
  # @rbs (Array[item_set] states, transitions transitions) -> [Array[packed_items], transitions]
127
352
  def merge_lalr(states, transitions)
128
- groups = {} #: Hash[Array[item_core], Integer]
353
+ groups = {} #: Hash[Array[Integer], Integer]
129
354
  state_groups = states.map do |items|
130
355
  core = core_key(items)
131
356
  groups[core] ||= groups.length
@@ -145,15 +370,6 @@ module Ibex
145
370
  [merged, merged_transitions]
146
371
  end
147
372
 
148
- # @rbs (Array[item_set] canonical_states, transitions canonical_transitions) -> [Array[packed_items], transitions]
149
- def automaton_items(canonical_states, canonical_transitions)
150
- return [pack_canonical_items(canonical_states), canonical_transitions] if @algorithm == :lr1
151
-
152
- items, transitions = merge_lalr(canonical_states, canonical_transitions)
153
- apply_slr_lookaheads(items) if @algorithm == :slr
154
- [items, transitions]
155
- end
156
-
157
373
  # @rbs (Array[item_set] states) -> Array[packed_items]
158
374
  def pack_canonical_items(states)
159
375
  states.map do |items|
@@ -176,7 +392,7 @@ module Ibex
176
392
 
177
393
  # @rbs (Integer production_id) -> Array[Integer]
178
394
  def slr_lookaheads(production_id)
179
- return [0] if production_id == AUGMENTED_PRODUCTION
395
+ return [0] if production_id.negative?
180
396
 
181
397
  lhs = @grammar.productions.fetch(production_id).lhs
182
398
  bits = @sets.follow_bits.fetch(lhs)
@@ -187,7 +403,8 @@ module Ibex
187
403
  def build_states(merged_items, transitions)
188
404
  merged_items.each_with_index.map do |item_map, state_id|
189
405
  items = item_map.sort.map do |(production, dot), lookaheads|
190
- IR::AutomatonItem.new(production: production, dot: dot, lookaheads: lookaheads.to_a)
406
+ visible_production = production.negative? ? AUGMENTED_PRODUCTION : production
407
+ IR::AutomatonItem.new(production: visible_production, dot: dot, lookaheads: lookaheads.to_a)
191
408
  end
192
409
  build_state(state_id, items, transitions[state_id])
193
410
  end
@@ -209,17 +426,36 @@ module Ibex
209
426
  end
210
427
  add_completed_actions(items, candidates)
211
428
  actions, conflicts = resolve_actions(candidates)
429
+ conflicts = conflicts.map { |conflict| attribute_midrule_conflict(conflict) }
212
430
  IR::AutomatonState.new(id: state_id, items: items, transitions: transitions, actions: actions,
213
431
  gotos: gotos, conflicts: conflicts)
214
432
  end
215
433
 
434
+ # Conflict hashes are produced locally immediately before this boundary.
435
+ # Both conflict variants carry the same optional midrule provenance field.
436
+ # @rbs (IR::conflict conflict) -> IR::conflict
437
+ def attribute_midrule_conflict(conflict)
438
+ production_ids = if conflict[:type] == :shift_reduce
439
+ [conflict[:reduce]]
440
+ else
441
+ conflict[:reductions]
442
+ end #: Array[Integer]
443
+ origins = production_ids.filter_map do |production_id|
444
+ production = @grammar.productions.fetch(production_id)
445
+ production.origin[:loc] if production.origin[:kind] == :inline_action
446
+ end
447
+ result = conflict.dup #: Hash[Symbol, untyped]
448
+ result[:midrule_origins] = origins.uniq unless origins.empty?
449
+ result #: IR::conflict
450
+ end
451
+
216
452
  # @rbs (Array[IR::AutomatonItem] items, Hash[Integer, Array[IR::parser_action]] candidates) -> void
217
453
  def add_completed_actions(items, candidates)
218
454
  items.each do |item|
219
455
  next unless item.dot == rhs_for(item.production).length
220
456
 
221
457
  item.lookaheads.each do |lookahead|
222
- action = if item.production == AUGMENTED_PRODUCTION
458
+ action = if item.production.negative?
223
459
  { type: :accept } #: IR::accept_action
224
460
  else
225
461
  { type: :reduce, production: item.production } #: IR::reduce_action
@@ -246,25 +482,251 @@ module Ibex
246
482
 
247
483
  # @rbs (Integer production_id) -> Array[Integer]
248
484
  def rhs_for(production_id)
249
- if production_id == AUGMENTED_PRODUCTION
250
- start = @grammar.symbol(@grammar.start) || raise(Ibex::Error, "missing start symbol")
485
+ if production_id.negative?
486
+ name = start_name_for_augmented(production_id)
487
+ start = @grammar.symbol(name) || raise(Ibex::Error, "missing start symbol #{name}")
251
488
  return [start.id]
252
489
  end
253
490
 
254
491
  @grammar.productions.fetch(production_id).rhs
255
492
  end
256
493
 
257
- # @rbs (item_set items) -> Array[item_core]
494
+ # @rbs () -> [Integer, Integer, Integer]
495
+ def canonical_key_radices
496
+ cached = @canonical_key_radices
497
+ return cached if cached
498
+
499
+ longest_rhs = @grammar.productions.map { |production| production.rhs.length }.max || 0
500
+ highest_terminal_id = @grammar.terminals.map(&:id).max || 0
501
+ radices = [
502
+ @grammar.starts.length,
503
+ [longest_rhs, 1].max + 1,
504
+ highest_terminal_id + 1
505
+ ] #: [Integer, Integer, Integer]
506
+ @canonical_key_radices = radices.freeze
507
+ end
508
+
509
+ # @rbs (item_set items) -> Array[Integer]
258
510
  def core_key(items)
511
+ production_offset, dot_radix, = canonical_key_radices
259
512
  items.map do |production, dot, _lookahead|
260
- [production, dot] #: item_core
513
+ ((production + production_offset) * dot_radix) + dot
261
514
  end.uniq.sort
262
515
  end
263
516
 
264
- # @rbs (item_set items) -> Array[lr_item]
517
+ # @rbs (item_set items) -> Array[Integer]
265
518
  def item_key(items)
266
- items.to_a.sort
519
+ production_offset, dot_radix, lookahead_radix = canonical_key_radices
520
+ items.map do |production, dot, lookahead|
521
+ core = ((production + production_offset) * dot_radix) + dot
522
+ (core * lookahead_radix) + lookahead
523
+ end.sort
524
+ end
525
+
526
+ # @rbs (String name) -> Integer
527
+ def augmented_production(name)
528
+ index = @grammar.starts.index(name)
529
+ raise Ibex::Error, "missing start symbol #{name}" unless index
530
+
531
+ AUGMENTED_PRODUCTION - index
532
+ end
533
+
534
+ # @rbs (Integer production_id) -> String
535
+ def start_name_for_augmented(production_id)
536
+ @grammar.starts.fetch(-production_id - 1)
537
+ end
538
+
539
+ # @rbs (Array[packed_items] items) -> Hash[String, Integer]
540
+ def entry_states_for(items)
541
+ @start_names.to_h do |name|
542
+ production = augmented_production(name)
543
+ state = items.index { |item_map| item_map.key?([production, 0]) }
544
+ raise Ibex::Error, "missing initial state for start symbol #{name}" unless state
545
+
546
+ [name, state]
547
+ end
548
+ end
549
+
550
+ # @rbs () -> IR::Automaton
551
+ def build_isolated_automaton
552
+ entries = @start_names.map { |name| isolated_entry(name) }
553
+ states = [] #: Array[IR::AutomatonState]
554
+ entry_states = {} #: Hash[String, Integer]
555
+ construction_states = 0
556
+ canonical_counts = [] #: Array[Integer?]
557
+ entries.each do |name, automaton, metrics|
558
+ offset = states.length
559
+ entry_states[name] = offset + automaton.entry_states.fetch(name)
560
+ states.concat(automaton.states.map { |state| offset_state(state, offset, name) })
561
+ construction_states += metrics.construction_states
562
+ canonical_counts << metrics.canonical_states
563
+ end
564
+ canonical_states = canonical_counts.compact.sum if canonical_counts.none?(&:nil?)
565
+ @metrics = isolated_metrics(entries, construction_states, canonical_states, states.length)
566
+ build_output_automaton(
567
+ states: states, conflict_summary: conflict_summary(states), entry_states: entry_states
568
+ )
569
+ end
570
+
571
+ # @rbs (Array[[String, IR::Automaton, BuildMetrics]] entries, Integer construction_states,
572
+ # Integer? canonical_states, Integer final_states) -> BuildMetrics
573
+ def isolated_metrics(entries, construction_states, canonical_states, final_states)
574
+ BuildMetrics.new(
575
+ construction_states: construction_states, canonical_states: canonical_states,
576
+ final_states: final_states, strategy: :entry_isolation,
577
+ lr0_states: sum_optional_metric(entries, :lr0_states),
578
+ lr0_items: sum_optional_metric(entries, :lr0_items),
579
+ canonical_items: sum_optional_metric(entries, :canonical_items),
580
+ final_items: sum_optional_metric(entries, :final_items),
581
+ final_lookahead_items: sum_optional_metric(entries, :final_lookahead_items),
582
+ propagation_edges: sum_optional_metric(entries, :propagation_edges),
583
+ ielr_initial_partitions: sum_optional_metric(entries, :ielr_initial_partitions),
584
+ ielr_final_partitions: sum_optional_metric(entries, :ielr_final_partitions),
585
+ ielr_annotations: sum_optional_metric(entries, :ielr_annotations),
586
+ ielr_annotated_states: sum_optional_metric(entries, :ielr_annotated_states),
587
+ ielr_inadequacies: sum_optional_metric(entries, :ielr_inadequacies),
588
+ ielr_split_stable_discarded: sum_optional_metric(entries, :ielr_split_stable_discarded),
589
+ ielr_lalr_states: sum_optional_metric(entries, :ielr_lalr_states),
590
+ ielr_split_states: sum_optional_metric(entries, :ielr_split_states),
591
+ ielr_unreachable_removed: sum_optional_metric(entries, :ielr_unreachable_removed),
592
+ ielr_remergeable_candidates: sum_optional_metric(entries, :ielr_remergeable_candidates)
593
+ )
594
+ end
595
+
596
+ # @rbs (Array[[String, IR::Automaton, BuildMetrics]] entries, Symbol method) -> Integer?
597
+ def sum_optional_metric(entries, method)
598
+ values = entries.map { |_name, _automaton, metrics| metrics.public_send(method) }
599
+ values.compact.sum if values.none?(&:nil?)
600
+ end
601
+
602
+ # @rbs () -> Integer
603
+ def output_schema_version
604
+ IR::SCHEMA_VERSION
605
+ end
606
+
607
+ # @rbs () -> String?
608
+ def output_entry_construction
609
+ @entry_isolation ? "isolated" : "shared"
610
+ end
611
+
612
+ # @rbs (String name) -> [String, IR::Automaton, BuildMetrics]
613
+ def isolated_entry(name)
614
+ builder = self.class.new(
615
+ @grammar, algorithm: @algorithm, lalr_strategy: @lalr_strategy,
616
+ ielr_strategy: @ielr_strategy,
617
+ starts: [name], entry_isolation: true, attribute_entries: false, profile: @profile,
618
+ remove_unreachable: @remove_unreachable
619
+ )
620
+ automaton = builder.build
621
+ metrics = builder.metrics
622
+ raise Ibex::Error, "missing build metrics for start symbol #{name}" unless metrics
623
+
624
+ [name, automaton, metrics]
625
+ end
626
+
627
+ # @rbs (IR::AutomatonState state, Integer offset, String entry) -> IR::AutomatonState
628
+ def offset_state(state, offset, entry)
629
+ actions = state.actions.transform_values { |action| offset_action(action, offset) }
630
+ conflicts = state.conflicts.map { |conflict| offset_conflict(conflict, offset, entry) }
631
+ IR::AutomatonState.new(
632
+ id: state.id + offset, items: state.items,
633
+ transitions: state.transitions.transform_values { |target| target + offset },
634
+ actions: actions, gotos: state.gotos.transform_values { |target| target + offset },
635
+ default_action: offset_default_action(state.default_action, offset), conflicts: conflicts
636
+ )
637
+ end
638
+
639
+ # @rbs (IR::parser_action action, Integer offset) -> IR::parser_action
640
+ def offset_action(action, offset)
641
+ return action unless action[:type] == :shift
642
+
643
+ shift = action #: IR::shift_action
644
+ { type: :shift, state: shift[:state] + offset }
645
+ end
646
+
647
+ # @rbs (IR::parser_action? action, Integer offset) -> IR::parser_action?
648
+ def offset_default_action(action, offset)
649
+ action && offset_action(action, offset)
650
+ end
651
+
652
+ # @rbs (IR::conflict conflict, Integer offset, String entry) -> IR::conflict
653
+ def offset_conflict(conflict, offset, entry)
654
+ if conflict[:type] == :shift_reduce
655
+ shift_reduce = conflict #: IR::shift_reduce_conflict
656
+ shifted = shift_reduce.dup #: IR::shift_reduce_conflict
657
+ shifted[:shift_to] = shift_reduce[:shift_to] + offset
658
+ shifted[:entries] = [entry]
659
+ return shifted
660
+ end
661
+
662
+ reduce_reduce = conflict #: IR::reduce_reduce_conflict
663
+ shifted = reduce_reduce.dup #: IR::reduce_reduce_conflict
664
+ shifted[:entries] = [entry]
665
+ shifted
666
+ end
667
+
668
+ # @rbs (Array[IR::AutomatonState] states, Hash[String, Integer] entry_states) ->
669
+ # Array[IR::AutomatonState]
670
+ def attribute_entry_conflicts(states, entry_states)
671
+ reachability = entry_reachability(states, entry_states)
672
+ isolated = isolated_conflict_fingerprints
673
+ states.map do |state|
674
+ conflicts = state.conflicts.map do |conflict| # @type var conflict: IR::conflict
675
+ entries = @start_names.select { |name| reachability.fetch(state.id).include?(name) }
676
+ attributed = conflict.dup #: IR::conflict
677
+ attributed[:entries] = entries
678
+ attributed[:composite] = true unless isolated.include?(conflict_fingerprint(conflict))
679
+ attributed
680
+ end #: Array[IR::conflict]
681
+ IR::AutomatonState.new(
682
+ id: state.id, items: state.items, transitions: state.transitions, actions: state.actions,
683
+ gotos: state.gotos, default_action: state.default_action, conflicts: conflicts
684
+ )
685
+ end
686
+ end
687
+
688
+ # @rbs (Array[IR::AutomatonState] states, Hash[String, Integer] entry_states) -> Array[Array[String]]
689
+ def entry_reachability(states, entry_states)
690
+ reachable = Array.new(states.length) { [] } #: Array[Array[String]]
691
+ entry_states.each do |name, initial|
692
+ queue = [initial]
693
+ visited = {} #: Hash[Integer, bool]
694
+ until queue.empty?
695
+ state_id = queue.shift
696
+ next if visited[state_id]
697
+
698
+ visited[state_id] = true
699
+ reachable.fetch(state_id) << name
700
+ states.fetch(state_id).transitions.each_value { |target| queue << target }
701
+ end
702
+ end
703
+ reachable
704
+ end
705
+
706
+ # @rbs () -> Set[conflict_fingerprint]
707
+ def isolated_conflict_fingerprints
708
+ @start_names.each_with_object(Set.new) do |name, fingerprints|
709
+ isolated = self.class.new(
710
+ @grammar, algorithm: @algorithm, lalr_strategy: @lalr_strategy,
711
+ ielr_strategy: @ielr_strategy,
712
+ starts: [name], attribute_entries: false
713
+ ).build
714
+ isolated.states.each do |state|
715
+ state.conflicts.each { |conflict| fingerprints << conflict_fingerprint(conflict) }
716
+ end
717
+ end
718
+ end
719
+
720
+ # @rbs (IR::conflict conflict) -> conflict_fingerprint
721
+ def conflict_fingerprint(conflict)
722
+ reductions = if conflict[:type] == :shift_reduce
723
+ [conflict[:reduce]]
724
+ else
725
+ conflict[:reductions]
726
+ end #: Array[Integer]
727
+ [conflict[:type], conflict[:symbol], reductions]
267
728
  end
268
729
  end
730
+ # rubocop:enable Metrics/ClassLength
269
731
  end
270
732
  end