ibex 0.1.0 → 0.2.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 (384) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +247 -104
  3. data/docs/architecture.md +322 -28
  4. data/docs/cst-migration.md +107 -0
  5. data/docs/cst.md +213 -0
  6. data/docs/development.md +129 -0
  7. data/docs/editor-setup.md +25 -0
  8. data/docs/error-ux.md +60 -0
  9. data/docs/grammar-reference.md +458 -18
  10. data/docs/lexer-migration.md +51 -0
  11. data/docs/racc-migration.md +34 -6
  12. data/docs/release-readiness.md +163 -0
  13. data/docs/stability.md +124 -0
  14. data/examples/README.md +60 -0
  15. data/examples/calculator.y +44 -0
  16. data/examples/csv.y +32 -0
  17. data/examples/ini.y +70 -0
  18. data/examples/json.y +48 -0
  19. data/examples/tiny_language.y +75 -0
  20. data/lib/ibex/analysis/sets.rb +5 -4
  21. data/lib/ibex/artifact_set.rb +63 -0
  22. data/lib/ibex/cli/ambiguity.rb +66 -0
  23. data/lib/ibex/cli/counterexample_options.rb +6 -2
  24. data/lib/ibex/cli/coverage.rb +173 -0
  25. data/lib/ibex/cli/debug.rb +106 -0
  26. data/lib/ibex/cli/diagnostics.rb +116 -0
  27. data/lib/ibex/cli/documentation.rb +67 -0
  28. data/lib/ibex/cli/error_messages.rb +177 -0
  29. data/lib/ibex/cli/explain.rb +76 -0
  30. data/lib/ibex/cli/formatting.rb +386 -0
  31. data/lib/ibex/cli/generation_artifacts.rb +138 -0
  32. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  33. data/lib/ibex/cli/grammar_tests.rb +122 -0
  34. data/lib/ibex/cli/ir_tools.rb +184 -0
  35. data/lib/ibex/cli/lsp.rb +33 -0
  36. data/lib/ibex/cli/outputs.rb +91 -8
  37. data/lib/ibex/cli/racc_migration.rb +87 -0
  38. data/lib/ibex/cli/samples.rb +92 -0
  39. data/lib/ibex/cli/watch.rb +103 -0
  40. data/lib/ibex/cli.rb +506 -37
  41. data/lib/ibex/codegen/action_locations.rb +103 -0
  42. data/lib/ibex/codegen/action_method_source.rb +210 -0
  43. data/lib/ibex/codegen/action_source.rb +118 -0
  44. data/lib/ibex/codegen/ambiguity.rb +173 -0
  45. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  46. data/lib/ibex/codegen/documentation.rb +170 -0
  47. data/lib/ibex/codegen/explain.rb +312 -0
  48. data/lib/ibex/codegen/generated_action_abi.rb +288 -0
  49. data/lib/ibex/codegen/html.rb +94 -10
  50. data/lib/ibex/codegen/mermaid.rb +43 -0
  51. data/lib/ibex/codegen/railroad.rb +197 -0
  52. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  53. data/lib/ibex/codegen/rbs.rb +323 -2
  54. data/lib/ibex/codegen/report.rb +46 -6
  55. data/lib/ibex/codegen/ruby.rb +334 -59
  56. data/lib/ibex/codegen/ruby_actions.rb +143 -0
  57. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  58. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  59. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  60. data/lib/ibex/codegen/ruby_syntax.rb +90 -0
  61. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  62. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  63. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  64. data/lib/ibex/coverage/collector.rb +188 -0
  65. data/lib/ibex/coverage/event_stream.rb +97 -0
  66. data/lib/ibex/coverage/report.rb +259 -0
  67. data/lib/ibex/coverage/runtime_event_validator.rb +160 -0
  68. data/lib/ibex/coverage.rb +13 -0
  69. data/lib/ibex/error_messages/parser.rb +159 -0
  70. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  71. data/lib/ibex/error_messages/renderer.rb +65 -0
  72. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  73. data/lib/ibex/error_messages/update.rb +169 -0
  74. data/lib/ibex/error_messages.rb +165 -0
  75. data/lib/ibex/frontend/ast.rb +145 -6
  76. data/lib/ibex/frontend/bootstrap_parser.rb +47 -4
  77. data/lib/ibex/frontend/diagnostic.rb +81 -0
  78. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  79. data/lib/ibex/frontend/dsl.rb +66 -7
  80. data/lib/ibex/frontend/formatter.rb +407 -0
  81. data/lib/ibex/frontend/generated_parser.rb +614 -190
  82. data/lib/ibex/frontend/generated_parser_base.rb +164 -30
  83. data/lib/ibex/frontend/generated_parser_includes.rb +61 -0
  84. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  85. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  86. data/lib/ibex/frontend/generation.rb +33 -0
  87. data/lib/ibex/frontend/lexer.rb +162 -10
  88. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  89. data/lib/ibex/frontend/parser/declarations.rb +247 -11
  90. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  91. data/lib/ibex/frontend/parser/rules.rb +43 -6
  92. data/lib/ibex/frontend/parser.rb +182 -4
  93. data/lib/ibex/frontend/regenerator.rb +26 -1
  94. data/lib/ibex/frontend/resolution.rb +69 -0
  95. data/lib/ibex/frontend/resolver.rb +217 -0
  96. data/lib/ibex/frontend/rule_documentation.rb +103 -0
  97. data/lib/ibex/frontend/source_cursor.rb +132 -8
  98. data/lib/ibex/frontend/source_document.rb +229 -0
  99. data/lib/ibex/frontend/source_loader.rb +150 -0
  100. data/lib/ibex/frontend/source_span.rb +81 -0
  101. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  102. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  103. data/lib/ibex/frontend/token_adapter/declaration_state.rb +216 -26
  104. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  105. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  106. data/lib/ibex/frontend/token_adapter.rb +8 -3
  107. data/lib/ibex/frontend.rb +13 -2
  108. data/lib/ibex/generation_input.rb +57 -0
  109. data/lib/ibex/generation_manifest.rb +200 -0
  110. data/lib/ibex/generation_transaction.rb +261 -0
  111. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  112. data/lib/ibex/generation_transaction_validation.rb +196 -0
  113. data/lib/ibex/grammar_tests.rb +206 -0
  114. data/lib/ibex/ir/automaton_ir.rb +38 -5
  115. data/lib/ibex/ir/grammar_ir.rb +137 -24
  116. data/lib/ibex/ir/lexer_ir.rb +76 -0
  117. data/lib/ibex/ir/migration.rb +120 -0
  118. data/lib/ibex/ir/serialize.rb +110 -19
  119. data/lib/ibex/ir/validator/automaton.rb +345 -0
  120. data/lib/ibex/ir/validator/base.rb +129 -0
  121. data/lib/ibex/ir/validator/grammar.rb +604 -0
  122. data/lib/ibex/ir/validator/lexer.rb +113 -0
  123. data/lib/ibex/ir/validator.rb +62 -0
  124. data/lib/ibex/ir.rb +57 -4
  125. data/lib/ibex/lalr/build_metrics.rb +23 -0
  126. data/lib/ibex/lalr/builder.rb +379 -52
  127. data/lib/ibex/lalr/conflict.rb +1 -0
  128. data/lib/ibex/lalr/conflict_search.rb +11 -5
  129. data/lib/ibex/lalr/counterexample.rb +20 -5
  130. data/lib/ibex/lalr/direct_lookaheads.rb +236 -0
  131. data/lib/ibex/lalr/ielr_partition.rb +152 -0
  132. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  133. data/lib/ibex/lalr.rb +7 -0
  134. data/lib/ibex/location.rb +129 -0
  135. data/lib/ibex/lsp/document_handlers.rb +66 -0
  136. data/lib/ibex/lsp/document_store.rb +264 -0
  137. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  138. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  139. data/lib/ibex/lsp/initialization_handlers.rb +78 -0
  140. data/lib/ibex/lsp/navigation_handlers.rb +56 -0
  141. data/lib/ibex/lsp/position_codec.rb +109 -0
  142. data/lib/ibex/lsp/protocol_error.rb +33 -0
  143. data/lib/ibex/lsp/request_handlers.rb +45 -0
  144. data/lib/ibex/lsp/request_support.rb +87 -0
  145. data/lib/ibex/lsp/server.rb +146 -0
  146. data/lib/ibex/lsp/symbol_index.rb +241 -0
  147. data/lib/ibex/lsp/symbol_index_builder.rb +267 -0
  148. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  149. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  150. data/lib/ibex/lsp/symbol_occurrence.rb +17 -0
  151. data/lib/ibex/lsp/transport.rb +118 -0
  152. data/lib/ibex/lsp/workspace.rb +127 -0
  153. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  154. data/lib/ibex/lsp.rb +32 -0
  155. data/lib/ibex/normalize/declarations.rb +140 -7
  156. data/lib/ibex/normalize/diagnostics.rb +50 -5
  157. data/lib/ibex/normalize/expander.rb +59 -55
  158. data/lib/ibex/normalize/expression.rb +60 -39
  159. data/lib/ibex/normalize/inline_expansion.rb +414 -0
  160. data/lib/ibex/normalize/inline_validation.rb +174 -0
  161. data/lib/ibex/normalize/lexer.rb +131 -0
  162. data/lib/ibex/normalize/named_references.rb +60 -0
  163. data/lib/ibex/normalize/nodes.rb +46 -0
  164. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +69 -0
  165. data/lib/ibex/normalize/parameter_lowering.rb +126 -0
  166. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  167. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  168. data/lib/ibex/normalize/parameters.rb +199 -0
  169. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  170. data/lib/ibex/normalize.rb +179 -14
  171. data/lib/ibex/racc_migration/checker.rb +122 -0
  172. data/lib/ibex/racc_migration/harness.rb +177 -0
  173. data/lib/ibex/racc_migration/report.rb +94 -0
  174. data/lib/ibex/racc_migration.rb +12 -0
  175. data/lib/ibex/rake_task.rb +116 -0
  176. data/lib/ibex/samples.rb +186 -0
  177. data/lib/ibex/table_simulation/result.rb +51 -0
  178. data/lib/ibex/table_simulation/simulator.rb +253 -0
  179. data/lib/ibex/table_simulation/step.rb +60 -0
  180. data/lib/ibex/table_simulation/text.rb +31 -0
  181. data/lib/ibex/table_simulation.rb +13 -0
  182. data/lib/ibex/tables.rb +9 -70
  183. data/lib/ibex/version.rb +1 -1
  184. data/lib/ibex/watch/runner.rb +172 -0
  185. data/lib/ibex/watch/source_snapshot.rb +93 -0
  186. data/lib/ibex/watch.rb +11 -0
  187. data/lib/ibex.rb +25 -1
  188. data/schema/automaton-ir-v1.schema.json +401 -0
  189. data/schema/automaton-ir-v2.schema.json +58 -0
  190. data/schema/benchmark-v1.schema.json +212 -0
  191. data/schema/benchmark-v2.schema.json +61 -0
  192. data/schema/cst-v1.json +170 -0
  193. data/schema/error-ux-v1.schema.json +258 -0
  194. data/schema/explain-v1.schema.json +433 -0
  195. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  196. data/schema/generation-manifest-v1.schema.json +115 -0
  197. data/schema/grammar-ir-v1.schema.json +426 -0
  198. data/schema/grammar-ir-v2.schema.json +779 -0
  199. data/schema/lexer-ir-v1.schema.json +215 -0
  200. data/schema/migration-check-v1.schema.json +60 -0
  201. data/schema/performance-comparison-v1.schema.json +395 -0
  202. data/schema/public-performance-comparison-v1.schema.json +506 -0
  203. data/schema/public-performance-profile-v1.schema.json +360 -0
  204. data/schema/runtime-coverage-v1.schema.json +86 -0
  205. data/schema/runtime-event-v1.schema.json +308 -0
  206. data/schema/table-simulation-v1.schema.json +70 -0
  207. data/sig/ibex/artifact_set.rbs +37 -0
  208. data/sig/ibex/cli/ambiguity.rbs +22 -0
  209. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  210. data/sig/ibex/cli/coverage.rbs +53 -0
  211. data/sig/ibex/cli/debug.rbs +28 -0
  212. data/sig/ibex/cli/diagnostics.rbs +38 -0
  213. data/sig/ibex/cli/documentation.rbs +25 -0
  214. data/sig/ibex/cli/error_messages.rbs +57 -0
  215. data/sig/ibex/cli/explain.rbs +25 -0
  216. data/sig/ibex/cli/formatting.rbs +103 -0
  217. data/sig/ibex/cli/generation_artifacts.rbs +50 -0
  218. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  219. data/sig/ibex/cli/grammar_tests.rbs +36 -0
  220. data/sig/ibex/cli/ir_tools.rbs +51 -0
  221. data/sig/ibex/cli/lsp.rbs +14 -0
  222. data/sig/ibex/cli/outputs.rbs +17 -0
  223. data/sig/ibex/cli/racc_migration.rbs +30 -0
  224. data/sig/ibex/cli/samples.rbs +30 -0
  225. data/sig/ibex/cli/watch.rbs +43 -0
  226. data/sig/ibex/cli.rbs +104 -5
  227. data/sig/ibex/codegen/action_locations.rbs +45 -0
  228. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  229. data/sig/ibex/codegen/action_source.rbs +50 -0
  230. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  231. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  232. data/sig/ibex/codegen/documentation.rbs +50 -0
  233. data/sig/ibex/codegen/explain.rbs +85 -0
  234. data/sig/ibex/codegen/generated_action_abi.rbs +101 -0
  235. data/sig/ibex/codegen/html.rbs +18 -2
  236. data/sig/ibex/codegen/mermaid.rbs +16 -0
  237. data/sig/ibex/codegen/railroad.rbs +82 -0
  238. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  239. data/sig/ibex/codegen/rbs.rbs +84 -4
  240. data/sig/ibex/codegen/report.rbs +8 -0
  241. data/sig/ibex/codegen/ruby.rbs +97 -23
  242. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  243. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  244. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  245. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  246. data/sig/ibex/codegen/ruby_syntax.rbs +25 -0
  247. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  248. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  249. data/sig/ibex/coverage/collector.rbs +76 -0
  250. data/sig/ibex/coverage/event_stream.rbs +42 -0
  251. data/sig/ibex/coverage/report.rbs +100 -0
  252. data/sig/ibex/coverage/runtime_event_validator.rbs +68 -0
  253. data/sig/ibex/coverage.rbs +7 -0
  254. data/sig/ibex/error_messages/parser.rbs +58 -0
  255. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  256. data/sig/ibex/error_messages/renderer.rbs +23 -0
  257. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  258. data/sig/ibex/error_messages/update.rbs +43 -0
  259. data/sig/ibex/error_messages.rbs +85 -0
  260. data/sig/ibex/frontend/ast.rbs +208 -19
  261. data/sig/ibex/frontend/bootstrap_parser.rbs +11 -0
  262. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  263. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  264. data/sig/ibex/frontend/dsl.rbs +33 -4
  265. data/sig/ibex/frontend/formatter.rbs +135 -0
  266. data/sig/ibex/frontend/generated_parser.rbs +218 -68
  267. data/sig/ibex/frontend/generated_parser_base.rbs +61 -10
  268. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  269. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  270. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  271. data/sig/ibex/frontend/generation.rbs +6 -0
  272. data/sig/ibex/frontend/lexer.rbs +49 -2
  273. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  274. data/sig/ibex/frontend/parser/declarations.rbs +54 -0
  275. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  276. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  277. data/sig/ibex/frontend/parser.rbs +66 -0
  278. data/sig/ibex/frontend/regenerator.rbs +11 -0
  279. data/sig/ibex/frontend/resolution.rbs +33 -0
  280. data/sig/ibex/frontend/resolver.rbs +91 -0
  281. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  282. data/sig/ibex/frontend/source_cursor.rbs +36 -3
  283. data/sig/ibex/frontend/source_document.rbs +119 -0
  284. data/sig/ibex/frontend/source_loader.rbs +66 -0
  285. data/sig/ibex/frontend/source_span.rbs +53 -0
  286. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  287. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  288. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +73 -5
  289. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  290. data/sig/ibex/frontend/token_adapter.rbs +5 -2
  291. data/sig/ibex/frontend.rbs +1 -1
  292. data/sig/ibex/generation_input.rbs +37 -0
  293. data/sig/ibex/generation_manifest.rbs +67 -0
  294. data/sig/ibex/generation_transaction.rbs +82 -0
  295. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  296. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  297. data/sig/ibex/grammar_tests.rbs +93 -0
  298. data/sig/ibex/ir/automaton_ir.rbs +8 -2
  299. data/sig/ibex/ir/grammar_ir.rbs +75 -15
  300. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  301. data/sig/ibex/ir/migration.rbs +34 -0
  302. data/sig/ibex/ir/serialize.rbs +23 -6
  303. data/sig/ibex/ir/validator/automaton.rbs +109 -0
  304. data/sig/ibex/ir/validator/base.rbs +65 -0
  305. data/sig/ibex/ir/validator/grammar.rbs +184 -0
  306. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  307. data/sig/ibex/ir/validator.rbs +16 -0
  308. data/sig/ibex/ir.rbs +38 -4
  309. data/sig/ibex/lalr/build_metrics.rbs +20 -0
  310. data/sig/ibex/lalr/builder.rbs +95 -15
  311. data/sig/ibex/lalr/conflict_search.rbs +7 -3
  312. data/sig/ibex/lalr/counterexample.rbs +5 -2
  313. data/sig/ibex/lalr/direct_lookaheads.rbs +86 -0
  314. data/sig/ibex/lalr/ielr_partition.rbs +59 -0
  315. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  316. data/sig/ibex/lalr.rbs +6 -0
  317. data/sig/ibex/location.rbs +67 -0
  318. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  319. data/sig/ibex/lsp/document_store.rbs +94 -0
  320. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  321. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  322. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  323. data/sig/ibex/lsp/navigation_handlers.rbs +30 -0
  324. data/sig/ibex/lsp/position_codec.rbs +40 -0
  325. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  326. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  327. data/sig/ibex/lsp/request_support.rbs +40 -0
  328. data/sig/ibex/lsp/server.rbs +55 -0
  329. data/sig/ibex/lsp/symbol_index.rbs +78 -0
  330. data/sig/ibex/lsp/symbol_index_builder.rbs +87 -0
  331. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  332. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  333. data/sig/ibex/lsp/symbol_occurrence.rbs +25 -0
  334. data/sig/ibex/lsp/transport.rbs +35 -0
  335. data/sig/ibex/lsp/workspace.rbs +41 -0
  336. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  337. data/sig/ibex/lsp.rbs +7 -0
  338. data/sig/ibex/normalize/declarations.rbs +31 -0
  339. data/sig/ibex/normalize/diagnostics.rbs +9 -0
  340. data/sig/ibex/normalize/expander.rbs +20 -14
  341. data/sig/ibex/normalize/expression.rbs +10 -10
  342. data/sig/ibex/normalize/inline_expansion.rbs +120 -0
  343. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  344. data/sig/ibex/normalize/lexer.rbs +37 -0
  345. data/sig/ibex/normalize/named_references.rbs +20 -0
  346. data/sig/ibex/normalize/nodes.rbs +14 -0
  347. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +32 -0
  348. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  349. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  350. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  351. data/sig/ibex/normalize/parameters.rbs +46 -0
  352. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  353. data/sig/ibex/normalize.rbs +123 -18
  354. data/sig/ibex/racc_migration/checker.rbs +36 -0
  355. data/sig/ibex/racc_migration/harness.rbs +16 -0
  356. data/sig/ibex/racc_migration/report.rbs +53 -0
  357. data/sig/ibex/racc_migration.rbs +8 -0
  358. data/sig/ibex/rake_task.rbs +51 -0
  359. data/sig/ibex/samples.rbs +51 -0
  360. data/sig/ibex/table_simulation/result.rbs +32 -0
  361. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  362. data/sig/ibex/table_simulation/step.rbs +40 -0
  363. data/sig/ibex/table_simulation/text.rbs +14 -0
  364. data/sig/ibex/table_simulation.rbs +7 -0
  365. data/sig/ibex/tables.rbs +0 -26
  366. data/sig/ibex/watch/runner.rbs +48 -0
  367. data/sig/ibex/watch/source_snapshot.rbs +42 -0
  368. data/sig/ibex/watch.rbs +7 -0
  369. data/sig/ibex.rbs +2 -0
  370. metadata +301 -16
  371. data/.rubocop.yml +0 -43
  372. data/CHANGELOG.md +0 -30
  373. data/Rakefile +0 -25
  374. data/Steepfile +0 -10
  375. data/docs/compat-notes.md +0 -37
  376. data/docs/lexer-coverage.md +0 -14
  377. data/docs/phase10-extensions.md +0 -27
  378. data/gemfiles/Gemfile +0 -7
  379. data/gemfiles/Gemfile.lock +0 -98
  380. data/lib/ibex/frontend/grammar.y +0 -156
  381. data/lib/ibex/runtime/parser.rb +0 -360
  382. data/lib/ibex/runtime.rb +0 -8
  383. data/sig/ibex/runtime/parser.rbs +0 -167
  384. data/sig/ibex/runtime.rbs +0 -6
