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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../lalr"
3
4
  require_relative "symbol_labels"
4
5
 
5
6
  module Ibex
@@ -38,13 +39,22 @@ module Ibex
38
39
  # private def self.symbol_name: (Hash[Integer, String] labels, Integer id) -> String
39
40
  # private def tree_label: (IR::Grammar grammar, Hash[Integer, String] labels, untyped value) -> untyped
40
41
  # private def self.tree_label: (IR::Grammar grammar, Hash[Integer, String] labels, untyped value) -> untyped
42
+ # private def display_conflict: (IR::conflict conflict, IR::Grammar grammar,
43
+ # Hash[Integer, String] labels) -> Hash[Symbol, untyped]
44
+ # private def self.display_conflict: (IR::conflict conflict, IR::Grammar grammar,
45
+ # Hash[Integer, String] labels) -> Hash[Symbol, untyped]
46
+ # private def format_value: (untyped value) -> String
47
+ # private def self.format_value: (untyped value) -> String
41
48
 
42
49
  # @rbs (IR::Automaton automaton, ?max_tokens: Integer, ?max_configurations: Integer) -> String
43
50
  def render(automaton, max_tokens: LALR::Counterexample::DEFAULT_MAX_TOKENS,
44
51
  max_configurations: LALR::Counterexample::DEFAULT_MAX_CONFIGURATIONS)
45
52
  grammar = automaton.grammar
46
53
  labels = SymbolLabels.build(grammar)
47
- lines = ["Algorithm: #{automaton.algorithm}", "States: #{automaton.states.length}", ""]
54
+ entries = automaton.entry_states.map { |name, state| "#{name}=#{state}" }.join(", ")
55
+ lines = ["Algorithm: #{automaton.algorithm}", "States: #{automaton.states.length}"]
56
+ lines << "Entries: #{entries}" if automaton.entry_states.length > 1
57
+ lines << ""
48
58
  examples = LALR::Counterexample.new(
49
59
  automaton, max_tokens: max_tokens, max_configurations: max_configurations
50
60
  ).all.group_by { |example| example[:state] }
@@ -69,7 +79,9 @@ module Ibex
69
79
  end
70
80
  lines << " default: #{format_action(state.default_action)}" if state.default_action
71
81
  state.gotos.each { |symbol_id, target| lines << " goto #{symbol_name(labels, symbol_id)}: #{target}" }
72
- state.conflicts.each { |conflict| lines << " conflict: #{conflict.inspect}" }
82
+ state.conflicts.each do |conflict|
83
+ lines << " conflict: #{format_value(display_conflict(conflict, grammar, labels))}"
84
+ end
73
85
  examples.each { |example| append_counterexample(lines, example, grammar, labels) }
74
86
  lines << ""
75
87
  end
@@ -77,7 +89,8 @@ module Ibex
77
89
  # @rbs skip
78
90
  def append_counterexample(lines, example, grammar, labels)
79
91
  label = example[:unifying] ? "unifying counterexample" : "nonunifying witness"
80
- sentence = example[:sentence].dup.insert(example[:lookahead_index], "•").join(" ")
92
+ sentence = example[:sentence].map { |name| tree_label(grammar, labels, name) }
93
+ sentence = sentence.dup.insert(example[:lookahead_index], "•").join(" ")
81
94
  lines << " #{label}: #{sentence}"
82
95
  example[:interpretations].each do |interpretation|
83
96
  lines << " #{interpretation[:kind]} derivation:"
@@ -106,7 +119,8 @@ module Ibex
106
119
  # @rbs skip
107
120
  def format_item(item, grammar, labels)
108
121
  if item.production == LALR::Builder::AUGMENTED_PRODUCTION
109
- rhs = [grammar.start]
122
+ start = grammar.symbol(grammar.start)
123
+ rhs = [grammar.starts.one? && start ? symbol_name(labels, start.id) : "<entry>"]
110
124
  lhs = "$accept"
111
125
  else
112
126
  production = grammar.productions.fetch(item.production)
