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,604 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module IR
5
+ module Validator
6
+ # Structural and referential validation for a versioned Grammar IR JSON object.
7
+ # rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.
8
+ class GrammarDocument < Base
9
+ ROOT_REQUIRED = %w[
10
+ ibex_ir schema_version class_name superclass start expect options symbols productions user_code
11
+ conversions warnings
12
+ ].freeze #: Array[String]
13
+ ROOT_OPTIONAL = %w[user_code_chunks expect_rr].freeze #: Array[String]
14
+ V2_ROOT_REQUIRED = %w[source_provenance migration].freeze #: Array[String]
15
+ V2_ROOT_OPTIONAL = %w[params printers tests recovery lexer mode starts].freeze #: Array[String]
16
+ SYMBOL_REQUIRED = %w[id name kind reserved prec loc].freeze #: Array[String]
17
+ SYMBOL_OPTIONAL = %w[display_name semantic_type].freeze #: Array[String]
18
+ V2_SYMBOL_REQUIRED = %w[doc].freeze #: Array[String]
19
+ PRODUCTION_REQUIRED = %w[id lhs rhs action prec_override origin].freeze #: Array[String]
20
+ V2_PRODUCTION_REQUIRED = %w[doc expansion].freeze #: Array[String]
21
+ V2_ACTION_REQUIRED = %w[composition].freeze #: Array[String]
22
+ ORIGIN_KINDS = %w[
23
+ optional_expansion star_expansion plus_expansion separated_list_expansion group_expansion
24
+ ].freeze #: Array[String]
25
+ RUBY_KEYWORDS = %w[
26
+ __ENCODING__ __FILE__ __LINE__ alias and begin break case class def defined do else elsif end ensure false for
27
+ if in module next nil not or redo rescue retry return self super then true undef unless until when while yield
28
+ ].freeze #: Array[String]
29
+
30
+ attr_reader :symbols_by_id #: Hash[Integer, Hash[String, untyped]]
31
+ attr_reader :symbols_by_name #: Hash[String, Hash[String, untyped]]
32
+ attr_reader :productions_by_id #: Hash[Integer, Hash[String, untyped]]
33
+
34
+ # @rbs @data: Hash[String, untyped]
35
+ # @rbs @path: String
36
+ # @rbs @version: Integer
37
+
38
+ # @rbs (Hash[String, untyped] data, ?path: String, ?version: Integer) -> void
39
+ def initialize(data, path: "$", version: data.fetch("schema_version"))
40
+ super()
41
+ @data = data
42
+ @path = path
43
+ @version = version
44
+ @symbols_by_id = {}
45
+ @symbols_by_name = {}
46
+ @productions_by_id = {}
47
+ end
48
+
49
+ # @rbs () -> self
50
+ def validate
51
+ required = ROOT_REQUIRED + (@version >= 2 ? V2_ROOT_REQUIRED : [])
52
+ optional = ROOT_OPTIONAL + (@version >= 2 ? V2_ROOT_OPTIONAL : [])
53
+ record(@data, @path, required, optional)
54
+ validate_envelope
55
+ validate_header
56
+ validate_options
57
+ validate_parser_parameters if @data.key?("params")
58
+ validate_symbols
59
+ validate_value_printers if @data.key?("printers")
60
+ validate_grammar_tests if @data.key?("tests")
61
+ validate_reserved_symbols
62
+ validate_start
63
+ validate_lexer if @data.key?("lexer")
64
+ validate_recovery if @data.key?("recovery")
65
+ validate_productions
66
+ validate_string_map(@data["user_code"], "#{@path}.user_code")
67
+ validate_string_map(@data["conversions"], "#{@path}.conversions")
68
+ validate_warnings
69
+ validate_user_code_chunks if @data.key?("user_code_chunks")
70
+ if @version >= 2
71
+ validate_source_provenance(@data["source_provenance"], "#{@path}.source_provenance")
72
+ validate_migration
73
+ end
74
+ self
75
+ end
76
+
77
+ private
78
+
79
+ # @rbs () -> void
80
+ def validate_envelope
81
+ literal(@data["ibex_ir"], "#{@path}.ibex_ir", "grammar")
82
+ literal(@data["schema_version"], "#{@path}.schema_version", @version)
83
+ end
84
+
85
+ # @rbs () -> void
86
+ def validate_header
87
+ nonempty_string(@data["class_name"], "#{@path}.class_name")
88
+ nullable_string(@data["superclass"], "#{@path}.superclass")
89
+ nonempty_string(@data["start"], "#{@path}.start")
90
+ enum(@data["mode"], "#{@path}.mode", %w[default extended]) if @data.key?("mode")
91
+ nonnegative_integer(@data["expect"], "#{@path}.expect")
92
+ nonnegative_integer(@data["expect_rr"], "#{@path}.expect_rr") if @data.key?("expect_rr")
93
+ end
94
+
95
+ # @rbs () -> void
96
+ def validate_options
97
+ path = "#{@path}.options"
98
+ options = record(@data["options"], path, %w[result_var omit_action_call], %w[cst])
99
+ boolean(options["result_var"], "#{path}.result_var")
100
+ boolean(options["omit_action_call"], "#{path}.omit_action_call")
101
+ return unless options.key?("cst")
102
+
103
+ invalid("#{path}.cst", "requires schema_version 2") unless @version >= 2
104
+ literal(options["cst"], "#{path}.cst", true)
105
+ invalid("#{@path}.mode", "must be extended when CST construction is enabled") unless
106
+ @data["mode"] == "extended"
107
+ end
108
+
109
+ # @rbs () -> void
110
+ def validate_parser_parameters
111
+ names = {} #: Hash[String, bool]
112
+ array(@data["params"], "#{@path}.params").each_with_index do |value, index|
113
+ path = "#{@path}.params[#{index}]"
114
+ parameter = record(value, path, %w[name semantic_type])
115
+ name = nonempty_string(parameter["name"], "#{path}.name")
116
+ invalid("#{path}.name", "must be a Ruby local identifier") unless name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
117
+ invalid("#{path}.name", "must not be a Ruby keyword") if RUBY_KEYWORDS.include?(name)
118
+ invalid("#{path}.name", "duplicates parameter #{name.inspect}") if names.key?(name)
119
+ names[name] = true
120
+ metadata(parameter["semantic_type"], "#{path}.semantic_type")
121
+ end
122
+ end
123
+
124
+ # @rbs () -> void
125
+ def validate_value_printers
126
+ names = {} #: Hash[String, bool]
127
+ array(@data["printers"], "#{@path}.printers").each_with_index do |value, index|
128
+ path = "#{@path}.printers[#{index}]"
129
+ printer = record(value, path, %w[symbol code loc])
130
+ name = nonempty_string(printer["symbol"], "#{path}.symbol")
131
+ invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
132
+ invalid("#{path}.symbol", "duplicates printer for #{name.inspect}") if names.key?(name)
133
+ names[name] = true
134
+ string(printer["code"], "#{path}.code")
135
+ location(printer["loc"], "#{path}.loc", nullable: false)
136
+ end
137
+ end
138
+
139
+ # @rbs () -> void
140
+ def validate_grammar_tests
141
+ invalid("#{@path}.mode", "must be extended for grammar tests") unless @data["mode"] == "extended"
142
+ seen = {} #: Hash[[String, String], bool]
143
+ array(@data["tests"], "#{@path}.tests").each_with_index do |value, index|
144
+ path = "#{@path}.tests[#{index}]"
145
+ test = record(value, path, %w[expectation source loc])
146
+ expectation = enum(test["expectation"], "#{path}.expectation", %w[accept reject])
147
+ source = string(test["source"], "#{path}.source")
148
+ key = [expectation, source] #: [String, String]
149
+ invalid(path, "duplicates grammar test") if seen[key]
150
+ seen[key] = true
151
+ location(test["loc"], "#{path}.loc", nullable: false)
152
+ end
153
+ end
154
+
155
+ # @rbs () -> void
156
+ def validate_symbols
157
+ array(@data["symbols"], "#{@path}.symbols").each_with_index do |value, index|
158
+ validate_symbol(value, index)
159
+ end
160
+ end
161
+
162
+ # @rbs (untyped value, Integer index) -> void
163
+ def validate_symbol(value, index)
164
+ path = "#{@path}.symbols[#{index}]"
165
+ required = SYMBOL_REQUIRED + (@version >= 2 ? V2_SYMBOL_REQUIRED : [])
166
+ symbol = record(value, path, required, SYMBOL_OPTIONAL)
167
+ id = nonnegative_integer(symbol["id"], "#{path}.id")
168
+ invalid("#{path}.id", "must equal its array index #{index}") unless id == index
169
+ name = nonempty_string(symbol["name"], "#{path}.name")
170
+ invalid("#{path}.name", "duplicates symbol #{name.inspect}") if @symbols_by_name.key?(name)
171
+ @symbols_by_id[id] = symbol
172
+ @symbols_by_name[name] = symbol
173
+ enum(symbol["kind"], "#{path}.kind", %w[terminal nonterminal])
174
+ boolean(symbol["reserved"], "#{path}.reserved")
175
+ validate_precedence(symbol["prec"], "#{path}.prec")
176
+ location(symbol["loc"], "#{path}.loc")
177
+ SYMBOL_OPTIONAL.each { |key| metadata(symbol[key], "#{path}.#{key}") if symbol.key?(key) }
178
+ nullable_string(symbol["doc"], "#{path}.doc") if @version >= 2
179
+ end
180
+
181
+ # @rbs (untyped value, String path) -> void
182
+ def validate_precedence(value, path)
183
+ return if value.nil?
184
+
185
+ precedence = record(value, path, %w[associativity level])
186
+ enum(precedence["associativity"], "#{path}.associativity", %w[left right nonassoc precedence])
187
+ positive_integer(precedence["level"], "#{path}.level")
188
+ end
189
+
190
+ # @rbs () -> void
191
+ def validate_reserved_symbols
192
+ validate_reserved_symbol(0, "$eof")
193
+ validate_reserved_symbol(1, "error")
194
+ end
195
+
196
+ # @rbs (Integer id, String name) -> void
197
+ def validate_reserved_symbol(id, name)
198
+ symbol = @symbols_by_id[id]
199
+ invalid("#{@path}.symbols", "must contain reserved symbol #{name.inspect} at id #{id}") unless symbol
200
+ invalid("#{@path}.symbols[#{id}]", "must be reserved terminal #{name.inspect}") unless
201
+ symbol["name"] == name && symbol["kind"] == "terminal" && symbol["reserved"]
202
+ end
203
+
204
+ # @rbs () -> void
205
+ def validate_start
206
+ start = @data["start"]
207
+ symbol = @symbols_by_name[start]
208
+ invalid("#{@path}.start", "references missing symbol #{start.inspect}") unless symbol
209
+ invalid("#{@path}.start", "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
210
+ return unless @data.key?("starts")
211
+
212
+ validate_multiple_starts(start)
213
+ end
214
+
215
+ # @rbs (String start) -> void
216
+ def validate_multiple_starts(start)
217
+ starts = array(@data["starts"], "#{@path}.starts")
218
+ invalid("#{@path}.starts", "must not be empty") if starts.empty?
219
+ invalid("#{@path}.starts[0]", "must equal start #{start.inspect}") unless starts.first == start
220
+ invalid("#{@path}.mode", "must be extended for multiple start symbols") unless @data["mode"] == "extended"
221
+ seen = {} #: Hash[String, bool]
222
+ starts.each_with_index do |name, index|
223
+ name = nonempty_string(name, "#{@path}.starts[#{index}]")
224
+ invalid("#{@path}.starts[#{index}]", "duplicates start symbol #{name.inspect}") if seen[name]
225
+ seen[name] = true
226
+ definition = @symbols_by_name[name]
227
+ invalid("#{@path}.starts[#{index}]", "references missing symbol #{name.inspect}") unless definition
228
+ invalid("#{@path}.starts[#{index}]", "must reference a nonterminal") unless
229
+ definition["kind"] == "nonterminal"
230
+ end
231
+ end
232
+
233
+ # @rbs () -> void
234
+ def validate_recovery
235
+ path = "#{@path}.recovery"
236
+ recovery = record(@data["recovery"], path, %w[sync_tokens on_error_reduce])
237
+ invalid("#{@path}.mode", "must be extended for recovery declarations") unless @data["mode"] == "extended"
238
+ validate_recovery_symbols(recovery["sync_tokens"], "#{path}.sync_tokens", kind: "terminal")
239
+ groups = array(recovery["on_error_reduce"], "#{path}.on_error_reduce")
240
+ seen = {} #: Hash[String, bool]
241
+ groups.each_with_index do |group, index|
242
+ group_path = "#{path}.on_error_reduce[#{index}]"
243
+ names = array(group, group_path)
244
+ invalid(group_path, "must not be empty") if names.empty?
245
+ names.each_with_index do |name, name_index|
246
+ validate_recovery_symbol(
247
+ name, "#{group_path}[#{name_index}]", kind: "nonterminal", seen: seen
248
+ )
249
+ end
250
+ end
251
+ return unless array(recovery["sync_tokens"], "#{path}.sync_tokens").empty? && groups.empty?
252
+
253
+ invalid(path, "must declare at least one recovery policy")
254
+ end
255
+
256
+ # @rbs (untyped values, String path, kind: String) -> void
257
+ def validate_recovery_symbols(values, path, kind:)
258
+ seen = {} #: Hash[String, bool]
259
+ array(values, path).each_with_index do |name, index|
260
+ validate_recovery_symbol(name, "#{path}[#{index}]", kind: kind, seen: seen)
261
+ end
262
+ end
263
+
264
+ # @rbs (untyped value, String path, kind: String, seen: Hash[String, bool]) -> void
265
+ def validate_recovery_symbol(value, path, kind:, seen:)
266
+ name = nonempty_string(value, path)
267
+ invalid(path, "duplicates symbol #{name.inspect}") if seen[name]
268
+ seen[name] = true
269
+ symbol = @symbols_by_name[name]
270
+ invalid(path, "references missing symbol #{name.inspect}") unless symbol
271
+ invalid(path, "must reference a #{kind}") unless symbol["kind"] == kind
272
+ invalid(path, "must not reference the synthetic error token") if name == "error"
273
+ end
274
+
275
+ # @rbs () -> void
276
+ def validate_productions
277
+ array(@data["productions"], "#{@path}.productions").each_with_index do |value, index|
278
+ validate_production(value, index)
279
+ end
280
+ end
281
+
282
+ # @rbs (untyped value, Integer index) -> void
283
+ def validate_production(value, index)
284
+ path = "#{@path}.productions[#{index}]"
285
+ required = PRODUCTION_REQUIRED + (@version >= 2 ? V2_PRODUCTION_REQUIRED : [])
286
+ optional = @version >= 2 ? %w[node] : Array.new(0) #: Array[String]
287
+ production = record(value, path, required, optional)
288
+ id = nonnegative_integer(production["id"], "#{path}.id")
289
+ invalid("#{path}.id", "must equal its array index #{index}") unless id == index
290
+ @productions_by_id[id] = production
291
+ validate_lhs(production["lhs"], "#{path}.lhs")
292
+ validate_rhs(production["rhs"], "#{path}.rhs")
293
+ validate_action(production["action"], "#{path}.action", rhs_length: production["rhs"].length)
294
+ validate_precedence_override(production["prec_override"], "#{path}.prec_override")
295
+ validate_origin(production["origin"], "#{path}.origin")
296
+ return unless @version >= 2
297
+
298
+ nullable_string(production["doc"], "#{path}.doc")
299
+ validate_expansion(production["expansion"], "#{path}.expansion")
300
+ validate_node_annotation(production["node"], "#{path}.node", rhs_length: production["rhs"].length)
301
+ end
302
+
303
+ # @rbs (untyped value, String path, rhs_length: Integer) -> void
304
+ def validate_node_annotation(value, path, rhs_length:)
305
+ return if value.nil?
306
+
307
+ node = record(value, path, %w[name fields loc])
308
+ name = nonempty_string(node["name"], "#{path}.name")
309
+ invalid("#{path}.name", "must be a Ruby constant identifier") unless name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
310
+ fields = array(node["fields"], "#{path}.fields")
311
+ invalid("#{path}.fields", "must contain #{rhs_length} entries") unless fields.length == rhs_length
312
+ seen = {} #: Hash[String, bool]
313
+ fields.each_with_index do |value, index|
314
+ field = nonempty_string(value, "#{path}.fields[#{index}]")
315
+ invalid("#{path}.fields[#{index}]", "must be a Ruby local identifier") unless
316
+ field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) && !RUBY_KEYWORDS.include?(field)
317
+ invalid("#{path}.fields[#{index}]", "duplicates field #{field.inspect}") if seen[field]
318
+ seen[field] = true
319
+ end
320
+ location(node["loc"], "#{path}.loc", nullable: false)
321
+ end
322
+
323
+ # @rbs (untyped value, String path) -> void
324
+ def validate_lhs(value, path)
325
+ id = nonnegative_integer(value, path)
326
+ symbol = @symbols_by_id[id]
327
+ invalid(path, "references missing symbol id #{id}") unless symbol
328
+ invalid(path, "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
329
+ end
330
+
331
+ # @rbs (untyped value, String path) -> void
332
+ def validate_rhs(value, path)
333
+ array(value, path).each_with_index do |id, index|
334
+ id = nonnegative_integer(id, "#{path}[#{index}]")
335
+ invalid("#{path}[#{index}]", "references missing symbol id #{id}") unless @symbols_by_id.key?(id)
336
+ end
337
+ end
338
+
339
+ # @rbs (untyped value, String path, rhs_length: Integer) -> void
340
+ def validate_action(value, path, rhs_length:)
341
+ return if value.nil?
342
+
343
+ required = %w[code loc named_refs context_length] + (@version >= 2 ? V2_ACTION_REQUIRED : [])
344
+ action = record(value, path, required)
345
+ string(action["code"], "#{path}.code")
346
+ location(action["loc"], "#{path}.loc", nullable: false)
347
+ context_length = nonnegative_integer(action["context_length"], "#{path}.context_length")
348
+ validate_named_refs(action["named_refs"], "#{path}.named_refs", limit: [rhs_length, context_length].max)
349
+ return unless @version >= 2
350
+
351
+ validate_action_composition(
352
+ action["composition"], "#{path}.composition", rhs_length: rhs_length
353
+ )
354
+ end
355
+
356
+ # @rbs (untyped value, String path, limit: Integer) -> void
357
+ def validate_named_refs(value, path, limit:)
358
+ names = {} #: Hash[String, bool]
359
+ array(value, path).each_with_index do |entry, index|
360
+ entry_path = "#{path}[#{index}]"
361
+ reference = record(entry, entry_path, %w[name index])
362
+ name = nonempty_string(reference["name"], "#{entry_path}.name")
363
+ invalid("#{entry_path}.name", "duplicates named reference #{name.inspect}") if names.key?(name)
364
+ names[name] = true
365
+ reference_index = nonnegative_integer(reference["index"], "#{entry_path}.index")
366
+ if reference_index >= limit
367
+ invalid("#{entry_path}.index", "must be less than the action context length #{limit}")
368
+ end
369
+ end
370
+ end
371
+
372
+ # @rbs (untyped value, String path) -> void
373
+ def validate_precedence_override(value, path)
374
+ return if value.nil?
375
+
376
+ id = nonnegative_integer(value, path)
377
+ symbol = @symbols_by_id[id]
378
+ invalid(path, "references missing symbol id #{id}") unless symbol
379
+ invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
380
+ end
381
+
382
+ # @rbs (untyped value, String path) -> void
383
+ def validate_origin(value, path)
384
+ origin = object(value, path)
385
+ kind = string(field(origin, "kind", path), "#{path}.kind")
386
+ optional = ORIGIN_KINDS.include?(kind) ? %w[expression] : [] # @type var optional: Array[String]
387
+ record(origin, path, %w[kind loc], optional)
388
+ enum(kind, "#{path}.kind", %w[user inline_action] + ORIGIN_KINDS)
389
+ string(origin["expression"], "#{path}.expression") if origin.key?("expression")
390
+ location(origin["loc"], "#{path}.loc", nullable: false)
391
+ end
392
+
393
+ # @rbs (untyped value, String path) -> void
394
+ def validate_string_map(value, path)
395
+ object(value, path).each do |key, item|
396
+ string(key, path)
397
+ string(item, child_path(path, key))
398
+ end
399
+ end
400
+
401
+ # @rbs () -> void
402
+ def validate_warnings
403
+ array(@data["warnings"], "#{@path}.warnings").each_with_index do |value, index|
404
+ validate_warning(value, "#{@path}.warnings[#{index}]")
405
+ end
406
+ end
407
+
408
+ # @rbs (untyped value, String path) -> void
409
+ def validate_warning(value, path)
410
+ warning = record(value, path, %w[type loc], %w[symbol production original])
411
+ nonempty_string(warning["type"], "#{path}.type")
412
+ location(warning["loc"], "#{path}.loc")
413
+ validate_warning_symbol(warning, path) if warning.key?("symbol")
414
+ validate_warning_production(warning, "production", path) if warning.key?("production")
415
+ validate_warning_production(warning, "original", path) if warning.key?("original")
416
+ end
417
+
418
+ # @rbs (Hash[String, untyped] warning, String path) -> void
419
+ def validate_warning_symbol(warning, path)
420
+ name = nonempty_string(warning["symbol"], "#{path}.symbol")
421
+ invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
422
+ end
423
+
424
+ # @rbs (Hash[String, untyped] warning, String field_name, String path) -> void
425
+ def validate_warning_production(warning, field_name, path)
426
+ id = nonnegative_integer(warning[field_name], "#{path}.#{field_name}")
427
+ invalid("#{path}.#{field_name}", "references missing production id #{id}") unless @productions_by_id.key?(id)
428
+ end
429
+
430
+ # @rbs () -> void
431
+ def validate_lexer
432
+ value = @data.fetch("lexer")
433
+ LexerDocument.new(value, path: "#{@path}.lexer").validate
434
+ value.fetch("rules").each_with_index do |rule, index|
435
+ next unless rule["kind"] == "token"
436
+
437
+ name = rule["token"]
438
+ symbol = @symbols_by_name[name]
439
+ invalid("#{@path}.lexer.rules[#{index}].token", "references missing terminal #{name.inspect}") unless
440
+ symbol&.fetch("kind") == "terminal"
441
+ end
442
+ end
443
+
444
+ # @rbs () -> void
445
+ def validate_user_code_chunks
446
+ path = "#{@path}.user_code_chunks"
447
+ object(@data["user_code_chunks"], path).each do |key, chunks|
448
+ string(key, path)
449
+ array(chunks, child_path(path, key)).each_with_index do |chunk, index|
450
+ chunk_path = "#{child_path(path, key)}[#{index}]"
451
+ chunk = record(chunk, chunk_path, %w[code loc])
452
+ string(chunk["code"], "#{chunk_path}.code")
453
+ location(chunk["loc"], "#{chunk_path}.loc", nullable: false)
454
+ end
455
+ end
456
+ end
457
+
458
+ # @rbs (untyped value, String path, ?nullable: bool) -> void
459
+ def validate_source_provenance(value, path, nullable: true)
460
+ return if nullable && value.nil?
461
+
462
+ source = record(value, path, %w[file root byte_span])
463
+ nullable_string(source["file"], "#{path}.file")
464
+ nullable_string(source["root"], "#{path}.root")
465
+ validate_byte_span(source["byte_span"], "#{path}.byte_span")
466
+ end
467
+
468
+ # @rbs (untyped value, String path) -> void
469
+ def validate_byte_span(value, path)
470
+ return if value.nil?
471
+
472
+ span = record(value, path, %w[start end])
473
+ start_byte = nonnegative_integer(span["start"], "#{path}.start")
474
+ end_byte = nonnegative_integer(span["end"], "#{path}.end")
475
+ invalid("#{path}.end", "must be greater than or equal to start") if end_byte < start_byte
476
+ end
477
+
478
+ # @rbs () -> void
479
+ def validate_migration
480
+ path = "#{@path}.migration"
481
+ value = @data["migration"]
482
+ return if value.nil?
483
+
484
+ migration = record(value, path, %w[from_schema_version unavailable])
485
+ literal(migration["from_schema_version"], "#{path}.from_schema_version", 1)
486
+ values = array(migration["unavailable"], "#{path}.unavailable")
487
+ invalid("#{path}.unavailable", "must not be empty") if values.empty?
488
+ values.each_with_index do |name, index|
489
+ enum(name, "#{path}.unavailable[#{index}]", Migration::UNAVAILABLE_V1_METADATA)
490
+ end
491
+ invalid("#{path}.unavailable", "must contain unique names") unless values.uniq.length == values.length
492
+ end
493
+
494
+ # @rbs (untyped value, String path) -> void
495
+ def validate_expansion(value, path)
496
+ return if value.nil?
497
+
498
+ expansion = record(value, path, %w[parameter inline include_chain])
499
+ validate_parameter_expansion(expansion["parameter"], "#{path}.parameter")
500
+ validate_inline_expansion(expansion["inline"], "#{path}.inline")
501
+ array(expansion["include_chain"], "#{path}.include_chain").each_with_index do |source, index|
502
+ validate_source_provenance(source, "#{path}.include_chain[#{index}]", nullable: false)
503
+ end
504
+ end
505
+
506
+ # @rbs (untyped value, String path) -> void
507
+ def validate_parameter_expansion(value, path)
508
+ return if value.nil?
509
+
510
+ parameter = record(value, path, %w[rule arguments])
511
+ nonempty_string(parameter["rule"], "#{path}.rule")
512
+ array(parameter["arguments"], "#{path}.arguments").each_with_index do |argument, index|
513
+ nonempty_string(argument, "#{path}.arguments[#{index}]")
514
+ end
515
+ end
516
+
517
+ # @rbs (untyped value, String path) -> void
518
+ def validate_inline_expansion(value, path)
519
+ return if value.nil?
520
+
521
+ inline = record(value, path, %w[rule])
522
+ nonempty_string(inline["rule"], "#{path}.rule")
523
+ end
524
+
525
+ # @rbs (untyped value, String path, rhs_length: Integer) -> void
526
+ def validate_action_composition(value, path, rhs_length:)
527
+ return if value.nil?
528
+
529
+ composition = record(value, path, %w[strategy fragments], %w[plan])
530
+ literal(composition["strategy"], "#{path}.strategy", "sequence")
531
+ fragments = array(composition["fragments"], "#{path}.fragments")
532
+ invalid("#{path}.fragments", "must not be empty") if fragments.empty?
533
+ fragments.each_with_index do |value, index|
534
+ fragment_path = "#{path}.fragments[#{index}]"
535
+ fragment = record(value, fragment_path, %w[kind source])
536
+ enum(fragment["kind"], "#{fragment_path}.kind", %w[rule inline])
537
+ validate_source_provenance(fragment["source"], "#{fragment_path}.source")
538
+ end
539
+ validate_action_composition_plan(composition["plan"], "#{path}.plan", rhs_length, fragments) if
540
+ composition.key?("plan")
541
+ end
542
+
543
+ # @rbs (untyped value, String path, Integer rhs_length, Array[untyped] fragments) -> void
544
+ def validate_action_composition_plan(value, path, rhs_length, fragments)
545
+ plan = record(value, path, %w[version physical steps])
546
+ literal(plan["version"], "#{path}.version", 1)
547
+ physical = nonnegative_integer(plan["physical"], "#{path}.physical")
548
+ invalid("#{path}.physical", "must equal production RHS length #{rhs_length}") unless physical == rhs_length
549
+ steps = array(plan["steps"], "#{path}.steps")
550
+ invalid("#{path}.steps", "must not be empty") if steps.empty?
551
+ invalid("#{path}.steps", "must align one-to-one with fragments") unless steps.length == fragments.length
552
+ steps.each_with_index do |step, index|
553
+ validate_action_composition_step(step, "#{path}.steps[#{index}]", physical, index)
554
+ next if step["kind"] == fragments.fetch(index)["kind"]
555
+
556
+ invalid("#{path}.steps[#{index}].kind", "must match the corresponding fragment")
557
+ end
558
+ end
559
+
560
+ # @rbs (untyped value, String path, Integer physical, Integer step_index) -> void
561
+ def validate_action_composition_step(value, path, physical, step_index)
562
+ step = record(
563
+ value, path,
564
+ %w[kind rule code loc named_refs context_length inputs stack_inputs lookahead result_var],
565
+ %w[result_type]
566
+ )
567
+ kind = enum(step["kind"], "#{path}.kind", %w[rule inline])
568
+ rule = nullable_string(step["rule"], "#{path}.rule")
569
+ invalid("#{path}.rule", "must name an inline rule") if kind == "inline" && (!rule || rule.empty?)
570
+ nullable_string(step["code"], "#{path}.code")
571
+ location(step["loc"], "#{path}.loc", nullable: false)
572
+ context = nonnegative_integer(step["context_length"], "#{path}.context_length")
573
+ inputs = array(step["inputs"], "#{path}.inputs")
574
+ limit = physical + step_index
575
+ validate_action_slots(inputs, "#{path}.inputs", limit)
576
+ stack_inputs = array(step["stack_inputs"], "#{path}.stack_inputs")
577
+ validate_action_slots(stack_inputs, "#{path}.stack_inputs", limit)
578
+ validate_named_refs(step["named_refs"], "#{path}.named_refs", limit: [inputs.length, context].max)
579
+ validate_action_lookahead(step["lookahead"], "#{path}.lookahead", physical)
580
+ boolean(step["result_var"], "#{path}.result_var")
581
+ nullable_string(step["result_type"], "#{path}.result_type")
582
+ end
583
+
584
+ # @rbs (Array[untyped] inputs, String path, Integer limit) -> void
585
+ def validate_action_slots(inputs, path, limit)
586
+ inputs.each_with_index do |input, index|
587
+ slot_path = "#{path}[#{index}]"
588
+ slot = nonnegative_integer(input, slot_path)
589
+ invalid(slot_path, "must reference an available slot below #{limit}") if slot >= limit
590
+ end
591
+ end
592
+
593
+ # @rbs (untyped value, String path, Integer physical) -> void
594
+ def validate_action_lookahead(value, path, physical)
595
+ return if value.nil?
596
+
597
+ boundary = nonnegative_integer(value, path)
598
+ invalid(path, "must reference a physical slot below #{physical}") if boundary >= physical
599
+ end
600
+ end
601
+ # rubocop:enable Metrics/ClassLength
602
+ end
603
+ end
604
+ end