data/docs/architecture.md CHANGED
@@ -3,26 +3,177 @@
3
3
  Ibex keeps syntax, grammar meaning, automaton construction, and output concerns behind two versioned immutable contracts.
4
4
 
5
5
  ```text
6
- .y Lexer -> Token adapter -> self-hosted LR Parser ─┐
7
- Ruby DSL ────────┴─> Grammar AST -> Normalizer -> Grammar IR
6
+ .y root/fragments -> Frontend Lexer/CST -> self-hosted LR Parser -> canonical Resolver ─┐
7
+ Ruby DSL ───────────────────────────────────────────────────────┴─> Grammar AST -> Normalizer -> Grammar IR
8
+ | |
9
+ | Lexer IR v1
8
10
  |
9
11
  set analysis
10
12
  |
11
- SLR/LALR/LR1 Builder -> Automaton IR
13
+ SLR/LALR/IELR/LR1 Builder -> Automaton IR
12
14
  |
13
- Ruby/RBS generators / report / DOT / HTML / counterexamples
15
+ Ruby/RBS/action-shadow generators / report / DOT / Mermaid / HTML / counterexamples
14
16
  ```
15
17
 
16
18
  Frontend changes stop at the Normalizer. Algorithm strategies consume Grammar IR and produce identical Automaton IR shapes.