@@ -137,12 +151,38 @@ module Ibex
137
151
  symbol = grammar.symbol(value.to_s)
138
152
  symbol ? symbol_name(labels, symbol.id) : value
139
153
  end
154
+
155
+ # @rbs skip
156
+ def display_conflict(conflict, grammar, labels)
157
+ symbol = grammar.symbol(conflict[:symbol])
158
+ return conflict unless symbol
159
+
160
+ conflict.merge(symbol: symbol_name(labels, symbol.id))
161
+ end
162
+
163
+ # Keep reports byte-identical across Ruby versions whose Hash#inspect
164
+ # formatting differs for symbol keys.
165
+ # @rbs skip
166
+ def format_value(value)
167
+ case value
168
+ when Hash
169
+ entries = value.map do |key, item|
170
+ label = key.is_a?(Symbol) ? "#{key}:" : "#{format_value(key)} =>"
171
+ "#{label} #{format_value(item)}"
172
+ end
173
+ "{#{entries.join(', ')}}"
174
+ when Array
175
+ "[#{value.map { |item| format_value(item) }.join(', ')}]"
176
+ else
177
+ value.inspect
178
+ end
179
+ end
140
180
  module_function :append_state, :append_counterexample, :append_tree, :format_item, :format_action, :symbol_name,
141
- :tree_label
181
+ :tree_label, :display_conflict, :format_value
142
182
 
143
183
  class << self
144
184
  private :append_state, :append_counterexample, :append_tree, :format_item, :format_action, :symbol_name,
145
- :tree_label
185
+ :tree_label, :display_conflict, :format_value
146
186
  end
147
187
  end
148
188
  end
@@ -1,11 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../runtime/parser"
3
+ require "ibex/runtime/table_format"
4
+ require_relative "cst_metadata"
5
+ require_relative "generated_action_abi"
6
+ require_relative "ruby_actions"
7
+ require_relative "ruby_error_messages"
8
+ require_relative "ruby_table_metadata"
9
+ require_relative "ruby_value_printers"
10
+ require_relative "ruby_lexer"
11
+ require_relative "ruby_ast"
12
+ require_relative "ruby_syntax"
4
13
 
5
14
  module Ibex
6
15
  module Codegen
7
16
  # Generates a standalone Ruby parser class from Automaton IR.
17
+ # rubocop:disable Metrics/ClassLength -- generation stages are split into focused mixins.
8
18
  class Ruby
19
+ include RubyActions
20
+ include RubyErrorMessages
21
+ include RubyTableMetadata
22
+ include RubyValuePrinters
23
+ include RubyLexer
24
+ include RubyAST
25
+ include RubySyntax
26
+
27
+ EMBEDDED_RUNTIME_SOURCES = %w[
28
+ ../runtime/version.rb
29
+ ../runtime/table_format.rb
30
+ ../runtime/location_span.rb
31
+ ../runtime/cst/kind.rb
32
+ ../runtime/cst/annotation.rb
33
+ ../runtime/cst/green/trivia.rb
34
+ ../runtime/cst/green/token.rb
35
+ ../runtime/cst/green/node.rb
36
+ ../runtime/cst/green/cache.rb
37
+ ../runtime/cst/green/builder.rb
38
+ ../runtime/cst/source_text.rb
39
+ ../runtime/cst/syntax_token.rb
40
+ ../runtime/cst/syntax_node.rb
41
+ ../runtime/cst/cursor.rb
42
+ ../runtime/cst/typed_node.rb
43
+ ../runtime/cst/parse_result.rb
44
+ ../runtime/cst/editing.rb
45
+ ../runtime/cst/text_edit.rb
46
+ ../runtime/cst/rewriter.rb
47
+ ../runtime/cst/editor.rb
48
+ ../runtime/cst/diff.rb
49
+ ../runtime/cst/serialized_tree.rb
50
+ ../runtime/cst/validator.rb
51
+ ../runtime/cst/serialize.rb
52
+ ../runtime/cst/incremental/token_memo.rb
53
+ ../runtime/cst/incremental/relexer.rb
54
+ ../runtime/cst/incremental/parse_memo.rb
55
+ ../runtime/cst/incremental/lexed_syntax.rb
56
+ ../runtime/cst/incremental/blender.rb
57
+ ../runtime/cst/incremental/session.rb
58
+ ../runtime/cst.rb
59
+ ../runtime/ast_data.rb
60
+ ../runtime/resource_limits.rb
61
+ ../runtime/event_sanitizer.rb
62
+ ../runtime/event.rb
63
+ ../runtime/observation.rb
64
+ ../runtime/repair.rb
65
+ ../runtime/repair_priority_queue.rb
66
+ ../runtime/repair_search.rb
67
+ ../runtime/parser_sync_recovery.rb
68
+ ../runtime/parser.rb
69
+ ../runtime/lexer_input.rb
70
+ ../runtime/generated_lexer.rb
71
+ ../runtime/jsonl_tracer.rb
72
+ ../runtime/event_jsonl_tracer.rb
73
+ ../tables/compact.rb
74
+ ../tables/compact_actions.rb
75
+ ../tables/compact_productions.rb
76
+ ].freeze #: Array[String]
77
+ private_constant :EMBEDDED_RUNTIME_SOURCES
78
+
9
79
  # @rbs @automaton: IR::Automaton
