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,116 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "rake/tasklib"
5
+ require_relative "cli"
6
+
7
+ # @rbs!
8
+ # module Rake
9
+ # class TaskLib
10
+ # def initialize: () -> void
11
+ # end
12
+ #
13
+ # class Task
14
+ # def self.define_task: (Hash[String | Symbol, String | Array[String]]) -> Task
15
+ # | (Hash[String | Symbol, String | Array[String]]) {
16
+ # (untyped, untyped) -> void
17
+ # } -> Task
18
+ # end
19
+ #
20
+ # class FileTask < Task
21
+ # end
22
+ # end
23
+
24
+ module Ibex
25
+ # Declares grammar-to-parser generation in a Rakefile.
26
+ class RakeTask < Rake::TaskLib
27
+ attr_accessor :grammar #: String?
28
+ attr_accessor :output #: String?
29
+ attr_accessor :options #: Array[String]
30
+ attr_accessor :action_source #: String | true | nil
31
+
32
+ # @rbs (String | Symbol name) ?{ (RakeTask) -> void } -> void
33
+ def initialize(name, &configuration)
34
+ super()
35
+ @name = name
36
+ @grammar = nil
37
+ @output = name.to_s.end_with?(".rb") ? name.to_s : nil
38
+ @options = []
39
+ @action_source = nil
40
+ configuration&.call(self)
41
+ define
42
+ end
43
+
44
+ private
45
+
46
+ # @rbs () -> void
47
+ def define
48
+ source = @grammar || raise(ArgumentError, "grammar is required")
49
+ reject_watch_option!
50
+ target = @output || source.sub(/\.[^.]+\z/, ".rb")
51
+ target = "#{source}.rb" if target == source
52
+ prerequisites = grammar_prerequisites(source)
53
+ action_target = configured_action_source(target)
54
+ arguments = [*@options]
55
+ arguments << (@action_source == true ? "--action-source" : "--action-source=#{action_target}") if action_target
56
+ generate = lambda do |_task, _task_arguments|
57
+ status = CLI.start([*arguments, "--output-file=#{target}", source])
58
+ raise "parser generation failed for #{source}" unless status.zero?
59
+ end
60
+
61
+ if action_target
62
+ Rake::FileTask.define_task(action_target => prerequisites, &generate)
63
+ Rake::FileTask.define_task(target => [*prerequisites, action_target], &generate)
64
+ else
65
+ Rake::FileTask.define_task(target => prerequisites, &generate)
66
+ end
67
+
68
+ Rake::Task.define_task(@name => target) unless @name.to_s == target
69
+ end
70
+
71
+ # @rbs () -> void
72
+ def reject_watch_option!
73
+ return unless @options.any? { |option| option == "--watch" || option.start_with?("--watch=") }
74
+
75
+ raise ArgumentError, "RakeTask options must not include --watch"
76
+ end
77
+
78
+ # @rbs (String target) -> String?
79
+ def configured_action_source(target)
80
+ return nil unless @action_source
81
+
82
+ path = @action_source == true ? default_action_source_path(target) : @action_source
83
+ raise ArgumentError, "action_source must be true or a non-empty String" unless
84
+ path.is_a?(String) && !path.empty?
85
+ if File.expand_path(path) == File.expand_path(target)
86
+ raise ArgumentError, "action_source and output must be distinct"
87
+ end
88
+
89
+ path
90
+ end
91
+
92
+ # @rbs (String target) -> String
93
+ def default_action_source_path(target)
94
+ directory = File.dirname(target)
95
+ basename = File.basename(target)
96
+ replaced = basename.sub(/\.[^.]+\z/, ".actions.rb")
97
+ replaced = "#{basename}.actions.rb" if replaced == basename
98
+ directory == "." ? replaced : File.join(directory, replaced)
99
+ end
100
+
101
+ # @rbs (String source) -> Array[String]
102
+ def grammar_prerequisites(source)
103
+ dependencies = Frontend::Resolver.new(source, mode: configured_mode).dependencies
104
+ [source, *dependencies.drop(1)]
105
+ end
106
+
107
+ # @rbs () -> Symbol
108
+ def configured_mode
109
+ explicit = @options.find { |option| option.start_with?("--mode=") }
110
+ return explicit.delete_prefix("--mode=").to_sym if explicit
111
+
112
+ index = @options.index("--mode")
113
+ index ? @options.fetch(index + 1, "default").to_sym : :default
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ # Generates bounded terminal sentences from Grammar IR.
6
+ class Samples
7
+ DEFAULT_MAX_EXPANSIONS = 100_000 #: Integer
8
+
9
+ # @rbs (IR::Grammar grammar, ?seed: Integer, ?max_tokens: Integer, ?max_depth: Integer,
10
+ # ?max_expansions: Integer) -> void
11
+ def initialize(grammar, seed: 0, max_tokens: 32, max_depth: 16, max_expansions: DEFAULT_MAX_EXPANSIONS)
12
+ raise ArgumentError, "max_tokens must be positive" unless max_tokens.positive?
13
+ raise ArgumentError, "max_depth must be positive" unless max_depth.positive?
14
+ raise ArgumentError, "max_expansions must be positive" unless max_expansions.positive?
15
+
16
+ @grammar = grammar
17
+ @random = Random.new(seed)
18
+ @max_tokens = max_tokens
19
+ @max_depth = max_depth
20
+ @max_expansions = max_expansions
21
+ @productions = grammar.productions.group_by(&:lhs)
22
+ @minimum_costs = compute_minimum_costs
23
+ @minimum_heights = compute_minimum_heights
24
+ end
25
+
26
+ # @rbs (?count: Integer) -> Array[Array[String]]
27
+ def generate(count: 1)
28
+ raise ArgumentError, "count must be positive" unless count.positive?
29
+
30
+ start = @grammar.symbol(@grammar.start) || raise(Ibex::Error, "(samples):1:1: missing start symbol")
31
+ minimum = @minimum_costs.fetch(start.id, nil)
32
+ raise Ibex::Error, "(samples):1:1: start symbol #{@grammar.start} derives no terminal sentence" unless minimum
33
+
34
+ if minimum > @max_tokens
35
+ raise Ibex::Error, "(samples):1:1: minimum sentence needs #{minimum} tokens; limit is #{@max_tokens}"
36
+ end
37
+ if count > @max_expansions
38
+ raise Ibex::Error, "(samples):1:1: count #{count} exceeds expansion limit #{@max_expansions}"
39
+ end
40
+
41
+ remaining_expansions = @max_expansions
42
+ Array.new(count) do
43
+ sample, remaining_expansions = expand(start, remaining_expansions)
44
+ sample
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # @rbs () -> Hash[Integer, Integer?]
51
+ def compute_minimum_costs
52
+ compute_minimum_values(1) do |production, values|
53
+ rhs_values = resolved_rhs_values(production, values)
54
+ rhs_values&.sum
55
+ end
56
+ end
57
+
58
+ # @rbs () -> Hash[Integer, Integer?]
59
+ def compute_minimum_heights
60
+ compute_minimum_values(0) do |production, values|
61
+ rhs_values = resolved_rhs_values(production, values)
62
+ rhs_values && ((rhs_values.max || 0) + 1)
63
+ end
64
+ end
65
+
66
+ # @rbs (Integer terminal_value) { (IR::Production, Hash[Integer, Integer?]) -> Integer? }
67
+ # -> Hash[Integer, Integer?]
68
+ def compute_minimum_values(terminal_value)
69
+ values = @grammar.symbols.to_h { |symbol| [symbol.id, nil] } #: Hash[Integer, Integer?]
70
+ dependents = {} #: Hash[Integer, Array[IR::Production]]
71
+ queue = seed_terminal_values(values, terminal_value)
72
+
73
+ @grammar.productions.each do |production|
74
+ update_minimum(values, queue, production.lhs, yield(production, values)) if production.rhs.empty?
75
+ production.rhs.uniq.each do |symbol_id|
76
+ (dependents[symbol_id] ||= []) << production
77
+ end
78
+ end
79
+
80
+ index = 0
81
+ while index < queue.length
82
+ dependents.fetch(queue.fetch(index), []).each do |production|
83
+ update_minimum(values, queue, production.lhs, yield(production, values))
84
+ end
85
+ index += 1
86
+ end
87
+
88
+ values
89
+ end
90
+
91
+ # @rbs (Hash[Integer, Integer?] values, Integer terminal_value) -> Array[Integer]
92
+ def seed_terminal_values(values, terminal_value)
93
+ @grammar.symbols.filter_map do |symbol|
94
+ next unless symbol.terminal? && !symbol.reserved
95
+
96
+ values[symbol.id] = terminal_value
97
+ symbol.id
98
+ end
99
+ end
100
+
101
+ # @rbs (Hash[Integer, Integer?] values, Array[Integer] queue, Integer symbol_id, Integer? candidate) -> void
102
+ def update_minimum(values, queue, symbol_id, candidate)
103
+ return unless candidate
104
+
105
+ current = values.fetch(symbol_id)
106
+ return if current && current <= candidate
107
+
108
+ values[symbol_id] = candidate
109
+ queue << symbol_id
110
+ end
111
+
112
+ # @rbs (IR::Production production, Hash[Integer, Integer?] values) -> Array[Integer]?
113
+ def resolved_rhs_values(production, values)
114
+ rhs_values = production.rhs.map { |symbol_id| values.fetch(symbol_id) }
115
+ return nil if rhs_values.any?(&:nil?)
116
+
117
+ rhs_values.compact
118
+ end
119
+
120
+ # @rbs (IR::GrammarSymbol start, Integer remaining_expansions) -> [Array[String], Integer]
121
+ def expand(start, remaining_expansions)
122
+ result = [] #: Array[String]
123
+ work = [[start.id, 0]] #: Array[[Integer, Integer]]
124
+ pending_minimum = minimum_cost!(start.id)
125
+
126
+ while (entry = work.pop)
127
+ if remaining_expansions.zero?
128
+ raise Ibex::Error, "(samples):1:1: expansion limit of #{@max_expansions} steps exceeded"
129
+ end
130
+
131
+ remaining_expansions -= 1
132
+ symbol_id, depth = entry
133
+ pending_minimum -= minimum_cost!(symbol_id)
134
+ symbol = @grammar.symbol_by_id(symbol_id) ||
135
+ raise(Ibex::Error, "(samples):1:1: missing symbol #{symbol_id}")
136
+ if symbol.terminal?
137
+ result << symbol.name
138
+ next
139
+ end
140
+
141
+ budget = @max_tokens - result.length - pending_minimum
142
+ production = choose_production(symbol_id, budget, depth)
143
+ pending_minimum += production_cost(production) ||
144
+ raise(Ibex::Error, "(samples):1:1: no bounded derivation for symbol #{symbol_id}")
145
+ production.rhs.reverse_each { |child_id| work << [child_id, depth + 1] }
146
+ end
147
+
148
+ [result, remaining_expansions]
149
+ end
150
+
151
+ # @rbs (Integer nonterminal_id, Integer budget, Integer depth) -> IR::Production
152
+ def choose_production(nonterminal_id, budget, depth)
153
+ productions = @productions[nonterminal_id]
154
+ raise Ibex::Error, "(samples):1:1: no bounded derivation for symbol #{nonterminal_id}" unless productions
155
+
156
+ candidates = productions.select do |production|
157
+ cost = production_cost(production)
158
+ cost && cost <= budget
159
+ end
160
+ raise Ibex::Error, "(samples):1:1: no bounded derivation for symbol #{nonterminal_id}" if candidates.empty?
161
+
162
+ if depth >= @max_depth
163
+ minimum = candidates.filter_map { |production| production_height(production) }.min
164
+ candidates = candidates.select { |production| production_height(production) == minimum }
165
+ end
166
+ candidates.fetch(@random.rand(candidates.length))
167
+ end
168
+
169
+ # @rbs (Integer symbol_id) -> Integer
170
+ def minimum_cost!(symbol_id)
171
+ @minimum_costs.fetch(symbol_id) ||
172
+ raise(Ibex::Error, "(samples):1:1: no bounded derivation for symbol #{symbol_id}")
173
+ end
174
+
175
+ # @rbs (IR::Production production) -> Integer?
176
+ def production_cost(production)
177
+ resolved_rhs_values(production, @minimum_costs)&.sum
178
+ end
179
+
180
+ # @rbs (IR::Production production) -> Integer?
181
+ def production_height(production)
182
+ child_heights = resolved_rhs_values(production, @minimum_heights)
183
+ child_heights && ((child_heights.max || 0) + 1)
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+
6
+ module Ibex
7
+ module TableSimulation
8
+ # Final immutable simulation document.
9
+ class Result
10
+ IDENTIFIER = "table-simulation" #: String
11
+ SCHEMA_VERSION = 1 #: Integer
12
+
13
+ attr_reader :grammar_digest #: String
14
+ attr_reader :algorithm #: String
15
+ attr_reader :tokens #: Array[String]
16
+ attr_reader :status #: Symbol
17
+ attr_reader :steps #: Array[Step]
18
+
19
+ # @rbs (grammar_digest: String, algorithm: String, tokens: Array[String], status: Symbol,
20
+ # steps: Array[Step]) -> void
21
+ def initialize(grammar_digest:, algorithm:, tokens:, status:, steps:)
22
+ raise ArgumentError, "simulation status must be accepted or error" unless %i[accepted error].include?(status)
23
+
24
+ @grammar_digest = grammar_digest.dup.freeze
25
+ @algorithm = algorithm.dup.freeze
26
+ @tokens = tokens.map { |token| token.dup.freeze }.freeze
27
+ @status = status
28
+ @steps = steps.dup.freeze
29
+ freeze
30
+ end
31
+
32
+ # @rbs () -> Hash[String, untyped]
33
+ def to_h
34
+ {
35
+ "ibex_table_simulation" => IDENTIFIER,
36
+ "schema_version" => SCHEMA_VERSION,
37
+ "grammar_digest" => @grammar_digest,
38
+ "algorithm" => @algorithm,
39
+ "tokens" => @tokens,
40
+ "status" => @status.to_s,
41
+ "steps" => @steps.map(&:to_h)
42
+ }
43
+ end
44
+
45
+ # @rbs (*untyped) -> String
46
+ def to_json(*)
47
+ "#{JSON.pretty_generate(to_h)}\n"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "../error"
5
+
6
+ module Ibex
7
+ module TableSimulation
8
+ # Deterministic, semantic-action-free table interpreter.
9
+ class Simulator
10
+ DEFAULT_MAX_STEPS = 100_000 #: Integer
11
+ DEFAULT_MAX_STACK = 10_000 #: Integer
12
+ EOF_NAME = "$eof" #: String
13
+ ERROR_NAME = "error" #: String
14
+ IMPLICIT_ERROR = { type: :error }.freeze #: IR::error_action
15
+
16
+ attr_reader :automaton #: IR::Automaton
17
+ attr_reader :max_steps #: Integer
18
+ attr_reader :max_stack #: Integer
19
+ attr_reader :eof #: IR::GrammarSymbol
20
+
21
+ # @rbs (IR::Automaton automaton, ?max_steps: Integer, ?max_stack: Integer) -> void
22
+ def initialize(automaton, max_steps: DEFAULT_MAX_STEPS, max_stack: DEFAULT_MAX_STACK)
23
+ raise ArgumentError, "max_steps must be positive" unless max_steps.positive?
24
+ raise ArgumentError, "max_stack must be positive" unless max_stack.positive?
25
+
26
+ @automaton = automaton
27
+ @max_steps = max_steps
28
+ @max_stack = max_stack
29
+ @terminals = automaton.grammar.terminals.to_h { |symbol| [symbol.name, symbol] }.freeze
30
+ @terminal_aliases = terminal_aliases(automaton.grammar.terminals).freeze
31
+ @eof = @terminals[EOF_NAME] || raise(Ibex::Error, "(debug):1:1: automaton has no $eof terminal")
32
+ end
33
+
34
+ # @rbs () -> Session
35
+ def start
36
+ Session.new(self)
37
+ end
38
+
39
+ # @rbs (Array[String] tokens) -> Result
40
+ def simulate(tokens)
41
+ session = start
42
+ tokens.each do |token|
43
+ session.push(token)
44
+ break if session.status
45
+ end
46
+ session.finish
47
+ end
48
+
49
+ # @rbs (String spelling) -> IR::GrammarSymbol
50
+ def terminal(spelling)
51
+ if [EOF_NAME, ERROR_NAME].include?(spelling)
52
+ raise Ibex::Error, "(debug):1:1: reserved terminal #{spelling.inspect} cannot be supplied"
53
+ end
54
+
55
+ terminal = @terminals[spelling]
56
+ return terminal if terminal
57
+
58
+ empty = [] #: Array[IR::GrammarSymbol]
59
+ aliases = @terminal_aliases.fetch(spelling, empty)
60
+ return aliases.first if aliases.length == 1
61
+
62
+ if aliases.length > 1
63
+ names = aliases.map(&:name).sort.join(", ")
64
+ raise Ibex::Error, "(debug):1:1: terminal display name #{spelling.inspect} is ambiguous: #{names}"
65
+ end
66
+
67
+ raise Ibex::Error, "(debug):1:1: unknown terminal #{spelling.inspect}"
68
+ end
69
+
70
+ private
71
+
72
+ # @rbs (Array[IR::GrammarSymbol] terminals) -> Hash[String, Array[IR::GrammarSymbol]]
73
+ def terminal_aliases(terminals)
74
+ aliases = Hash.new { |hash, key| hash[key] = [] } #: Hash[String, Array[IR::GrammarSymbol]]
75
+ terminals.each do |terminal|
76
+ aliases[terminal.display_name] << terminal if terminal.display_name
77
+ end
78
+ aliases.each_value(&:freeze)
79
+ aliases
80
+ end
81
+ end
82
+
83
+ # Stateful token-at-a-time table simulation session.
84
+ class Session
85
+ # @rbs @steps: Array[Step]
86
+ # @rbs @tokens: Array[String]
87
+
88
+ attr_reader :status #: Symbol?
89
+
90
+ # @rbs (Simulator simulator) -> void
91
+ def initialize(simulator)
92
+ @simulator = simulator
93
+ @state_stack = [0] #: Array[Integer]
94
+ @status = nil
95
+ @steps = []
96
+ @tokens = []
97
+ @finished = false
98
+ end
99
+
100
+ # @rbs (String spelling) -> Array[Step]
101
+ def push(spelling)
102
+ ensure_active!
103
+ terminal = @simulator.terminal(spelling)
104
+ @tokens << terminal.name
105
+ process(terminal, eof: false)
106
+ end
107
+
108
+ # @rbs () -> Array[Step]
109
+ def steps = @steps.dup.freeze
110
+
111
+ # @rbs () -> Array[String]
112
+ def tokens = @tokens.dup.freeze
113
+
114
+ # @rbs () -> Result
115
+ def finish
116
+ raise Ibex::Error, "(debug):1:1: simulation session is already finished" if @finished
117
+
118
+ @finished = true
119
+ process(@simulator.eof, eof: true) until @status
120
+ Result.new(
121
+ grammar_digest: @simulator.automaton.grammar_digest,
122
+ algorithm: @simulator.automaton.algorithm,
123
+ tokens: @tokens,
124
+ status: @status || raise(Ibex::Error, "(debug):1:1: simulation did not terminate"),
125
+ steps: @steps
126
+ )
127
+ end
128
+
129
+ private
130
+
131
+ # @rbs (IR::GrammarSymbol terminal, eof: bool) -> Array[Step]
132
+ def process(terminal, eof:)
133
+ first = @steps.length
134
+ loop do
135
+ state = current_state
136
+ action, source = action_for(state, terminal.id)
137
+ case action.fetch(:type)
138
+ when :shift
139
+ record_shift(state, terminal, action, source)
140
+ next if eof
141
+
142
+ break
143
+ when :reduce then record_reduce(state, terminal, action, source)
144
+ when :accept
145
+ record_terminal(state, terminal, "accept", source)
146
+ @status = :accepted
147
+ break
148
+ when :error
149
+ record_terminal(state, terminal, "error", source)
150
+ @status = :error
151
+ break
152
+ else raise Ibex::Error, "(debug):1:1: unknown table action #{action.inspect}"
153
+ end
154
+ end
155
+ @steps.drop(first).freeze
156
+ end
157
+
158
+ # @rbs (IR::AutomatonState state, Integer token_id) -> [IR::parser_action, String]
159
+ def action_for(state, token_id)
160
+ return [state.actions.fetch(token_id), "explicit"] if state.actions.key?(token_id)
161
+ return [state.default_action, "default"] if state.default_action
162
+
163
+ [Simulator::IMPLICIT_ERROR, "implicit"]
164
+ end
165
+
166
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, IR::parser_action action, String source) -> void
167
+ def record_shift(state, terminal, action, source)
168
+ shift = action #: IR::shift_action
169
+ before = @state_stack.length
170
+ target = shift.fetch(:state)
171
+ @state_stack << target
172
+ enforce_stack_budget!
173
+ append_step(state, terminal, "shift", source, target_state: target, before: before)
174
+ end
175
+
176
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, IR::parser_action action, String source) -> void
177
+ def record_reduce(state, terminal, action, source)
178
+ reduction = action #: IR::reduce_action
179
+ production_id = reduction.fetch(:production)
180
+ production = @simulator.automaton.grammar.productions.fetch(production_id)
181
+ length = production.rhs.length
182
+ before = @state_stack.length
183
+ if length >= before
184
+ raise Ibex::Error, "(debug):1:1: production #{production_id} underflows the simulated state stack"
185
+ end
186
+
187
+ @state_stack.pop(length)
188
+ goto = current_state.gotos[production.lhs]
189
+ unless goto
190
+ raise Ibex::Error, "(debug):1:1: missing goto for production #{production_id} from state #{current_state.id}"
191
+ end
192
+
193
+ @state_stack << goto
194
+ enforce_stack_budget!
195
+ lhs = @simulator.automaton.grammar.symbol_by_id(production.lhs)
196
+ raise Ibex::Error, "(debug):1:1: missing lhs symbol #{production.lhs}" unless lhs
197
+
198
+ append_step(
199
+ state, terminal, "reduce", source,
200
+ production_id: production_id, lhs: lhs.name, rhs_length: length,
201
+ target_state: goto, before: before
202
+ )
203
+ end
204
+
205
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, String action, String source) -> void
206
+ def record_terminal(state, terminal, action, source)
207
+ append_step(state, terminal, action, source, before: @state_stack.length)
208
+ end
209
+
210
+ # @rbs (IR::AutomatonState state, IR::GrammarSymbol terminal, String action, String source,
211
+ # before: Integer, ?production_id: Integer?, ?lhs: String?, ?rhs_length: Integer?,
212
+ # ?target_state: Integer?) -> void
213
+ def append_step(state, terminal, action, source, before:, production_id: nil, lhs: nil, rhs_length: nil,
214
+ target_state: nil)
215
+ raise Ibex::Error, "(debug):1:1: simulation exceeded #{@simulator.max_steps} actions" \
216
+ if @steps.length >= @simulator.max_steps
217
+
218
+ @steps << Step.new(
219
+ sequence: @steps.length + 1,
220
+ state: state.id,
221
+ token_id: terminal.id,
222
+ token: terminal.display_name || terminal.name,
223
+ action: action,
224
+ action_source: source,
225
+ production_id: production_id,
226
+ lhs: lhs,
227
+ rhs_length: rhs_length,
228
+ target_state: target_state,
229
+ stack_depth_before: before,
230
+ stack_depth_after: @state_stack.length
231
+ )
232
+ end
233
+
234
+ # @rbs () -> IR::AutomatonState
235
+ def current_state
236
+ id = @state_stack.last || raise(Ibex::Error, "(debug):1:1: simulated state stack is empty")
237
+ @simulator.automaton.states.fetch(id)
238
+ end
239
+
240
+ # @rbs () -> void
241
+ def enforce_stack_budget!
242
+ return if @state_stack.length <= @simulator.max_stack
243
+
244
+ raise Ibex::Error, "(debug):1:1: simulation exceeded stack depth #{@simulator.max_stack}"
245
+ end
246
+
247
+ # @rbs () -> void
248
+ def ensure_active!
249
+ raise Ibex::Error, "(debug):1:1: simulation session is already finished" if @finished || @status
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module TableSimulation
6
+ # One immutable parser-table action.
7
+ class Step
8
+ attr_reader :sequence #: Integer
9
+ attr_reader :state #: Integer
10
+ attr_reader :token_id #: Integer
11
+ attr_reader :token #: String
12
+ attr_reader :action #: String
13
+ attr_reader :action_source #: String
14
+ attr_reader :production_id #: Integer?
15
+ attr_reader :lhs #: String?
16
+ attr_reader :rhs_length #: Integer?
17
+ attr_reader :target_state #: Integer?
18
+ attr_reader :stack_depth_before #: Integer
19
+ attr_reader :stack_depth_after #: Integer
20
+
21
+ # @rbs (sequence: Integer, state: Integer, token_id: Integer, token: String, action: String,
22
+ # action_source: String, production_id: Integer?, lhs: String?, rhs_length: Integer?,
23
+ # target_state: Integer?, stack_depth_before: Integer, stack_depth_after: Integer) -> void
24
+ def initialize(sequence:, state:, token_id:, token:, action:, action_source:, production_id:, lhs:, rhs_length:,
25
+ target_state:, stack_depth_before:, stack_depth_after:)
26
+ @sequence = sequence
27
+ @state = state
28
+ @token_id = token_id
29
+ @token = token.dup.freeze
30
+ @action = action.dup.freeze
31
+ @action_source = action_source.dup.freeze
32
+ @production_id = production_id
33
+ @lhs = lhs&.dup&.freeze
34
+ @rhs_length = rhs_length
35
+ @target_state = target_state
36
+ @stack_depth_before = stack_depth_before
37
+ @stack_depth_after = stack_depth_after
38
+ freeze
39
+ end
40
+
41
+ # @rbs () -> Hash[String, untyped]
42
+ def to_h
43
+ {
44
+ "sequence" => @sequence,
45
+ "state" => @state,
46
+ "token_id" => @token_id,
47
+ "token" => @token,
48
+ "action" => @action,
49
+ "action_source" => @action_source,
50
+ "production_id" => @production_id,
51
+ "lhs" => @lhs,
52
+ "rhs_length" => @rhs_length,
53
+ "target_state" => @target_state,
54
+ "stack_depth_before" => @stack_depth_before,
55
+ "stack_depth_after" => @stack_depth_after
56
+ }.freeze
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module TableSimulation
6
+ # Stable one-line renderer for simulation steps.
7
+ module Text
8
+ class << self
9
+ # @rbs (Step step) -> String
10
+ def render_step(step)
11
+ detail = case step.action
12
+ when "shift" then " target=#{step.target_state}"
13
+ when "reduce"
14
+ " production=#{step.production_id} lhs=#{step.lhs} rhs=#{step.rhs_length} " \
15
+ "goto=#{step.target_state}"
16
+ else ""
17
+ end
18
+ "#{step.sequence}: state=#{step.state} token=#{step.token.inspect} action=#{step.action}" \
19
+ "#{detail} source=#{step.action_source} stack=#{step.stack_depth_before}->#{step.stack_depth_after}"
20
+ end
21
+
22
+ # @rbs (Result result) -> String
23
+ def render(result)
24
+ lines = result.steps.map { |step| render_step(step) }
25
+ lines << "status=#{result.status}"
26
+ "#{lines.join("\n")}\n"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end