17
19
  Outputs consume Automaton IR and never call builder internals. The CLI only connects stages and supports JSON resumption.
18
20
 
21
+ An optional root-only `lexer` declaration normalizes to independently versioned
22
+ Lexer IR v1 and is embedded unchanged in Grammar IR v2. Its flat rule list
23
+ records state, declaration id, pattern source/options, action, and provenance;
24
+ `--emit=lexer-ir` exposes the same document. Code generation compiles every
25
+ pattern with an internal current-position anchor and emits immutable,
26
+ state-indexed rules. Per-parser mutable input, position, emission, and state
27
+ stacks live in `Runtime::GeneratedLexer`, never in the tables. See
28
+ [ADR 0014](decisions/0014-versioned-generated-lexer.md).
29
+
30
+ `pragma cst` remains an optional Grammar IR v2 flag. Regenerated format-v6
31
+ tables add deterministic kind and normalized slot metadata. `Runtime::Parser`
32
+ builds a pure-syntax Green entry for every shift and reduction on a stack
33
+ parallel to the semantic stack, so tree shape no longer depends on semantic
34
+ actions. Generated lexer skips become token-owned trivia. Parser failure,
35
+ repair, and lexer failure remain explicit and lossless. See
36
+ [ADR 0016](decisions/0016-red-green-concrete-syntax.md) and the
37
+ [CST guide](cst.md). Batch CST, typed views, editing, and serialization form
38
+ the Stable v1 contract. The runtime accepts structured CST metadata only in
39
+ the current format and rejects older CST tables before reading input; see
40
+ [ADR 0008](decisions/0008-versioned-runtime-package-boundary.md).
41
+
42
+ ## Red/Green CST v2
43
+
44
+ Green nodes and tokens contain no parents, source objects, absolute positions,
45
+ semantic values, or parser states. Their integer kinds, binary text, trivia,
46
+ flags, widths, children, and descendant counts are immutable and
47
+ Ractor-shareable. A session-owned `NodeCache` interns unannotated values. Lazy
48
+ Red wrappers add occurrence-specific parent, index, offset, span, location, and
49
+ typed-field navigation. The root is `source_file(start, EOF)`, and
50
+ `to_source` is byte exact for the `leading` and `balanced` trivia policies.
51
+
52
+ Generated `Parser::Syntax::<Name>` classes are typed views over Red nodes using
53
+ the same normalized `@node` metadata as Data AST generation. Persistent edits
54
+ replace one Green occurrence and copy its ancestor path; rewriters, batched
55
+ editors, annotations, and identity-skipping text diffing share that mechanism.
56
+ See [ADR 0017](decisions/0017-persistent-syntax-artifacts.md).
57
+
58
+ `ibex_cst` schema v1 serializes the Green root, kind metadata, compatibility
59
+ counts, and optional preorder parser memo independently of Grammar IR.
60
+ Validation reconstructs every derived width, flag, and descendant count.
61
+ Non-UTF-8 bytes use canonical Base64. See
62
+ [ADR 0017](decisions/0017-persistent-syntax-artifacts.md).
63
+
64
+ Incremental sessions are syntax-only: parser production actions do not run.
65
+ The generated lexer first validates token/state resynchronization. `Blender`
66
+ then offers either a fresh token or an old Green nonterminal to the LR driver.
67
+ A subtree is pushed directly through `goto` only when damage, recorded
68
+ left-state, follow-token identity, error flags, and positive width satisfy the
69
+ conservative reuse proof. Token and parse memos remain preorder/occurrence
70
+ state owned by one session; resource exhaustion falls back to the fresh token
71
+ stream. See [ADR 0018](decisions/0018-conservative-incremental-syntax-reuse.md).
72
+
73
+ Alternative-level `@node` declarations are preserved as Grammar IR v2
74
+ production metadata. Runtime Ruby, static action-shadow Ruby, and generated
75
+ RBS all derive Data node classes and Visitor/Listener hooks from that same
76
+ metadata; action source is never inspected to infer a shape. Symbol semantic
77
+ types and fully annotated nonterminals supply field types.
78
+
19
79
  The text frontend's canonical syntax is `lib/ibex/frontend/grammar.y`. Ibex generates and commits