10
80
  # @rbs @grammar: IR::Grammar
11
81
  # @rbs @table_format: Symbol
@@ -16,12 +86,18 @@ module Ibex
16
86
  # @rbs @omit_action_call: bool
17
87
  # @rbs @superclass: String
18
88
  # @rbs @executable: String?
89
+ # @rbs @cst_trivia: Symbol
90
+ # @rbs @error_messages: Hash[Integer, String | { id: String, message: String }]
91
+ # @rbs @generated_action_abi: GeneratedActionABI::Cache
92
+ # @rbs @runtime_require: String?
19
93
 
20
94
  # @rbs (IR::Automaton automaton, ?table: Symbol | String, ?embedded: bool, ?line_convert: bool,
21
95
  # ?line_convert_all: bool, ?debug: bool, ?omit_action_call: bool?, ?superclass: String?,
22
- # ?executable: String?) -> void
96
+ # ?executable: String?, ?cst_trivia: Symbol | String, ?runtime_require: String?,
97
+ # ?error_messages: Hash[Integer, String | { id: String, message: String }]) -> void
23
98
  def initialize(automaton, table: :compact, embedded: false, line_convert: true, debug: false,
24
- line_convert_all: false, omit_action_call: nil, superclass: nil, executable: nil)
99
+ line_convert_all: false, omit_action_call: nil, superclass: nil, executable: nil,
100
+ cst_trivia: :leading, runtime_require: "ibex/runtime", error_messages: {})
25
101
  @automaton = automaton
26
102
  @grammar = automaton.grammar
27
103
  @table_format = table.to_sym
@@ -32,6 +108,15 @@ module Ibex
32
108
  @omit_action_call = omit_action_call.nil? ? @grammar.options[:omit_action_call] : omit_action_call
33
109
  @superclass = superclass || @grammar.superclass || "Ibex::Runtime::Parser"
34
110
  @executable = executable
111
+ @cst_trivia = cst_trivia.to_sym
112
+ @generated_action_abi = GeneratedActionABI::Cache.new
113
+ @runtime_require = runtime_require
114
+ @cst_trivia = :leading if @cst_trivia == :attach
115
+ unless %i[leading balanced drop].include?(@cst_trivia)
116
+ raise ArgumentError, "cst_trivia must be :leading, :balanced, or :drop"
117
+ end
118
+
119
+ @error_messages = error_messages.sort.to_h.freeze
35
120
  end
36
121
 
37
122
  # @rbs () -> String
@@ -44,7 +129,13 @@ module Ibex
44
129
  modules, class_name = class_parts
45
130
  modules.each { |name| lines << "module #{name}" }
46
131
  lines << "class #{class_name} < #{@superclass}"
