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
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Static validation and collection for rules eliminated by inline expansion.
5
+ module NormalizeInlineValidation
6
+ private
7
+
8
+ # @rbs () -> void
9
+ def gather_inline_rules
10
+ # @type self: Normalizer
11
+ @inline_rule_names = Set.new #: Set[String]
12
+ markings = {} #: Hash[String, bool]
13
+ @ast.rules.each do |rule|
14
+ previous = markings[rule.lhs]
15
+ if !previous.nil? && previous != rule.inline
16
+ fail_at(rule.loc, "rule #{rule.lhs} has both inline and ordinary definitions")
17
+ end
18
+ markings[rule.lhs] = rule.inline
19
+ @inline_rule_names << rule.lhs if rule.inline
20
+ end
21
+ validate_inline_terminal_collisions
22
+ validate_inline_start
23
+ validate_inline_cycles
24
+ end
25
+
26
+ # @rbs () -> void
27
+ def validate_inline_terminal_collisions
28
+ # @type self: Normalizer
29
+ @ast.rules.each do |rule|
30
+ next unless rule.inline
31
+ next unless @declared_tokens.key?(rule.lhs) || @precedence.key?(rule.lhs)
32
+
33
+ fail_at(rule.loc, "inline rule #{rule.lhs} collides with terminal #{rule.lhs}")
34
+ end
35
+ end
36
+
37
+ # @rbs () -> void
38
+ def validate_inline_start
39
+ # @type self: Normalizer
40
+ inline_start = @explicit_starts&.find { |name| @inline_rule_names.include?(name) }
41
+ return unless inline_start
42
+
43
+ fail_at(@start_location || @ast.loc, "inline rule #{inline_start} cannot be the start symbol")
44
+ end
45
+
46
+ # @rbs () -> void
47
+ def validate_inline_cycles
48
+ # @type self: Normalizer
49
+ names = @ast.rules.to_set(&:lhs)
50
+ liveness = parameter_formal_liveness
51
+ graph = Hash.new { |hash, name| hash[name] = [] } #: Hash[String, Array[[String, Frontend::Location]]]
52
+ @ast.rules.each do |rule|
53
+ rule.alternatives.each do |alternative|
54
+ alternative.items.each do |item|
55
+ collect_inline_edges(item, rule.parameters, names, graph[rule.lhs], liveness)
56
+ end
57
+ end
58
+ end
59
+ states = {} #: Hash[String, Symbol]
60
+ names.each { |name| visit_inline_cycle(name, graph, states) unless states.key?(name) }
61
+ end
62
+
63
+ # A formal is live for cycle analysis only when substituting its actual can
64
+ # introduce a grammar reference. Calls propagate liveness through their
65
+ # callee's live positions until the finite formal set reaches a fixed point.
66
+ # @rbs () -> Hash[String, Set[Integer]]
67
+ def parameter_formal_liveness
68
+ # @type self: Normalizer
69
+ liveness = @parameter_formals.to_h { |name, _formals| [name, Set.new] } #: Hash[String, Set[Integer]]
70
+ loop do
71
+ changed = false
72
+ @parameter_templates.each do |name, rules|
73
+ formals = @parameter_formals.fetch(name)
74
+ found = Set.new #: Set[Integer]
75
+ rules.each do |rule|
76
+ rule.alternatives.each do |alternative|
77
+ alternative.items.each { |item| collect_live_formals(item, formals, liveness, found) }
78
+ end
79
+ end
80
+ additions = found - liveness.fetch(name)
81
+ next if additions.empty?
82
+
83
+ liveness.fetch(name).merge(additions)
84
+ changed = true
85
+ end
86
+ return liveness unless changed
87
+ end
88
+ end
89
+
90
+ # @rbs (Frontend::AST::item item, Array[String] formals,
91
+ # Hash[String, Set[Integer]] liveness, Set[Integer] found) -> void
92
+ def collect_live_formals(item, formals, liveness, found)
93
+ # @type self: Normalizer
94
+ case item
95
+ when Frontend::AST::SymbolReference
96
+ index = formals.index(item.name)
97
+ found << index if index
98
+ when Frontend::AST::ParameterizedReference
99
+ liveness.fetch(item.name, Set.new).each do |index|
100
+ collect_live_formals(item.arguments.fetch(index), formals, liveness, found)
101
+ end
102
+ when Frontend::AST::Group
103
+ item.alternatives.flatten.each { |child| collect_live_formals(child, formals, liveness, found) }
104
+ when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
105
+ collect_live_formals(item.item, formals, liveness, found)
106
+ when Frontend::AST::SeparatedList
107
+ collect_live_formals(item.item, formals, liveness, found)
108
+ collect_live_formals(item.separator, formals, liveness, found)
109
+ end
110
+ end
111
+
112
+ # Structural AST traversal necessarily branches once for each public item shape.
113
+ # rubocop:disable Metrics/CyclomaticComplexity
114
+ # @rbs (Frontend::AST::item item, Array[String] formals, Set[String] names,
115
+ # Array[[String, Frontend::Location]] edges, Hash[String, Set[Integer]] liveness) -> void
116
+ def collect_inline_edges(item, formals, names, edges, liveness)
117
+ # @type self: Normalizer
118
+ case item
119
+ when Frontend::AST::SymbolReference
120
+ edges << [item.name, item.loc] if !formals.include?(item.name) && names.include?(item.name)
121
+ when Frontend::AST::ParameterizedReference
122
+ edges << [item.name, item.loc] if names.include?(item.name)
123
+ liveness.fetch(item.name, Set.new).each do |index|
124
+ collect_inline_edges(item.arguments.fetch(index), formals, names, edges, liveness)
125
+ end
126
+ when Frontend::AST::Group
127
+ item.alternatives.flatten.each do |child|
128
+ collect_inline_edges(child, formals, names, edges, liveness)
129
+ end
130
+ when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
131
+ collect_inline_edges(item.item, formals, names, edges, liveness)
132
+ when Frontend::AST::SeparatedList
133
+ collect_inline_edges(item.item, formals, names, edges, liveness)
134
+ collect_inline_edges(item.separator, formals, names, edges, liveness)
135
+ end
136
+ end
137
+ # rubocop:enable Metrics/CyclomaticComplexity
138
+
139
+ # @rbs (String name, Hash[String, Array[[String, Frontend::Location]]] graph,
140
+ # Hash[String, Symbol] states) -> void
141
+ def visit_inline_cycle(name, graph, states)
142
+ # @type self: Normalizer
143
+ states[name] = :visiting
144
+ path = [name] #: Array[String]
145
+ positions = { name => 0 } #: Hash[String, Integer]
146
+ worklist = [[name, 0]] #: Array[[String, Integer]]
147
+ until worklist.empty?
148
+ current, edge_index = worklist.fetch(-1)
149
+ edges = graph[current]
150
+ if edge_index >= edges.length
151
+ worklist.pop
152
+ positions.delete(path.pop || raise(Ibex::Error, "internal inline cycle path underflow"))
153
+ states[current] = :visited
154
+ next
155
+ end
156
+
157
+ worklist[-1] = [current, edge_index + 1]
158
+ target, location = edges.fetch(edge_index)
159
+ if states[target] == :visiting
160
+ cycle = path.drop(positions.fetch(target))
161
+ next unless cycle.any? { |entry| @inline_rule_names.include?(entry) }
162
+
163
+ fail_at(location, "inline expansion cycle: #{(cycle + [target]).join(' -> ')}")
164
+ end
165
+ next if states.key?(target)
166
+
167
+ states[target] = :visiting
168
+ positions[target] = path.length
169
+ path << target
170
+ worklist << [target, 0]
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Lexer declaration normalization and static safety checks.
5
+ module NormalizeLexer
6
+ private
7
+
8
+ # @rbs () -> IR::Lexer?
9
+ def normalize_lexer
10
+ # @type self: Normalizer
11
+ declaration = @lexer_declaration
12
+ return unless declaration
13
+
14
+ states = ["INITIAL"] #: Array[String]
15
+ rules = [] #: Array[IR::LexerRule]
16
+ warnings = [] #: Array[Hash[Symbol, untyped]]
17
+ declaration.definitions.each do |definition|
18
+ if definition.is_a?(Frontend::AST::LexerState)
19
+ normalize_lexer_state(definition, states, rules, warnings)
20
+ else
21
+ rules << normalize_lexer_rule(definition, "INITIAL", rules.length, warnings)
22
+ end
23
+ end
24
+ fail_at(declaration.loc, "lexer declaration requires at least one rule") if rules.empty?
25
+ IR::Lexer.new(
26
+ states: states, rules: rules, warnings: warnings,
27
+ source_provenance: { file: declaration.loc.file, root: @resolution&.root_directory, byte_span: nil }
28
+ )
29
+ end
30
+
31
+ # @rbs (Frontend::AST::LexerState definition, Array[String] states, Array[IR::LexerRule] rules,
32
+ # Array[Hash[Symbol, untyped]] warnings) -> void
33
+ def normalize_lexer_state(definition, states, rules, warnings)
34
+ # @type self: Normalizer
35
+ fail_at(definition.loc, "lexer state INITIAL is reserved") if definition.name == "INITIAL"
36
+ fail_at(definition.loc, "duplicate lexer state #{definition.name}") if states.include?(definition.name)
37
+ states << definition.name
38
+ definition.definitions.each do |entry|
39
+ fail_at(entry.loc, "nested lexer states are not supported") if entry.is_a?(Frontend::AST::LexerState)
40
+
41
+ rules << normalize_lexer_rule(entry, definition.name, rules.length, warnings)
42
+ end
43
+ end
44
+
45
+ # @rbs (Frontend::AST::LexerRule definition, String state, Integer id,
46
+ # Array[Hash[Symbol, untyped]] warnings) -> IR::LexerRule
47
+ def normalize_lexer_rule(definition, state, id, warnings)
48
+ # @type self: Normalizer
49
+ validate_lexer_token(definition)
50
+ validate_lexer_action(definition)
51
+ source, options = normalize_lexer_pattern(definition)
52
+ validate_lexer_regexp(definition, source, options)
53
+ if risky_lexer_pattern?(source)
54
+ warning = { type: :lexer_redos, loc: definition.loc.to_h } #: IR::grammar_warning
55
+ warning[:symbol] = definition.token if definition.token
56
+ @warnings << warning
57
+ warnings << { type: :redos, rule: id, loc: definition.loc.to_h }
58
+ end
59
+ IR::LexerRule.new(
60
+ id: id, state: state, kind: definition.kind, token: definition.token,
61
+ pattern: source, pattern_kind: definition.pattern_kind, options: options,
62
+ action: definition.action, location: definition.loc.to_h
63
+ )
64
+ end
65
+
66
+ # @rbs (Frontend::AST::LexerRule definition) -> void
67
+ def validate_lexer_token(definition)
68
+ # @type self: Normalizer
69
+ return unless definition.kind == :token
70
+ return if definition.token && @declared_tokens.key?(definition.token)
71
+
72
+ fail_at(definition.loc, "lexer rule references undeclared terminal #{definition.token}")
73
+ end
74
+
75
+ # @rbs (Frontend::AST::LexerRule definition) -> void
76
+ def validate_lexer_action(definition)
77
+ # @type self: Normalizer
78
+ action = definition.action
79
+ return unless action&.lstrip&.start_with?("|")
80
+
81
+ match = action.match(/\A\s*\|([a-z_][a-zA-Z0-9_]*)\|/m)
82
+ fail_at(definition.loc, "lexer action accepts exactly one local identifier") unless match
83
+ name = match[1]
84
+ fail_at(definition.loc, "lexer action parameter #{name.inspect} is a Ruby keyword") if
85
+ Normalizer::RUBY_KEYWORDS.include?(name)
86
+ end
87
+
88
+ # @rbs (Frontend::AST::LexerRule definition) -> [String, String]
89
+ def normalize_lexer_pattern(definition)
90
+ # @type self: Normalizer
91
+ raw = definition.pattern
92
+ if definition.pattern_kind == :literal
93
+ decoded = decode_lexer_literal(raw, definition.loc)
94
+ fail_at(definition.loc, "lexer pattern must not be empty") if decoded.empty?
95
+ return [Regexp.escape(decoded), ""]
96
+ end
97
+
98
+ closing = raw.rindex("/")
99
+ fail_at(definition.loc, "invalid lexer regular expression") unless closing&.positive?
100
+ [raw[1...closing] || "", raw[(closing + 1)..] || ""]
101
+ end
102
+
103
+ # @rbs (String raw, Frontend::Location location) -> String
104
+ def decode_lexer_literal(raw, location)
105
+ # @type self: Normalizer
106
+ return raw.undump if raw.start_with?('"')
107
+
108
+ (raw[1...-1] || "").gsub("\\'", "'").gsub("\\\\", "\\")
109
+ rescue RuntimeError => e
110
+ fail_at(location, "invalid lexer literal: #{e.message}")
111
+ end
112
+
113
+ # @rbs (Frontend::AST::LexerRule definition, String source, String options) -> void
114
+ def validate_lexer_regexp(definition, source, options)
115
+ # @type self: Normalizer
116
+ flags = 0
117
+ flags |= Regexp::IGNORECASE if options.include?("i")
118
+ flags |= Regexp::MULTILINE if options.include?("m")
119
+ flags |= Regexp::EXTENDED if options.include?("x")
120
+ regexp = Regexp.new("\\A(?:#{source})", flags)
121
+ fail_at(definition.loc, "lexer pattern must not match an empty string") if regexp.match?("")
122
+ rescue RegexpError => e
123
+ fail_at(definition.loc, "invalid lexer regular expression: #{e.message}")
124
+ end
125
+
126
+ # @rbs (String source) -> bool
127
+ def risky_lexer_pattern?(source)
128
+ source.match?(/\([^)]*[+*][^)]*\)[+*{]/) || source.match?(/\.\*[+*{]/)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Named-reference collection and validation shared by ordinary and parameterized items.
5
+ module NormalizeNamedReferences
6
+ private
7
+
8
+ # @rbs (Frontend::AST::Group group) -> void
9
+ def reject_group_named_references(group)
10
+ # @type self: Normalizer
11
+ reference = group.alternatives.flatten.filter_map { |item| named_reference_in(item) }.first
12
+ fail_at(reference.loc, "named references inside EBNF groups are not supported") if reference
13
+ end
14
+
15
+ # @rbs (Frontend::AST::item item) -> (Frontend::AST::SymbolReference | Frontend::AST::ParameterizedReference)?
16
+ def named_reference_in(item)
17
+ # @type self: Normalizer
18
+ if (item.is_a?(Frontend::AST::SymbolReference) ||
19
+ item.is_a?(Frontend::AST::ParameterizedReference)) && item.named_reference
20
+ return item
21
+ end
22
+ if item.is_a?(Frontend::AST::Group)
23
+ return item.alternatives.flatten.filter_map { |child| named_reference_in(child) }.first
24
+ end
25
+ if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
26
+ item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
27
+ return named_reference_in(item.item)
28
+ end
29
+
30
+ nil
31
+ end
32
+
33
+ # @rbs (Frontend::AST::item item, Array[IR::named_ref] refs, Integer index) -> void
34
+ def add_named_reference(item, refs, index)
35
+ # @type self: Normalizer
36
+ reference = unwrap_reference(item)
37
+ return unless reference
38
+
39
+ name = reference.named_reference
40
+ return unless name
41
+
42
+ fail_at(reference.loc, "reserved named reference #{name}") if Normalizer::RESERVED_NAMES.include?(name)
43
+ fail_at(reference.loc, "duplicate named reference #{name}") if refs.any? { |entry| entry[:name] == name }
44
+ refs << { name: name, index: index }
45
+ end
46
+
47
+ # @rbs (Frontend::AST::item item) -> (Frontend::AST::SymbolReference | Frontend::AST::ParameterizedReference)?
48
+ def unwrap_reference(item)
49
+ # @type self: Normalizer
50
+ return item if item.is_a?(Frontend::AST::SymbolReference) ||
51
+ item.is_a?(Frontend::AST::ParameterizedReference)
52
+ if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
53
+ item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
54
+ return unwrap_reference(item.item)
55
+ end
56
+
57
+ nil
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Validation and normalization for generated AST node declarations.
5
+ module NormalizeNodes
6
+ private
7
+
8
+ # @rbs (Frontend::AST::Alternative alternative, Array[String] rhs) -> IR::node_annotation?
9
+ def normalize_node_annotation(alternative, rhs)
10
+ # @type self: Normalizer
11
+ annotation = alternative.node_annotation
12
+ return unless annotation
13
+
14
+ if alternative.action || alternative.items.any?(Frontend::AST::InlineAction)
15
+ fail_at(annotation.loc, "@node cannot be combined with semantic actions")
16
+ end
17
+ unless annotation.name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
18
+ fail_at(annotation.loc, "@node name #{annotation.name.inspect} must be a Ruby constant identifier")
19
+ end
20
+ validate_node_fields(annotation, rhs)
21
+ previous = @node_shapes[annotation.name]
22
+ if previous && previous != annotation.fields
23
+ fail_at(annotation.loc, "@node #{annotation.name} was already declared with fields (#{previous.join(', ')})")
24
+ end
25
+ @node_shapes[annotation.name] = annotation.fields.dup.freeze
26
+ { name: annotation.name, fields: annotation.fields, loc: annotation.loc.to_h }
27
+ end
28
+
29
+ # @rbs (Frontend::AST::NodeAnnotation annotation, Array[String] rhs) -> void
30
+ def validate_node_fields(annotation, rhs)
31
+ # @type self: Normalizer
32
+ unless annotation.fields.length == rhs.length
33
+ fail_at(
34
+ annotation.loc,
35
+ "@node #{annotation.name} declares #{annotation.fields.length} fields for #{rhs.length} RHS values"
36
+ )
37
+ end
38
+ duplicate = annotation.fields.tally.find { |_name, count| count > 1 }&.first
39
+ fail_at(annotation.loc, "@node field #{duplicate.inspect} is duplicated") if duplicate
40
+ invalid = annotation.fields.find do |field|
41
+ !field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) || Normalizer::RUBY_KEYWORDS.include?(field)
42
+ end
43
+ fail_at(annotation.loc, "@node field #{invalid.inspect} must be a Ruby local identifier") if invalid
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Resumable EBNF lowering used inside specialized parameter alternatives.
5
+ module NormalizeParameterEbnfLowering
6
+ private
7
+
8
+ # @rbs (Hash[Symbol, untyped] frame,
9
+ # Frontend::AST::Optional | Frontend::AST::Star | Frontend::AST::Plus item) -> void
10
+ def lower_parameter_suffix(frame, item)
11
+ frame.fetch(:operations).push([:finish_suffix, item], [:item, item.item])
12
+ end
13
+
14
+ # @rbs (Hash[Symbol, untyped] frame,
15
+ # Frontend::AST::Optional | Frontend::AST::Star | Frontend::AST::Plus item) -> void
16
+ def finish_parameter_suffix(frame, item)
17
+ # @type self: Normalizer
18
+ base = frame.fetch(:values).pop
19
+ helper = case item
20
+ when Frontend::AST::Optional then build_optional(item, base)
21
+ when Frontend::AST::Star then build_star(item, base)
22
+ when Frontend::AST::Plus then build_plus(item, base)
23
+ end
24
+ frame.fetch(:values) << helper
25
+ end
26
+
27
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::SeparatedList item) -> void
28
+ def lower_parameter_separated_list(frame, item)
29
+ frame.fetch(:operations).push(
30
+ [:finish_separated, item], [:item, item.separator], [:item, item.item]
31
+ )
32
+ end
33
+
34
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::SeparatedList item) -> void
35
+ def finish_parameter_separated_list(frame, item)
36
+ # @type self: Normalizer
37
+ separator = frame.fetch(:values).pop
38
+ base = frame.fetch(:values).pop
39
+ frame.fetch(:values) << build_separated_list(item, base, separator)
40
+ end
41
+
42
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::Group item) -> void
43
+ def lower_parameter_group(frame, item)
44
+ # @type self: Normalizer
45
+ reject_group_named_references(item)
46
+ helper = new_helper("group", item.loc)
47
+ operations = frame.fetch(:operations)
48
+ operations << [:value, helper]
49
+ item.alternatives.reverse_each do |alternative|
50
+ operations << [:group_alternative, helper, item, alternative]
51
+ end
52
+ end
53
+
54
+ # @rbs (Hash[Symbol, untyped] frame, String helper, Frontend::AST::Group item,
55
+ # Array[Frontend::AST::item] alternative) -> void
56
+ def start_parameter_group_alternative(frame, helper, item, alternative)
57
+ operations = frame.fetch(:operations)
58
+ operations << [:finish_group_alternative, helper, item, alternative.length]
59
+ alternative.reverse_each { |child| operations << [:item, child] }
60
+ end
61
+
62
+ # @rbs (Hash[Symbol, untyped] frame, String helper, Frontend::AST::Group item, Integer length) -> void
63
+ def finish_parameter_group_alternative(frame, helper, item, length)
64
+ # @type self: Normalizer
65
+ rhs = frame.fetch(:values).pop(length)
66
+ add_group_production(helper, rhs, item)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ # Resumable, source-ordered lowering for specialized parameter alternatives.
5
+ module NormalizeParameterLowering
6
+ private
7
+
8
+ # @rbs (Hash[Symbol, untyped] frame) -> void
9
+ def advance_parameter_alternative(frame)
10
+ # @type self: Normalizer
11
+ with_parameter_frame_context(frame) do
12
+ operations = frame.fetch(:operations)
13
+ if operations.any?
14
+ process_parameter_operation(frame, operations.pop)
15
+ elsif parameter_items_remaining?(frame)
16
+ start_parameter_item(frame)
17
+ else
18
+ finish_parameter_alternative(frame)
19
+ end
20
+ end
21
+ end
22
+
23
+ # @rbs (Hash[Symbol, untyped] frame) { () -> void } -> void
24
+ def with_parameter_frame_context(frame)
25
+ # @type self: Normalizer
26
+ template, = frame.fetch(:current)
27
+ previous_chain = @current_include_chain
28
+ previous_expansion = @current_parameter_expansion
29
+ reference = frame.fetch(:reference)
30
+ @current_include_chain = @resolution&.include_chain_for(template) || []
31
+ @current_parameter_expansion = {
32
+ rule: reference.name, arguments: frame.fetch(:rendered_arguments)
33
+ }
34
+ yield
35
+ ensure
36
+ @current_include_chain = previous_chain
37
+ @current_parameter_expansion = previous_expansion
38
+ end
39
+
40
+ # @rbs (Hash[Symbol, untyped] frame) -> bool
41
+ def parameter_items_remaining?(frame)
42
+ _template, _rule, alternative = frame.fetch(:current)
43
+ frame.fetch(:item_index) < alternative.items.length
44
+ end
45
+
46
+ # @rbs (Hash[Symbol, untyped] frame) -> void
47
+ def start_parameter_item(frame)
48
+ # @type self: Normalizer
49
+ _template, _rule, alternative = frame.fetch(:current)
50
+ index = frame.fetch(:item_index)
51
+ item = alternative.items.fetch(index)
52
+ frame[:item_index] = index + 1
53
+ if item.is_a?(Frontend::AST::InlineAction)
54
+ frame.fetch(:rhs) << expand_inline_action(
55
+ item, frame.fetch(:rhs).length, frame.fetch(:named_refs)
56
+ )
57
+ else
58
+ frame.fetch(:operations).push([:finish_item, item], [:item, item])
59
+ end
60
+ end
61
+
62
+ # @rbs (Hash[Symbol, untyped] frame, Array[untyped] operation) -> void
63
+ def process_parameter_operation(frame, operation)
64
+ # @type self: Normalizer
65
+ kind, *arguments = operation
66
+ case kind
67
+ when :item then lower_parameter_item(frame, arguments.fetch(0))
68
+ when :finish_item then finish_parameter_item(frame, arguments.fetch(0))
69
+ when :value then frame.fetch(:values) << arguments.fetch(0)
70
+ when :finish_suffix then finish_parameter_suffix(frame, arguments.fetch(0))
71
+ when :finish_separated then finish_parameter_separated_list(frame, arguments.fetch(0))
72
+ when :group_alternative
73
+ start_parameter_group_alternative(frame, arguments.fetch(0), arguments.fetch(1), arguments.fetch(2))
74
+ when :finish_group_alternative
75
+ finish_parameter_group_alternative(frame, arguments.fetch(0), arguments.fetch(1), arguments.fetch(2))
76
+ else raise Ibex::Error, "internal parameter lowering operation #{kind.inspect}"
77
+ end
78
+ end
79
+
80
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::item item) -> void
81
+ def lower_parameter_item(frame, item)
82
+ # @type self: Normalizer
83
+ case item
84
+ when Frontend::AST::SymbolReference
85
+ frame.fetch(:values) << symbol_for_reference(item).name
86
+ when Frontend::AST::ParameterizedReference
87
+ lower_parameter_call(frame, item)
88
+ when Frontend::AST::Group
89
+ lower_parameter_group(frame, item)
90
+ when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus
91
+ lower_parameter_suffix(frame, item)
92
+ when Frontend::AST::SeparatedList
93
+ lower_parameter_separated_list(frame, item)
94
+ else
95
+ fail_at(item.loc, "unsupported nested EBNF expression")
96
+ end
97
+ end
98
+
99
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::ParameterizedReference item) -> void
100
+ def lower_parameter_call(frame, item)
101
+ # @type self: Normalizer
102
+ helper, = schedule_parameter_specialization(item)
103
+ frame.fetch(:values) << helper
104
+ end
105
+
106
+ # @rbs (Hash[Symbol, untyped] frame, Frontend::AST::item item) -> void
107
+ def finish_parameter_item(frame, item)
108
+ # @type self: Normalizer
109
+ rhs = frame.fetch(:rhs)
110
+ rhs << frame.fetch(:values).pop
111
+ add_named_reference(item, frame.fetch(:named_refs), rhs.length - 1)
112
+ end
113
+
114
+ # @rbs (Hash[Symbol, untyped] frame) -> void
115
+ def finish_parameter_alternative(frame)
116
+ # @type self: Normalizer
117
+ _template, rule, alternative = frame.fetch(:current)
118
+ action = normalize_action(alternative.action, frame.fetch(:named_refs))
119
+ add_production(
120
+ rule.lhs, frame.fetch(:rhs), action, alternative.precedence,
121
+ { kind: :user, loc: alternative.loc.to_h }, rule.documentation
122
+ )
123
+ clear_parameter_alternative(frame)
124
+ end
125
+ end
126
+ end