20
80
  `lib/ibex/frontend/generated_parser.rb`; the public `Frontend::Parser` always delegates to that class. Lexer `Token` objects remain
21
81
  the semantic values passed through `TokenAdapter`, preserving their `Location` in AST nodes and diagnostics. The explicitly named
22
82
  handwritten `BootstrapParser` is excluded from normal loading and exists only to break the regeneration cycle. See
23
- [ADR 0015](decisions/0015-self-hosted-grammar-frontend.md) for the update procedure and boundary.
83
+ [ADR 0003](decisions/0003-self-hosted-grammar-frontend.md) for the update procedure and boundary.
84
+ `lib/ibex/frontend/shadow_grammar.y` describes the same frontend with parameterized list rules and an inline terminal wrapper.
85
+ It is generated only in tests and must match the production parser's AST across the canonical grammar and extended fixtures;
86
+ see the [development guide](development.md).
87
+
88
+ The lexer also retains an immutable lexical CST without changing the semantic token stream. `Frontend::Parser#parse_document`
89
+ returns a `SourceDocument` whose source, token-indexed segments, and AST come from that single lexer/parser pass.
90
+ `SourceSpan` uses half-open zero-based byte offsets and one-based Unicode-scalar line/column positions. Actions and user-code
91
+ bodies remain opaque segments; whitespace, line breaks, both comment forms, user-code markers, and EOF remain individually traversable.
92
+ `render`, byte slicing, and byte/line/column conversion provide the common source contract for formatter, documentation, include,
93
+ and language-server layers. `RuleDocumentation` correlates immediately preceding `##` comment-only lines with semantic rule
94
+ locations and copy-enriches generated Root/Fragment nodes; occupied opaque-segment lines are never scanned as comments. See
95
+ [ADR 0004](decisions/0004-shared-semantic-and-lossless-source-model.md).
96
+
97
+ `Frontend::Formatter` classifies the document's existing semantic tokens, replaces only whitespace/newline trivia, and protects
98
+ token, comment, action, heredoc, marker, and user-code bytes. It reparses the rendered root or fragment in the same frontend mode
99
+ and compares ASTs with an explicit work stack after removing location fields. The CLI's stdout, batch check, and transactional
100
+ in-place surfaces are therefore downstream consumers of `SourceDocument`, not an alternate grammar parser. Existing newline
101
+ segment spellings and blank-line counts survive required line boundaries; new boundaries use the first newline even when it is
102
+ inside opaque text. Batch stages and hard-link backups live beside each resolved target. Alias rejection, reverse rollback, and
103
+ all-directory synchronization preserve full file modes and relative or absolute symlink identities. Backups are synchronized
104
+ before installation; a failed restore preserves its backup, while post-commit cleanup problems are status-0 warnings. See
105
+ [ADR 0004](decisions/0004-shared-semantic-and-lossless-source-model.md).
24
106
 