132
+ append_ast(lines)
133
+ append_syntax(lines)
47
134
  append_tables(lines)
135
+ append_parameter_initializer(lines)
136
+ append_entry_methods(lines)
137
+ append_lexer(lines)
138
+ append_value_printers(lines)
48
139
  append_actions(lines)
49
140
  append_user_code(lines, "inner", indent: 2)
50
141
  lines << "end"
@@ -58,11 +149,9 @@ module Ibex
58
149
  # @rbs (Array[String] lines) -> void
59
150
  def append_runtime(lines)
60
151
  if @embedded
61
- lines << embedded_source("../runtime/parser.rb")
62
- lines << embedded_source("../../ibex/tables.rb")
63
- else
64
- lines << 'require "ibex/runtime"'
65
- lines << 'require "ibex/tables"' if @table_format == :compact
152
+ EMBEDDED_RUNTIME_SOURCES.each { |path| lines << embedded_source(path) }
153
+ elsif @runtime_require
154
+ lines << "require #{@runtime_require.inspect}"
66
155
  end
67
156
  lines << ""
68
157
  end
@@ -77,28 +166,134 @@ module Ibex
77
166
  def append_tables(lines)
78
167
  table_set = Tables.build(@automaton, format: @table_format)
79
168
  indent = " "
80
- lines << "#{indent}PARSER_TABLE_FORMAT_VERSION = #{Runtime::PARSER_TABLE_FORMAT_VERSION}"
169
+ append_table_metadata(lines, indent)
81
170
  lines << "#{indent}TOKEN_IDS = #{token_ids_literal}.freeze"
82
171
  lines << "#{indent}TOKEN_NAMES = #{token_names_literal}.freeze"
172
+ append_cst_metadata(lines, indent)
83
173
  lines << "#{indent}ACTIONS = #{table_literal(table_set.actions)}"
84
174
  lines << "#{indent}GOTOS = #{table_literal(table_set.gotos)}"
85
175
  lines << "#{indent}DEFAULT_ACTIONS = #{table_set.default_actions.inspect}.freeze"
176
+ eager_type = "Hash[Integer, [:reduce, Integer]]"
177
+ lines << "#{indent}eager_reductions = #{eager_reductions_literal} # @type var eager_reductions: #{eager_type}"
178
+ lines << "#{indent}EAGER_REDUCTIONS = eager_reductions.freeze #: #{eager_type}"
86
179
  lines << "#{indent}PRODUCTIONS = #{productions_literal}.freeze"
180
+ append_value_printer_table(lines, indent)
181
+ declaration = "#{indent}error_messages = #{error_messages_literal}"
182
+ type = "Hash[Integer, String | { id: String, message: String }]"
183
+ lines << "#{declaration} # @type var error_messages: #{type}"
184
+ lines << "#{indent}ERROR_MESSAGES = error_messages.freeze #: #{type}"
185
+ append_parser_tables(lines, indent, table_set)
186
+ lines << "#{indent}def self.parser_tables = PARSER_TABLES"
187
+ lines << "#{indent}DEBUG_ENABLED = #{@debug}"
188
+ lines << ""
189
+ end
190
+
191
+ # @rbs (Array[String] lines, String indent) -> void
192
+ def append_cst_metadata(lines, indent)
193
+ return unless cst?
194
+
195
+ lines << "#{indent}SYMBOL_NAMES = #{symbol_names_literal}.freeze"
196
+ lines << "#{indent}CST_METADATA = #{deep_frozen_literal(cst_metadata)}"
197
+ lines << "#{indent}CST_KIND_MODEL = Ibex::Runtime::CST::Kind.new("
198
+ lines << "#{indent} CST_METADATA.fetch(:kinds), slots: CST_METADATA.fetch(:slots)"
199
+ lines << "#{indent})"
200
+ end
201
+
202
+ # @rbs (Array[String] lines, String indent, Tables::TableSet table_set) -> void
203
+ def append_parser_tables(lines, indent, table_set)
87
204
  lines << "#{indent}PARSER_TABLES = { format_version: PARSER_TABLE_FORMAT_VERSION,"