25
- The RBS generator emits the generated class namespace, superclass, parser-table constants, and `.parser_tables` contract. The
107
+ `Frontend::SourceLoader` is the shared disk/overlay read boundary. Resolver's default loader retains canonical filesystem
108
+ behavior; LSP injects open buffers, including safe new files, while the resolver continues to enforce realpath containment,
109
+ symlink escape rejection, cycle identity, and diamond deduplication. `LSP::DocumentStore` layers monotonic open versions,
110
+ root/include closures, reverse dependencies, and disk restoration over that loader. `PositionCodec` is the only UTF-16 adapter
111
+ over frontend byte/scalar spans. `SymbolIndex` derives navigation and guarded rename edits from parsed nodes and lossless tokens,
112
+ never from opaque Ruby or textual scanning. Content-Length transport, lifecycle handling, and request handlers remain separate
113
+ from workspace semantics; see [ADR 0004](decisions/0004-shared-semantic-and-lossless-source-model.md).
114
+
115
+ CLI file generation renders every requested output into an immutable `ArtifactSet` before entering `GenerationTransaction`.
116
+ The transaction records the exact root, fragment, IR, and message bytes read through `GenerationInput`, rejects portable target
117
+ collisions and input aliases, takes stable sidecar locks, stages and synchronizes every file, and can restore hard-link backups
118
+ in reverse publication order. Ordinary companions publish first, the parser second, and an opt-in generation manifest last.
119
+ That manifest is the coherence marker: readers verify its listed paths, sizes, and SHA-256 digests and retry from a newly read
120
+ manifest on a mismatch. It is not a claim that several filesystem renames occur atomically. `--watch` feeds the same transaction
121
+ only candidates whose complete canonical source closure and failed include attempts remain unchanged across rendering and
122
+ publication. Portable polling, bounded debounce, failure deduplication, and cancellable nonblocking locks keep the last successful
123
+ generation usable while a source is invalid; see [ADR 0013](decisions/0013-transactional-generation-publication.md).
124
+ The executable's ordinary generation path declares this pipeline directly instead of loading the complete library and every
125
+ subcommand. Optional subcommands and generation outputs load at their invocation boundary while their public constants remain
126
+ autoloadable.
127
+
128
+ Extended grammar paths cross an explicit `Frontend::Resolver` boundary. The canonical `import` declaration and compatible
129
+ `include` spelling share this boundary. Roots retain class, start, options, and user code;
130
+ fragments contain composable declarations and rules. Canonical realpaths define DFS order, diamond deduplication, cycle identity,
131
+ and the Rake dependency closure. Canonical dirname ancestry keeps every resolved target below the root grammar directory after
132
+ symlink resolution, including when that directory is a filesystem or drive root. The source-only Parser never follows an
133
+ include. A `Resolution` recursively freezes its owned AST and defensive provenance copies while retaining rule identity for
134
+ include-chain lookup. Rake resolves this closure at task definition and refuses invalid graphs before timestamp checks can reuse
135
+ a stale output; see [ADR 0005](decisions/0005-contained-grammar-composition.md).
136
+
137
+ Extended parameterized definitions remain structural AST templates rather than grammar symbols. The Normalizer validates the
138
+ complete template graph, interns an impossible `$parameter_N` helper for each canonical call, records that helper in a memo
139
+ before expanding its body, and substitutes formals through nested EBNF and calls. Resumable alternative, item, and EBNF
140
+ continuations on an explicit depth-first worklist preserve ordinary lowering order and active-depth semantics without consuming
141
+ the Ruby call stack. Same-argument recursion therefore closes over the memo, while a configurable total-specialization budget
142
+ and structural constructor-growth detection bound argument-growing recursion without an arbitrary depth cutoff. Template actions,
143
+ precedence, metadata, documentation, locations, and definition include chains flow into the specialized productions; see
144
+ [ADR 0006](decisions/0006-bounded-structural-grammar-lowering.md).
145
+
146
+ Inline definitions are lowered temporarily, then a bounded deterministic post-pass substitutes marked alternatives through
147
+ ordinary, parameterized, and EBNF productions before diagnostics and LR construction. It removes every marked symbol and
148
+ production, remaps the dense symbol/production ids, and retains eliminated semantic reductions as a versioned post-order
149
+ action plan. The plan addresses flattened physical values followed by earlier logical results, records a nullable semantic
150
+ `result_type` on every newly emitted step, reconstructs surrounding stack
151
+ prefixes and semantic spans, and remains executable after IR serialization. Cycle validation covers paths through ordinary
152
+ rules and templates; the default 10,000-production cartesian budget is configurable. See
153
+ [ADR 0006](decisions/0006-bounded-structural-grammar-lowering.md).
154
+
155
+ The strict generated frontend remains the grammar authority during batch diagnostics. A diagnostic parse retries it after
156
+ suppressing only a whole declaration, whole rule, or outer alternative at balanced delimiters. Each retry must remove a new
157
+ original token and both attempts and emitted diagnostics are bounded. Lexical and syntax phases collect independently before a
158
+ global source-order limit is applied, so an earlier syntax error cannot be hidden by a later lexical error. Machine-readable
159
+ diagnostics retain source spans, defensive locations, and stable codes. A repaired AST is marked partial by `ParseResult`, and
160
+ is not attached to the unchanged source document. After a successful root parse, the CLI reports the first resolver grammar
161
+ failure through the same diagnostic schema, while actual resolution I/O failures remain invocation errors. The CLI exposes this
162
+ analysis only through `ibex diagnose`; see [ADR 0004](decisions/0004-shared-semantic-and-lossless-source-model.md).
163
+
164
+ The RBS generator emits the generated class namespace, superclass, parser-table constants, `.parser_tables` contract, and
165
+ private reduction and composed-fragment signatures. Declared symbol types refine the RHS tuple and LHS result independently,
166
+ with `untyped` used at undeclared boundaries; composed inputs resolve either a physical symbol type or an earlier plan step's
167
+ `result_type`. Reduction methods also receive a location tuple, surrounding location stack, and optional
168
+ `Runtime::LocationSpan`. Generated tables mark whether actions require locations. The runtime leaves the location stack
169
+ unallocated for ordinary two-element-token parses and creates or backfills it only when an action, tooling observer, or
170
+ three-element token requires it. The public immutable `Ibex::Location` range and `loc`/`result_loc` action helpers sit above the
171
+ same stack contract.
172
+ Default source mapping compiles opaque action methods with `class_eval` when the generated class loads. The opt-in action-shadow
173
+ generator makes those exact method bodies visible to Steep without runtime loading: runtime and shadow output share one method
174
+ source builder, while the shadow omits parser infrastructure and every user-code section. Ibex only generates this source;
175
+ executing Steep remains an application/CI boundary. See
176
+ [ADR 0011](decisions/0011-versioned-semantic-action-boundary.md). The
26
177
  gem also ships a one-to-one rbs-inline-generated signature tree under `sig/` for every Ruby source in `lib/`, including the
27
178
  self-hosted parser. CI regenerates into an empty temporary directory, compares the complete trees, validates the RBS environment,
28
179
  and runs Steep against the entire library. Token/location records, grammar AST nodes, parser classifier and bootstrap state, the
@@ -30,7 +181,7 @@ Ruby DSL, IR records, and automaton actions use concrete domain types. Generated
30
181
  cells, decoded JSON values, and user methods embedded as opaque Ruby source remain `untyped`; applications can reopen the generated
31
182
  class in their own RBS files to declare embedded methods.
32
183
 
33
- ## Grammar IR v1
184
+ ## Grammar IR versions 1 and 2
34
185
 
35
186
  Top-level fields:
36
187
 
@@ -38,30 +189,67 @@ Top-level fields:
38
189
  |---|---|
39
190
  | `ibex_ir`, `schema_version` | `"grammar"`, `1` |
40
191
  | `class_name`, `superclass` | Generated Ruby class contract |
41
- | `start`, `expect`, `options` | Start name, unresolved S/R expectation, result/action flags |
192
+ | `start`, optional `starts`, `expect`, `options` | Primary/ordered start names, unresolved S/R expectation, result/action flags |
193
+ | optional `params`, `printers` | Generated-constructor keywords and symbol-specific debug value formatters |
194
+ | optional `lexer` | Embedded independently versioned Lexer IR v1 |
42
195
  | `symbols` | Interned terminals and nonterminals; `$eof` id 0 and `error` id 1 |
43
196
  | `productions` | Numeric LHS/RHS ids, action, precedence override, source origin |
44
197
  | `user_code`, `conversions`, `warnings` | Concatenated code, external token expressions, structured diagnostics |
45
198
  | `user_code_chunks` | Optional opaque chunks with first-code-line locations for compatible source mapping |
46
199
 
47
- Warning records use stable type names (`undeclared_terminal`, `unused_terminal`, `unreachable_nonterminal`,
48
- `duplicate_production`, and `empty_language`) and retain source locations. The CLI applies display/error policy at the boundary;
200
+ Warning records use the additive type vocabulary `undeclared_terminal`, `unused_terminal`, `unused_precedence`,
201
+ `unreachable_terminal`, `unreachable_nonterminal`, `duplicate_production`, and `empty_language`, and retain source locations.
202
+ Schema-v1 readers must ignore warning types they do not recognize. The CLI applies display/error policy at the boundary;
49
203
  normalization and IR serialization do not discard diagnostics.
50
204
 