205
+ lines << "#{indent} grammar_digest: GRAMMAR_DIGEST,"
206
+ lines << "#{indent} state_count: STATE_COUNT, production_count: PRODUCTION_COUNT,"
207
+ lines << "#{indent} uses_locations: #{uses_locations?},"
208
+ if @table_format == :compact
209
+ default_codes = table_set.default_actions.map { |action| Tables::CompactActions.pack(action) }
210
+ lines << "#{indent} compact_fast_driver: true, " \
211
+ "compact_action_encoding: :signed, compact_default_actions: #{default_codes.inspect}.freeze,"
212
+ end
213
+ lines << "#{indent} cst: CST_METADATA, cst_kinds: CST_KIND_MODEL," if cst?
214
+ lines << "#{indent} exact_expected_tokens: true," if @grammar.mode == :extended
88
215
  lines << "#{indent} tokens: TOKEN_IDS, token_names: TOKEN_NAMES, actions: ACTIONS,"
89
216
  lines << "#{indent} gotos: GOTOS, default_actions: DEFAULT_ACTIONS,"
90
- lines << "#{indent} productions: PRODUCTIONS }.freeze"
91
- lines << "#{indent}def self.parser_tables = PARSER_TABLES"
92
- lines << "#{indent}DEBUG_ENABLED = #{@debug}"
217
+ entries = entry_table_fields
218
+ recovery = recovery_table_fields
219
+ value_printers = @grammar.value_printers.empty? ? "" : " value_printers: VALUE_PRINTERS,"
220
+ lines << "#{indent} productions: PRODUCTIONS,#{entries}#{value_printers} " \
221
+ "#{recovery} eager_reductions: EAGER_REDUCTIONS, error_messages: ERROR_MESSAGES }.freeze"
222
+ return unless shareable_parser_tables?
223
+
224
+ lines << "#{indent}Ractor.make_shareable(PARSER_TABLES) " \
225
+ "if defined?(Ractor) && Ractor.respond_to?(:make_shareable)"
226
+ end
227
+
228
+ # @rbs (Array[String] lines) -> void
229
+ def append_parameter_initializer(lines)
230
+ parameters = @grammar.parser_parameters
231
+ return if parameters.empty?
232
+
233
+ keywords = parameters.map { |parameter| "#{parameter[:name]}:" }.join(", ")
234
+ lines << " IBEX_PARAMETER_INITIALIZER = Module.new do"
235
+ lines << " def initialize(#{keywords}, **_ibex_super_arguments)"
236
+ parameters.each { |parameter| lines << " @#{parameter[:name]} = #{parameter[:name]}" }
237
+ lines << " super(**_ibex_super_arguments)"
238
+ lines << " end"
239
+ lines << " end"
240
+ lines << " prepend IBEX_PARAMETER_INITIALIZER"
241
+ lines << " private_constant :IBEX_PARAMETER_INITIALIZER"
93
242
  lines << ""
94
243
  end
95
244
 
245
+ # Custom conversion expressions can return caller-owned mutable or
246
+ # inherently unshareable objects, so only standard token mappings are
247
+ # safe to freeze transitively.
248
+ # @rbs () -> bool
249
+ def shareable_parser_tables?
250
+ @grammar.conversions.empty?
251
+ end
252
+
253
+ # @rbs () -> Hash[Symbol, untyped]
254
+ def cst_metadata
255
+ @cst_metadata ||= CSTMetadata.new(@grammar, trivia_policy: @cst_trivia).build
256
+ end
257
+
258
+ # @rbs (untyped value) -> String
259
+ def deep_frozen_literal(value)
260
+ case value
261
+ when Array
262
+ "[#{value.map { |item| deep_frozen_literal(item) }.join(', ')}].freeze"
263
+ when Hash
264
+ entries = value.map do |key, item|
265
+ "#{deep_frozen_literal(key)} => #{deep_frozen_literal(item)}"
266
+ end
267
+ "{ #{entries.join(', ')} }.freeze"
268
+ when String
269
+ "#{value.inspect}.freeze"
270
+ else
271
+ value.inspect
272
+ end
273
+ end
274
+
96
275
  # @rbs (untyped table) -> String