51
- A symbol has `id`, `name`, `kind`, `reserved`, optional `prec {associativity, level}`, and `loc`. A production has `id`, `lhs`,
52
- `rhs`, optional `action`, optional `prec_override`, and `origin`. Synthetic EBNF origins include an additive, deterministic
53
- `expression` label used by text, DOT, and HTML presentation while numeric symbol identities remain unchanged. An action has opaque
54
- `code`, `loc`, `named_refs [{name,index}]`, and `context_length`; middle-action helpers use the last field to view preceding stack
55
- values.
205
+ A symbol has `id`, `name`, `kind`, `reserved`, optional `prec {associativity, level}`, `loc`, `display_name`, and
206
+ `semantic_type`. The last two fields are omitted when undeclared, so older schema-v1 documents remain byte-stable and loadable.
207
+ A production has `id`, `lhs`, `rhs`, optional `action`, optional `prec_override`, and `origin`. Synthetic EBNF origins include
208
+ an additive, deterministic `expression` label used by text, DOT, and HTML presentation while numeric symbol identities remain
209
+ unchanged. An action has opaque `code`, `loc`, `named_refs [{name,index}]`, and `context_length`; middle-action helpers use the
210
+ last field to view preceding stack values.
211
+
212
+ IR objects and nested collections are frozen. JSON keys have deterministic order, so dump/load/dump is byte-stable. The additive
213
+ `user_code_chunks` field remains optional in version 1 so older JSON stays loadable.
214
+
215
+ New normalized grammars use version 2. It keeps every version-1 semantic field and adds explicit nullable metadata:
216
+
217
+ | Record | Version-2 metadata |
218
+ |---|---|
219
+ | grammar | `source_provenance {file, root, byte_span {start,end}}` and optional `migration` loss record |
220
+ | symbol | `doc` |
221
+ | production | `doc` and `expansion {parameter, inline, include_chain}` |
222
+ | action | `composition {strategy, fragments, plan {version, physical, steps}}`; new steps include nullable `result_type` |
223
+
224
+ The source-only text frontend supplies the source filename and leaves unknown metadata null. A resolved grammar also supplies its
225
+ canonical source root and each production's include chain while preserving its original-file origin. Lossless rule comments
226
+ populate symbol and user-production documentation, including through fragment resolution; synthetic EBNF helpers remain
227
+ undocumented. Parameterized specializations populate `expansion.parameter` with the template name and canonical structural
228
+ arguments while retaining the definition's include chain. Version-1 upgrades mark every unrecoverable metadata family in
229
+ `migration.unavailable` instead of guessing.
230
+ For compatibility with earlier version-2 documents, the input schema also accepts an absent composition-step
231
+ `result_type`; generators treat it as `untyped`.
232
+ `Serialize.load` and `Validator.validate` accept both versions; a loaded version-1 object dumps with the original version-1
233
+ shape. `IR::Migration.to_version` upgrades version 1 to 2 and is idempotent at version 2. The CLI exposes this as
234
+ `ibex migrate-ir INPUT --to=2 [-o FILE]`; file output uses an atomic same-directory rename and refuses to alias the input.
235
+
236
+ The v1 stabilization freeze covers required core fields, meanings, ordering,
237
+ identity, and validation behavior. The future `x-` experimental namespace is
238
+ outside that freeze, but current schemas remain closed and reject unknown
239
+ fields. Experimental data must therefore begin in a new additive schema
240
+ version rather than weakening an existing document. See
241
+ [stability and deprecation](stability.md).
56
242
 
57
- IR objects and nested collections are frozen. JSON keys have deterministic order, so dump/load/dump is byte-stable. Incompatible
58
- schema changes require a new version. The additive `user_code_chunks` field is optional so older schema-v1 JSON remains loadable;
59
- new text-front-end IR retains it to make direct and resumed `--line-convert-all` generation identical.
243
+ `Codegen::Documentation` renders normalized user rules and alternatives as escaped Markdown, self-contained accessible HTML, or
244
+ railroad SVG. The railroad renderer includes visible wrapped rule descriptions in its section-height calculation and exposes the
245
+ full escaped text through SVG descriptions. `ibex doc` resolves the same canonical include graph and writes to stdout or an
246
+ atomic file without generating or executing application parser code.
60
247
 
61
- ## Automaton IR v1
248
+ ## Automaton IR versions 1 and 2
62
249
 
63
- Top-level fields are `ibex_ir: "automaton"`, `schema_version`, `algorithm`, `grammar_digest`, embedded `grammar`, `states`, and
64
- `conflict_summary`. Embedding Grammar IR makes automaton JSON sufficient for code generation after `--from=automaton-ir`.
250
+ Top-level fields are `ibex_ir: "automaton"`, `schema_version`, `algorithm`, `grammar_digest`, embedded `grammar`, `states`,
251
+ optional `entry_states`, and `conflict_summary`. Embedding Grammar IR makes automaton JSON sufficient for code generation after
252
+ `--from=automaton-ir`. A multi-entry automaton maps every ordered grammar start name to its initial state.
65
253
 
66
254
  Each state contains:
67
255
 
@@ -70,30 +258,136 @@ Each state contains:
70
258
  - resolved terminal `actions` and nonterminal `gotos`;
71
259
  - an optional reduce `default_action`, selected only when explicit error masks preserve every terminal lookup and reduce the
72
260
  total encoded ACTION entries;
73
- - every conflict, including precedence-resolved conflicts and the resolution reason.
261
+ - every conflict, including precedence-resolved conflicts and the resolution reason; multi-entry conflicts also carry their
262
+ reachable `entries` and optional `composite` marker.
74
263
 
75
264
  `conflict_summary.sr` counts unresolved default-shift conflicts for `expect`; `resolved_sr` counts retained precedence or
76
265
  associativity decisions; `rr` counts reduce/reduce cells.
77
266
 
267
+ New automata use version 2 and always embed Grammar IR version 2. Migration recalculates `grammar_digest` from the upgraded
268
+ canonical grammar. Version-1 automata remain loadable, validatable, and byte-stable. Published Draft 2020-12 contracts for both
269
+ versions live under `schema/`; see
270
+ [ADR 0001](decisions/0001-versioned-ir-pipeline.md).
271
+
272
+ `--emit=sets` is a deterministic analysis view rather than another IR: it emits lexically sorted nullable nonterminals and
273
+ FIRST/FOLLOW maps for nonterminals. DOT, Mermaid, and the self-contained searchable HTML report are deterministic presentation
274
+ views over Automaton IR.
275
+
276
+ ## Construction algorithms and counterexamples
277
+
278
+ The `lalr` and `slr` strategies construct LR(0) states directly for a single entry. LALR lookaheads are the least fixed point of deterministic
279
+ shift, spontaneous-FIRST, and nullable-suffix propagation edges over item occurrences; SLR replaces completed lookaheads with
280
+ FOLLOW sets. Multiple entries seed distinct augmented canonical items and use canonical core merging because the direct
281
+ lookahead graph has a single-root contract. Canonical `lr1` retains the canonical collection. An explicit canonical-and-merge
282
+ LALR reference strategy proves byte equivalence without changing the Automaton IR algorithm label. `ielr` conservatively merges action-compatible canonical
283
+ states and refines partitions until outgoing transitions are congruent, avoiding LALR inadequacies without promising a minimum
284
+ state count. `--entry-isolation` instead constructs each start independently and concatenates the resulting state sets with
285
+ deterministic offsets. Shared builds attribute reachable entries to conflicts and compare isolated conflict fingerprints to
286
+ identify merge-created composite conflicts. All strategies use the same conflict resolver and default reduction pass. After a build, frozen diagnostic
287
+ `metrics` record the strategy and construction/final state counts, plus a canonical count only when one was actually built. See
288
+ [ADR 0007](decisions/0007-shared-parser-construction-pipeline.md).
289
+
290
+ `Ibex::LALR::Counterexample` consumes only Automaton IR. For each conflict it explores parser-stack configurations, forces the
291
+ competing actions, and searches for a common accepting suffix. A successful result contains both complete derivation trees and is
292
+ marked `unifying: true`. Search defaults to 32 tokens and 50,000 configurations; the Ruby and CLI APIs can set both positive
293
+ budgets. If no common sentence is found within them, the result is explicitly marked nonunifying and contains the deterministic
294
+ shortest reachability witness instead of claiming ambiguity.
295
+
296
+ `Ibex::Codegen::Explain` filters those immutable conflicts by state and canonical token identity before asking
297
+ `Counterexample#for_conflict` to search only the selected entries, then renders text or the versioned `explain` JSON analysis
298
+ shape. `Counterexample#all` retains its original all-conflict behavior. The view performs no additional parser analysis and does
299
+ not extend Grammar or Automaton IR.
300
+
301
+ The repository's self-authored representative grammar feeds the current versioned `ibex_benchmark` v2 document. Its JSON Schema
302
+ is shipped beside the IR schemas, while committed environment-specific observations live under the matching
303
+ `benchmark/results/vN` directory. Timing and peak RSS remain non-gating; CI reproduces only deterministic structure and digests.
304
+ See the [benchmark guide](../benchmark/README.md).
305
+
78
306
  ## Runtime table contract
79
307
 
80
308
  Generated subclasses expose `.parser_tables` with a required `format_version`, external `tokens`, display `token_names`, ACTION
81
309
  and GOTO tables, per-state default actions, and production `{lhs,length,action}` records. The runtime validates the version before
82
- reading input and rejects missing or unsupported formats with a regeneration instruction; see
83
- [ADR 0018](decisions/0018-parser-table-format-version.md). Plain tables are arrays of Hash rows. Compact tables use row
310
+ reading input and rejects missing or unsupported formats with a regeneration instruction. The generator emits v6. For non-CST
311
+ tables, the runtime accepts v1's two-argument actions, v2/v3 explicitly marked five-argument location actions, v3 explicitly
312
+ marked six-argument composed actions, v4 one-Array values actions, and v5/v6 positional actions. Marker contracts are validated
313
+ before input. A CST table is executable only when it uses current format-v6 structured metadata; older or boolean CST shapes
314
+ must be regenerated. See
315
+ [ADR 0008](decisions/0008-versioned-runtime-package-boundary.md). Plain tables are arrays of Hash rows. Compact tables use row
84
316
  displacement with offsets, values, and row-ownership checks; both expose equivalent lookups. Default reductions are restricted
85
317
  to known token ids, and explicit error masks preserve the pre-optimization result of every declared terminal cell, including
86
- the synthetic `error` terminal. The deterministic size policy is fixed by
87
- [ADR 0014](decisions/0014-compatibility-safe-default-reductions.md).
318
+ the synthetic `error` terminal. Extended parser tables opt `expected_tokens` into lookahead correction: the runtime copies only
319
+ the state stack and simulates reductions and gotos for each declared terminal, without evaluating semantic actions. The explicit
320
+ `expected_tokens_exact` API exposes the same result for compatible tables. The deterministic size policy is fixed by
321
+ [ADR 0008](decisions/0008-versioned-runtime-package-boundary.md).
322
+
323
+ Runtime execution is packaged independently as `ibex-runtime`, with its own version and RBS tree. The generator package depends
324
+ on a compatible runtime series but does not duplicate runtime-owned files. Compact lookup values live in a runtime-safe leaf
325
+ file, while table construction remains generator-only. Normal output requires only `ibex/runtime`; `-E` embeds the same sources.
326
+ See [ADR 0008](decisions/0008-versioned-runtime-package-boundary.md).
88
327
 
89
328
  The runtime maintains state and value stacks, pulls a lookahead only when required, and applies tagged `shift`, `reduce`,
90
329
  `accept`, and `error` actions. Recovery pops to a state that shifts token id 1, suppresses repeated reports for three successful
91
- shifts, and honors `yyerrok`. No-op `on_shift`, `on_reduce`, and `on_error_recover` extension points observe successfully
330
+ shifts, and honors `yyerrok`. No-op shift, reduce, recovery, location-aware, and discard extension points observe successfully
92
331
  committed events without changing parser results; the recovery hook retains the pre-pop error context and is distinct from an
93
- ordinary token shift. Their ordering and payload contract is fixed by [ADR 0013](decisions/0013-runtime-observation-hooks.md).
332
+ ordinary token shift. A configured value printer affects only human `yydebug` output. Their ordering and payload contract is
333
+ extended additively by
334
+ [ADR 0010](decisions/0010-committed-runtime-observation.md). Grammar-declared
335
+ symbol printers are optional IR v2 metadata compiled into private methods and
336
+ an id-indexed table.
337
+
338
+ Ordinary generated tables are recursively frozen and made Ractor-shareable. Threads and Ractors share those tables but parse
339
+ through distinct instances; stacks, lookahead, lexer state, callbacks, observers, and semantic values are session-owned. A
340
+ single instance rejects overlapping drivers. Immutable `Runtime::ResourceLimits` values bound every stack push and recovery
341
+ entry with finite defaults. Exhaustion raises the structured `ResourceLimitError`; see
342
+ [ADR 0009](decisions/0009-isolated-parser-sessions.md).
343
+
344
+ Extended Grammar IR v2 may additionally carry synchronization terminals and ordered `%on_error_reduce` groups. Table
345
+ construction fills only otherwise erroneous ACTION cells with a unique highest-priority completed declared production. At
346
+ runtime, an explicit shift of the synthetic `error` token always wins; only when it is unavailable does panic recovery discard
347
+ through a configured synchronization token and pop to a state that accepts that retained lookahead. Generated parsers without
348
+ sync declarations omit the optional table field. The pull/push ordering and observer contract are fixed by
349
+ the runtime and grammar reference documentation.
350
+
351
+ The separate `Runtime::Parser#observe` API publishes ordered, immutable schema-v1 events for tooling. Its bounded sanitizer
352
+ copies only JSON data and never retains application identities or private stacks. With no observer, parse transitions construct
353
+ no Event, payload summary, or dispatch snapshot; parser initialization still creates its ownership mutex. Generated tables
354
+ contribute grammar digest, table format, state count, and production count to the `start` event. `Runtime::EventJSONLTracer`
355
+ exposes the versioned stream; the original hook-based `Runtime::JSONLTracer` remains byte-compatible. The protocol and
356
+ exception/threading behavior are fixed by
357
+ [ADR 0010](decisions/0010-committed-runtime-observation.md).
358
+
359
+ Optional `Runtime::RepairPolicy` drives a bounded Dijkstra search over copied state stacks and buffered token identities. Search
360
+ uses explicit/default ACTION, GOTO, and production shape only; it never executes semantic code. A selected immutable edit plan is
361
+ reported once, then replayed through the ordinary runtime so actions and hooks remain committed-path effects. Pull lookahead,
362
+ push buffering, deterministic tie-breaking, fallback to yacc recovery, and no-policy compatibility are fixed by
363
+ [ADR 0012](decisions/0012-bounded-nonexecuting-analysis.md).
364
+
365
+ `Coverage::Collector` accepts only contiguous, complete runtime-event sessions with generated parser metadata. It counts entries
366
+ to the initial, shift, reduce-goto, and recovery states and counts committed reductions by production id. `Coverage::Report`
367
+ publishes ascending sparse hit arrays under the versioned runtime-coverage schema. Merge requires identical full grammar digest,
368
+ table format, and totals and uses checked addition. The coverage CLI only reads bounded JSON/JSON Lines and never loads generated
369
+ Ruby or executes semantic actions; collection, merge, threshold, and atomic-output policy are fixed by
370
+ [ADR 0010](decisions/0010-committed-runtime-observation.md).
371
+
372
+ `TableSimulation::Simulator` is a separate state-stack interpreter over validated Automaton IR. It resolves an explicit ACTION
373
+ cell before a default action, so explicit error masks remain authoritative, and never evaluates the opaque semantic-action
374
+ source stored in Grammar IR. Immutable steps expose state, lookahead, selected action source, reduction/goto metadata, and stack
375
+ depth. Action and stack budgets bound default/epsilon cycles and growth. The text/JSON CLI and versioned output contract are
376
+ fixed by [ADR 0012](decisions/0012-bounded-nonexecuting-analysis.md).
377
+
378
+ Grammar IR v2 may carry ordered accept/reject source tests without adding them to parser tables. `GrammarTests::Runner` generates
379
+ one embedded parser and executes fresh parser instances in a separate Ruby process, distinguishing `ParseError` rejection from
380
+ lexer/application errors and bounding the whole suite by a timeout. The separate runner loads the generated file, so guarded
381
+ footer programs stay inactive. The source contract, isolation boundary, and CI behavior are fixed by
382
+ the grammar reference documentation.
94
383
 