97
276
  def table_literal(table)
277
+ if table.is_a?(Tables::CompactActions)
278
+ return "Ibex::Tables::CompactActions.packed(#{packed_integers_literal(table.offsets)}, " \
279
+ "#{packed_signed_integers_literal(table.codes)}, #{packed_integers_literal(table.checks)}, " \
280
+ "row_count: #{table.row_count}, encoding: :signed, column_count: #{table.column_count})"
281
+ end
98
282
  return "#{table.inspect}.freeze" unless table.is_a?(Tables::Compact)
99
283
 
100
- "Ibex::Tables::Compact.new(offsets: #{table.offsets.inspect}, values: #{table.values.inspect}, " \
101
- "checks: #{table.checks.inspect}, row_count: #{table.row_count})"
284
+ "Ibex::Tables::Compact.packed(#{packed_integers_literal(table.offsets)}, " \
285
+ "#{packed_integers_literal(table.values)}, #{packed_integers_literal(table.checks)}, " \
286
+ "row_count: #{table.row_count}, dense_width: #{table.dense_width})"
287
+ end
288
+
289
+ # @rbs (Array[Integer?] values) -> String
290
+ def packed_integers_literal(values)
291
+ Tables::PackedIntegers.encode(values).inspect
292
+ end
293
+
294
+ # @rbs (Array[Integer?] values) -> String
295
+ def packed_signed_integers_literal(values)
296
+ Tables::PackedIntegers.encode_signed(values).inspect
102
297
  end
103
298
 
104
299
  # @rbs () -> String
@@ -122,77 +317,156 @@ module Ibex
122
317
 
123
318
  # @rbs () -> String
124
319
  def token_names_literal
125
- entries = @grammar.terminals.map { |terminal| "#{terminal.id} => #{terminal.name.inspect}" }
320
+ entries = @grammar.terminals.map do |terminal|
321
+ "#{terminal.id} => #{(terminal.display_name || terminal.name).inspect}"
322
+ end
323
+ "{ #{entries.join(', ')} }"
324
+ end
325
+
326
+ # @rbs () -> String
327
+ def symbol_names_literal
328
+ entries = @grammar.symbols.map { |symbol| "#{symbol.id} => #{symbol.name.inspect}" }
126
329
  "{ #{entries.join(', ')} }"
127
330
  end
128
331
 
129
332
  # @rbs () -> String
130
333
  def productions_literal
334
+ return compact_productions_literal if @table_format == :compact
335
+
336
+ plain_productions_literal
337
+ end
338
+
339
+ # @rbs () -> String
340
+ def plain_productions_literal
131
341
  entries = @grammar.productions.map do |production|
132
- action = action_method?(production) ? ":_ibex_action_#{production.id}" : "nil"
133
- "{ lhs: #{production.lhs}, length: #{production.rhs.length}, action: #{action} }"
342
+ generated_action = action_method?(production)
343
+ action = generated_action ? ":_ibex_action_#{production.id}" : "nil"
344
+ action_abi = production_action_abi_literal(production, generated_action)
345
+ composition_action = composed_action?(production) ? ", composition_action: true" : ""
346
+ cst_rhs = cst? ? ", rhs: #{production.rhs.inspect}.freeze" : ""
347
+ location_context, named_locations = production_location_metadata(production.action)
348
+ "{ lhs: #{production.lhs}, length: #{production.rhs.length}, action: #{action}" \
349
+ "#{action_abi}#{composition_action}#{cst_rhs}#{location_context}#{named_locations} }"
134
350
  end
135
351
  "[#{entries.join(', ')}]"
136
352
  end
137
353
 
138
- # @rbs (Array[String] lines) -> void
139
- def append_actions(lines)
140
- @grammar.productions.each do |production|
141
- next unless action_method?(production)
354
+ # @rbs (IR::Production production, bool generated_action) -> String
355
+ def production_action_abi_literal(production, generated_action)
356
+ return "" unless generated_action
357
+ return ", location_action: true" unless @generated_action_abi.values_only?(production)
358
+ return ", positional_action: true" if @generated_action_abi.positional_values?(production)
359
+
360
+ borrowed = if @generated_action_abi.borrowed_values?(production)
361
+ ", borrowed_values_action: true"
362
+ else
363
+ ""
364
+ end
365
+ ", values_action: true#{borrowed}"
366
+ end
367
+
368
+ # @rbs () -> String
369
+ def compact_productions_literal
370
+ lhs_ids = @grammar.productions.map(&:lhs)
371
+ lengths = @grammar.productions.map { |production| production.rhs.length }
372
+ flags = @grammar.productions.map { |production| compact_production_flags(production) }
373
+ metadata = compact_production_metadata_literal
374
+ "Ibex::Tables::CompactProductions.packed(#{packed_integers_literal(lhs_ids)}, " \
375
+ "#{packed_integers_literal(lengths)}, #{packed_integers_literal(flags)}, metadata: #{metadata})"
376
+ end
377
+
378
+ # @rbs (IR::Production production) -> Integer
379
+ def compact_production_flags(production)
380
+ return 0 unless action_method?(production)
142
381
 
143
- append_action_constant(lines, production)
144
- append_action_method(lines, production)
382
+ value = 0
383
+ if @generated_action_abi.positional_values?(production)
384
+ value |= Tables::CompactProductions::POSITIONAL_ACTION
385
+ elsif @generated_action_abi.values_only?(production)
386
+ value |= Tables::CompactProductions::VALUES_ACTION
387
+ value |= Tables::CompactProductions::BORROWED_VALUES_ACTION if
388
+ @generated_action_abi.borrowed_values?(production)
389
+ else
390
+ value |= Tables::CompactProductions::LOCATION_ACTION
145
391
  end
392
+ value |= Tables::CompactProductions::COMPOSITION_ACTION if composed_action?(production)
393
+ value
146
394
  end
147
395
 
148
- # @rbs (Array[String] lines, IR::Production production) -> void
149
- def append_action_constant(lines, production)
150
- return unless production.action && @line_convert
396
+ # @rbs () -> String
397
+ def compact_production_metadata_literal
398
+ entries = @grammar.productions.filter_map do |production|
399
+ fields = [] #: Array[String]
400
+ fields << "rhs: #{production.rhs.inspect}.freeze" if cst?
401
+ action = production.action
402
+ if action
403
+ fields << "location_context_length: #{action.context_length}" if action.context_length.positive?
404
+ names = action.named_refs.to_h { |reference| [reference[:name].to_sym, reference[:index]] }
405
+ fields << "location_names: #{names.inspect}.freeze" unless names.empty?
406
+ end
407
+ next if fields.empty?
151
408
 
152
- lines << " ACTION_CODE_#{production.id} = #{production.action.code.inspect}.freeze"
409
+ "#{production.id} => { #{fields.join(', ')} }.freeze"
410
+ end
411
+ entries.empty? ? "Hash.new.freeze" : "{ #{entries.join(', ')} }.freeze"
153
412
  end
154
413
 
155
- # @rbs (Array[String] lines, IR::Production production) -> void
156
- def append_action_method(lines, production)
157
- action = production.action
158
- lines << " private def _ibex_action_#{production.id}(val, _values)"
159
- lines << " val = _values.last(#{action.context_length})" if action&.context_length&.positive?
160
- append_named_bindings(lines, action)
161
- if action
162
- append_semantic_code(lines, production)
163
- else
164
- lines << " val[0]"
414
+ # Reductions that are the sole non-error behavior of a state may run
415
+ # before requesting another lexer token. This enables parser actions to
416
+ # select lexer_state without speculative lookahead.
417
+ # @rbs () -> String
418
+ def eager_reductions_literal
419
+ return "{}" unless @grammar.lexer
420
+
421
+ entries = @automaton.states.filter_map do |state|
422
+ action = eager_lexer_feedback_reduction(state)
423
+ next unless action
424
+
425
+ "#{state.id} => [:reduce, #{action.fetch(:production)}].freeze"
165
426
  end