95
384
  ## Clean-room boundary
96
385
 
97
386
  Implementation work uses public racc documentation, CLI black-box behavior, and published LR algorithms only. racc implementation
98
387
  sources and generated source are not inputs to the design. Self-authored compatibility grammars execute both outputs in separate
99
388
  processes and compare observable results.
389
+
390
+ `RaccMigration::Checker` treats grammar code as opaque while reporting default-mode parse/normalization errors and known runtime
391
+ coupling. The separate harness generator emits source only; its output refuses an empty corpus and makes bounded child-process
392
+ execution explicit. The boundary is fixed by
393
+ [ADR 0012](decisions/0012-bounded-nonexecuting-analysis.md).
@@ -0,0 +1,107 @@
1
+ # CST Red/Green migration
2
+
3
+ The runtime supports only the current format-v6 Red/Green representation for
4
+ `pragma cst`. CST parser tables from formats v1 through v5, and boolean
5
+ `cst: true` tables without structured metadata, fail before the first token is
6
+ read and instruct the application to regenerate. Older non-CST parser tables
7
+ remain executable.
8
+
9
+ Regenerate with the same command used for the grammar, for example:
10
+
11
+ ```sh
12
+ ibex grammar.y -o parser.rb
13
+ ```
14
+
15
+ Upgrade the generator and runtime together, regenerate the parser, and update
16
+ consumers to the current APIs below. The removed `CST::Trivia`, `CST::Token`,
17
+ `CST::Missing`, `CST::Error`, and `CST::Node` constants are not defined.
18
+
19
+ ## Parsing
20
+
21
+ Use `parse_with_syntax` when both the semantic result and syntax are needed:
22
+
23
+ ```ruby
24
+ result = parser.parse_with_syntax(source, file: "input.txt")
25
+ value = result.value
26
+ root = result.syntax_root
27
+ diagnostics = result.diagnostics
28
+ ```
29
+
30
+ `parse`, `do_parse`, and `yyparse` keep returning the semantic result. After a
31
+ CST parse, `syntax_root` exposes the most recently built Red root.
32
+
33
+ ## Changed tree shape
34
+
35
+ The new root is always `source_file`:
36
+
37
+ ```text
38
+ source_file
39
+ ├── selected start-symbol node
40
+ └── $eof
41
+ ```
42
+
43
+ The EOF token owns final trivia. Code that previously treated the returned CST
44
+ as the start node should use `result.syntax_root.children.fetch(0)`.
45
+
46
+ The legacy implementation exposed semantic values inside an actionless
47
+ parent. For example, an action-bearing `term` could appear as a `CST::Token`
48
+ whose symbol was `"term"` and whose value was the action result. Format v6
49
+ always exposes the physical `term` syntax node in that position. Read the
50
+ semantic result from `result.value`; syntax children never contain semantic
51
+ values.
52
+
53
+ These are the primary incompatible shapes found by the migration
54
+ characterization suite. `deconstruct` continues to return syntax children and
55
+ `deconstruct_keys` retains the compatibility keys. Typed field keys are added
56
+ from `@node` slot metadata.
57
+
58
+ ## Removed public API mapping
59
+
60
+ The inventory below records the removed values and their format-v6
61
+ replacements.
62
+
63
+ | Removed value and members | Previous use | Format-v6 replacement |
64
+ | --- | --- | --- |
65
+ | `CST::Trivia#text`, `#location`, `#to_h` | leading/trailing trivia assertions | `GreenTrivia#text` and `#kind`; obtain absolute location from the owning Red token |
66
+ | `CST::Token#symbol`, `#value`, `#location`, `#leading_trivia` | terminal names, lexer action values, positions, skipped text | `SyntaxToken#kind_name`/`#symbol`, `#location`, and `#leading_trivia`; compatibility `#value` returns the same source bytes as `#text`, while semantic lexer values stay in the normal action/value path |
67
+ | `CST::Token#kind`, `#children`, `#deconstruct`, `#deconstruct_keys`, `#to_h` | error classification and pattern matching | integer `#kind`, `#error?`/`#missing?`, empty `#children`/`#deconstruct`, and compatibility pattern/hash keys |
68
+ | `CST::Missing` | bounded-repair insertion checks | `SyntaxToken#missing?`, `GreenToken#expected_kind`, and `CONTAINS_MISSING` |
69
+ | `CST::Error#reason` | lexical, syntax, delete, and discard checks | `SyntaxToken#error?`, `SyntaxNode#error?`, diagnostics, and Green error/skipped flags |
70
+ | `CST::Node#symbol`, `#production_id`, `#children`, `#location`, `#trailing_trivia` | root/reduction shape, source position, final trivia | `SyntaxNode#symbol`, `#children`, `#location`, and compatibility `#trailing_trivia`; `#production_id` is the `-1` sentinel because kinds/slot metadata replace occurrence-local production identity |
71
+ | `CST::Node#each`, `#deconstruct`, `#deconstruct_keys`, `#to_h`, `#with_trailing_trivia` | enumeration, pattern matching, final-trivia attachment | Red `Enumerable`/pattern/hash methods; persistent trivia changes use the owning token's `with_leading`/`with_trailing` |
72
+
73
+ Current pure-syntax shape and compatibility accessors are executable in
74
+ `test/codegen/cst_characterization_test.rb`. Rejection of obsolete CST table
75
+ shapes is covered by `test/runtime/table_format_test.rb` and
76
+ `test/codegen/cst_runtime_integration_test.rb`. The semantic-value removal is
77
+ the C1 change; the `source_file` root is C2.
78
+
79
+ ## Trivia
80
+
81
+ `--cst-trivia=attach` remains accepted and means `leading`.
82
+
83
+ - `leading`: skipped lexer text belongs to the next token.
84
+ - `balanced`: text through the first newline belongs to the preceding token,
85
+ with the remainder leading the next token.
86
+ - `drop`: trivia is omitted; source-coordinate and incremental APIs raise
87
+ because offsets no longer correspond to the original source.
88
+
89
+ Use `to_source` for a binary-exact reconstruction. On early `yyaccept`,
90
+ `incomplete_input?` is true and `to_source` is the consumed prefix.
91
+
92
+ ## Errors and repair
93
+
94
+ Lexical failures, yacc recovery, panic discards, and bounded repair return a
95
+ syntax result rather than changing the semantic action contract. Inspect
96
+ `diagnostics`, `contains_error?`, `each_error`, missing tokens, and Green flags
97
+ for skipped input. Unrecoverable input is retained under `synthetic_root`.
98
+
99
+ ## New capabilities
100
+
101
+ Format v6 provides typed `Parser::Syntax` views, persistent
102
+ path-copy editing, annotations, structural text diffing, `ibex_cst` v1
103
+ serialization, and syntax-only incremental sessions. These APIs have no
104
+ equivalent on the removed mixed semantic/syntax tree. Batch CST, views,
105
+ editing, and serialization are Stable; incremental sessions remain
106
+ Experimental. See the [CST guide](cst.md) for examples and the incremental
107
+ action contract.