166
- lines << " end"
167
- lines << ""
427
+ "{ #{entries.join(', ')} }"
168
428
  end
169
429
 
170
- # @rbs (Array[String] lines, IR::Action? action) -> void
171
- def append_named_bindings(lines, action)
172
- return unless action&.named_refs&.any?
430
+ # @rbs (IR::AutomatonState state) -> IR::reduce_action?
431
+ def eager_lexer_feedback_reduction(state)
432
+ candidates = state.actions.values
433
+ candidates += [state.default_action] if state.default_action
434
+ actions = candidates.reject { |action| action[:type] == :error }.uniq
435
+ return unless actions.one?
436
+ return unless actions.first[:type] == :reduce
173
437
 
174
- action.named_refs.each { |reference| lines << " #{reference[:name]} = val[#{reference[:index]}]" }
175
- names = action.named_refs.map { |reference| reference[:name] }
176
- lines << " _ibex_named_values = [#{names.join(', ')}]"
438
+ reduction = actions.first #: IR::reduce_action
439
+ production = @grammar.productions.fetch(reduction.fetch(:production))
440
+ reduction if production.action&.code&.match?(/\blexer_state\s*=/)
177
441
  end
178
442
 
179
- # @rbs (Array[String] lines, IR::Production production) -> void
180
- def append_semantic_code(lines, production)
181
- action = production.action || raise(Ibex::Error, "missing semantic action")
182
- lines << " result = val[0]" if @grammar.options[:result_var]
183
- if @line_convert
184
- evaluation = "eval(ACTION_CODE_#{production.id}, binding, #{action.location[:file].inspect}, " \
185
- "#{action.location[:line]})"
186
- lines << " #{@grammar.options[:result_var] ? evaluation : "return #{evaluation}"}"
187
- else
188
- action.code.lines.each { |line| lines << " #{line.rstrip}" }
443
+ # @rbs (IR::Action? action) -> [String, String]
444
+ def production_location_metadata(action)
445
+ return ["", ""] unless action
446
+
447
+ context = action.context_length.positive? ? ", location_context_length: #{action.context_length}" : ""
448
+ names = action.named_refs.to_h { |reference| [reference[:name].to_sym, reference[:index]] }
449
+ named = names.empty? ? "" : ", location_names: #{names.inspect}.freeze"
450
+ [context, named]
451
+ end
452
+
453
+ # @rbs () -> bool
454
+ def uses_locations?
455
+ cst? || @grammar.productions.any? do |production|
456
+ action = production.action
457
+ action && (!action.composition.nil? || action_references_locations?(production, action))
189
458
  end
190
- lines << " result" if @grammar.options[:result_var]
191
459
  end
192
460
 
193
- # @rbs (IR::Production production) -> bool
194
- def action_method?(production)
195
- !!(production.action || !@omit_action_call)
461
+ # @rbs (IR::Production production, IR::Action action) -> bool
462
+ def action_references_locations?(production, action)
463
+ maximum = action.context_length.positive? ? action.context_length : production.rhs.length
464
+ ActionLocations.new(action.code, maximum: maximum, location: action.location).references?
465
+ end
466
+
467
+ # @rbs () -> bool
468
+ def cst?
469
+ @grammar.options[:cst] == true
196
470
  end
197
471
 
198
472
  # @rbs (Array[String] lines, String name, ?indent: Integer) -> void
@@ -231,5 +505,6 @@ module Ibex
231
505
  @automaton.grammar_digest.delete_prefix("sha256:").chars.first(12).join
232
506
  end
233
507
  end
508
+ # rubocop:enable Metrics/ClassLength
234
509
  end
235
510
  end