ibex 0.2.0 → 0.3.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 (437) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +95 -272
  3. data/docs/architecture.md +187 -35
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +3 -4
  12. data/docs/cst.md +24 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +179 -2
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +2 -1
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +54 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +184 -13
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +5 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +11 -0
  63. data/docs/release-readiness.md +126 -68
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +144 -34
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/lib/ibex/analysis/digraph.rb +128 -0
  75. data/lib/ibex/analysis.rb +1 -0
  76. data/lib/ibex/bison_import/importer.rb +516 -0
  77. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  78. data/lib/ibex/bison_import.rb +200 -0
  79. data/lib/ibex/bounded_subprocess.rb +171 -0
  80. data/lib/ibex/cli/ambiguity.rb +14 -5
  81. data/lib/ibex/cli/analysis.rb +134 -0
  82. data/lib/ibex/cli/bison_import.rb +121 -0
  83. data/lib/ibex/cli/config.rb +128 -0
  84. data/lib/ibex/cli/diagnostics.rb +31 -11
  85. data/lib/ibex/cli/documentation.rb +11 -8
  86. data/lib/ibex/cli/equiv.rb +169 -0
  87. data/lib/ibex/cli/error_messages.rb +11 -4
  88. data/lib/ibex/cli/explain.rb +14 -5
  89. data/lib/ibex/cli/fix.rb +203 -0
  90. data/lib/ibex/cli/formatting.rb +11 -10
  91. data/lib/ibex/cli/fuzz.rb +200 -0
  92. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  93. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  94. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  95. data/lib/ibex/cli/grammar_tests.rb +32 -14
  96. data/lib/ibex/cli/ir_tools.rb +5 -61
  97. data/lib/ibex/cli/outputs.rb +60 -33
  98. data/lib/ibex/cli/reduce.rb +202 -0
  99. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  100. data/lib/ibex/cli/samples.rb +49 -23
  101. data/lib/ibex/cli/verify.rb +116 -0
  102. data/lib/ibex/cli/watch.rb +6 -5
  103. data/lib/ibex/cli.rb +357 -56
  104. data/lib/ibex/codegen/action_locations.rb +1 -1
  105. data/lib/ibex/codegen/action_method_source.rb +5 -6
  106. data/lib/ibex/codegen/action_source.rb +3 -2
  107. data/lib/ibex/codegen/ambiguity.rb +5 -4
  108. data/lib/ibex/codegen/explain.rb +123 -55
  109. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  110. data/lib/ibex/codegen/rbs.rb +15 -7
  111. data/lib/ibex/codegen/report.rb +25 -13
  112. data/lib/ibex/codegen/ruby.rb +28 -64
  113. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  114. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  115. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  116. data/lib/ibex/configuration/explanation.rb +421 -0
  117. data/lib/ibex/configuration/inspector.rb +232 -0
  118. data/lib/ibex/configuration.rb +580 -0
  119. data/lib/ibex/coverage/collector.rb +21 -11
  120. data/lib/ibex/coverage/event_stream.rb +13 -7
  121. data/lib/ibex/coverage/report.rb +26 -18
  122. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  123. data/lib/ibex/delta_reducer.rb +99 -0
  124. data/lib/ibex/diff.rb +140 -0
  125. data/lib/ibex/equiv/machine.rb +135 -0
  126. data/lib/ibex/equiv.rb +373 -0
  127. data/lib/ibex/fix.rb +557 -0
  128. data/lib/ibex/frontend/ast.rb +22 -2
  129. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  130. data/lib/ibex/frontend/diagnostic.rb +1 -1
  131. data/lib/ibex/frontend/formatter.rb +72 -26
  132. data/lib/ibex/frontend/generated_parser.rb +147 -117
  133. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  134. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  135. data/lib/ibex/frontend/generation.rb +1 -1
  136. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  137. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  138. data/lib/ibex/frontend/regenerator.rb +1 -0
  139. data/lib/ibex/frontend/resolution.rb +2 -1
  140. data/lib/ibex/frontend/resolver.rb +1 -1
  141. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  142. data/lib/ibex/frontend/source_cursor.rb +2 -2
  143. data/lib/ibex/frontend/source_span.rb +9 -1
  144. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  145. data/lib/ibex/frontend/token_adapter.rb +10 -0
  146. data/lib/ibex/frontend.rb +1 -0
  147. data/lib/ibex/fuzz.rb +219 -0
  148. data/lib/ibex/generation_input.rb +1 -1
  149. data/lib/ibex/generation_manifest.rb +36 -25
  150. data/lib/ibex/generation_transaction.rb +1 -1
  151. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  152. data/lib/ibex/generation_transaction_validation.rb +2 -2
  153. data/lib/ibex/grammar_tests.rb +1 -1
  154. data/lib/ibex/ir/automaton_ir.rb +61 -19
  155. data/lib/ibex/ir/grammar_ir.rb +76 -45
  156. data/lib/ibex/ir/lexer_ir.rb +5 -5
  157. data/lib/ibex/ir/parser_contract.rb +135 -0
  158. data/lib/ibex/ir/serialize.rb +203 -76
  159. data/lib/ibex/ir/validator/automaton.rb +64 -34
  160. data/lib/ibex/ir/validator/base.rb +51 -33
  161. data/lib/ibex/ir/validator/grammar.rb +113 -83
  162. data/lib/ibex/ir/validator/lexer.rb +2 -2
  163. data/lib/ibex/ir/validator.rb +2 -1
  164. data/lib/ibex/ir.rb +16 -3
  165. data/lib/ibex/lalr/build_metrics.rb +50 -2
  166. data/lib/ibex/lalr/builder.rb +191 -56
  167. data/lib/ibex/lalr/conflict_search.rb +15 -4
  168. data/lib/ibex/lalr/counterexample.rb +19 -14
  169. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  170. data/lib/ibex/lalr/goto_follows.rb +229 -0
  171. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  172. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  173. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  174. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  175. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  176. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  177. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  178. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  179. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  180. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  181. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  182. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  183. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  184. data/lib/ibex/lalr.rb +52 -1
  185. data/lib/ibex/location.rb +3 -3
  186. data/lib/ibex/lsp/document_handlers.rb +9 -7
  187. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  188. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  189. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  190. data/lib/ibex/lsp/position_codec.rb +6 -2
  191. data/lib/ibex/lsp/request_handlers.rb +3 -2
  192. data/lib/ibex/lsp/request_support.rb +14 -7
  193. data/lib/ibex/lsp/server.rb +13 -11
  194. data/lib/ibex/lsp/symbol_index.rb +13 -13
  195. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  196. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  197. data/lib/ibex/lsp/transport.rb +13 -3
  198. data/lib/ibex/lsp/workspace.rb +5 -4
  199. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  200. data/lib/ibex/lsp.rb +1 -0
  201. data/lib/ibex/messages/en.yml +28 -0
  202. data/lib/ibex/messages/ja.yml +28 -0
  203. data/lib/ibex/messages.rb +68 -0
  204. data/lib/ibex/metrics.rb +175 -0
  205. data/lib/ibex/normalize/declarations.rb +2 -0
  206. data/lib/ibex/normalize/diagnostics.rb +63 -38
  207. data/lib/ibex/normalize/expander.rb +5 -4
  208. data/lib/ibex/normalize/expression.rb +8 -6
  209. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  210. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  211. data/lib/ibex/normalize/lexer.rb +3 -3
  212. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  213. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  214. data/lib/ibex/normalize/parameters.rb +31 -4
  215. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  216. data/lib/ibex/normalize.rb +10 -20
  217. data/lib/ibex/racc_migration/report.rb +6 -3
  218. data/lib/ibex/rake_task.rb +1 -1
  219. data/lib/ibex/samples.rb +43 -13
  220. data/lib/ibex/table_artifact/builder.rb +266 -0
  221. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  222. data/lib/ibex/table_artifact/document.rb +39 -0
  223. data/lib/ibex/table_artifact/executor.rb +187 -0
  224. data/lib/ibex/table_artifact/serializer.rb +57 -0
  225. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  226. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  227. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  228. data/lib/ibex/table_artifact/validator.rb +253 -0
  229. data/lib/ibex/table_artifact.rb +88 -0
  230. data/lib/ibex/table_simulation/result.rb +6 -2
  231. data/lib/ibex/table_simulation/step.rb +3 -1
  232. data/lib/ibex/tables.rb +14 -0
  233. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  234. data/lib/ibex/verification_report/builder.rb +159 -0
  235. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  236. data/lib/ibex/verification_report/logical_path.rb +68 -0
  237. data/lib/ibex/verification_report/validator.rb +329 -0
  238. data/lib/ibex/verification_report.rb +69 -0
  239. data/lib/ibex/verify/action_correspondence.rb +138 -0
  240. data/lib/ibex/verify/language_witness.rb +295 -0
  241. data/lib/ibex/verify/reference_collection.rb +190 -0
  242. data/lib/ibex/verify/result.rb +73 -0
  243. data/lib/ibex/verify/verifier.rb +598 -0
  244. data/lib/ibex/verify.rb +15 -0
  245. data/lib/ibex/version.rb +1 -1
  246. data/lib/ibex/watch/runner.rb +10 -1
  247. data/lib/ibex/watch/source_snapshot.rb +13 -8
  248. data/lib/ibex.rb +10 -0
  249. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  250. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  251. data/schema/bison-import-v1.schema.json +106 -0
  252. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  253. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  254. data/schema/construction-profile-v1.schema.json +779 -0
  255. data/schema/diff-v1.schema.json +41 -0
  256. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  257. data/schema/equiv-v1.schema.json +64 -0
  258. data/schema/error-ux-review-v1.schema.json +609 -0
  259. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  260. data/schema/error-ux-round2-v1.schema.json +519 -0
  261. data/schema/explain-v1.schema.json +63 -1
  262. data/schema/fix-v1.schema.json +64 -0
  263. data/schema/fix-v2.schema.json +66 -0
  264. data/schema/fix-v3.schema.json +101 -0
  265. data/schema/fuzz-regression-v1.schema.json +79 -0
  266. data/schema/fuzz-v1.schema.json +114 -0
  267. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  268. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  269. data/schema/grammar-ir.schema.json +271 -0
  270. data/schema/ielr-benchmark-v1.schema.json +85 -0
  271. data/schema/lexer-profile-v1.schema.json +491 -0
  272. data/schema/metrics-v1.schema.json +60 -0
  273. data/schema/reduce-v1.schema.json +22 -0
  274. data/schema/reduce-v2.schema.json +63 -0
  275. data/schema/table-artifact-v1.schema.json +377 -0
  276. data/schema/verification-report-v1.schema.json +222 -0
  277. data/schema/verify-v1.schema.json +42 -0
  278. data/sig/ibex/analysis/digraph.rbs +19 -0
  279. data/sig/ibex/bison_import/importer.rbs +140 -0
  280. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  281. data/sig/ibex/bison_import.rbs +95 -0
  282. data/sig/ibex/bounded_subprocess.rbs +76 -0
  283. data/sig/ibex/cli/ambiguity.rbs +6 -0
  284. data/sig/ibex/cli/analysis.rbs +43 -0
  285. data/sig/ibex/cli/bison_import.rbs +32 -0
  286. data/sig/ibex/cli/config.rbs +35 -0
  287. data/sig/ibex/cli/diagnostics.rbs +16 -3
  288. data/sig/ibex/cli/documentation.rbs +5 -1
  289. data/sig/ibex/cli/equiv.rbs +49 -0
  290. data/sig/ibex/cli/error_messages.rbs +6 -0
  291. data/sig/ibex/cli/explain.rbs +6 -0
  292. data/sig/ibex/cli/fix.rbs +53 -0
  293. data/sig/ibex/cli/formatting.rbs +5 -1
  294. data/sig/ibex/cli/fuzz.rbs +47 -0
  295. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  296. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  297. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  298. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  299. data/sig/ibex/cli/ir_tools.rbs +7 -19
  300. data/sig/ibex/cli/outputs.rbs +9 -3
  301. data/sig/ibex/cli/reduce.rbs +60 -0
  302. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  303. data/sig/ibex/cli/samples.rbs +8 -0
  304. data/sig/ibex/cli/verify.rbs +28 -0
  305. data/sig/ibex/cli/watch.rbs +2 -2
  306. data/sig/ibex/cli.rbs +79 -5
  307. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  308. data/sig/ibex/codegen/explain.rbs +45 -25
  309. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  310. data/sig/ibex/codegen/rbs.rbs +12 -10
  311. data/sig/ibex/codegen/report.rbs +12 -12
  312. data/sig/ibex/codegen/ruby.rbs +18 -15
  313. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  314. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  315. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  316. data/sig/ibex/configuration/explanation.rbs +195 -0
  317. data/sig/ibex/configuration/inspector.rbs +53 -0
  318. data/sig/ibex/configuration.rbs +242 -0
  319. data/sig/ibex/coverage/collector.rbs +16 -14
  320. data/sig/ibex/coverage/event_stream.rbs +12 -10
  321. data/sig/ibex/coverage/report.rbs +20 -18
  322. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  323. data/sig/ibex/delta_reducer.rbs +38 -0
  324. data/sig/ibex/diff.rbs +47 -0
  325. data/sig/ibex/equiv/machine.rbs +54 -0
  326. data/sig/ibex/equiv.rbs +129 -0
  327. data/sig/ibex/fix.rbs +138 -0
  328. data/sig/ibex/frontend/ast.rbs +33 -7
  329. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  330. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  331. data/sig/ibex/frontend/formatter.rbs +38 -30
  332. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  333. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  334. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  335. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  336. data/sig/ibex/frontend/resolution.rbs +3 -2
  337. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  338. data/sig/ibex/frontend/source_span.rbs +5 -2
  339. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  340. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  341. data/sig/ibex/fuzz.rbs +71 -0
  342. data/sig/ibex/generation_input.rbs +2 -2
  343. data/sig/ibex/generation_manifest.rbs +35 -32
  344. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  345. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  346. data/sig/ibex/grammar_tests.rbs +2 -2
  347. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  348. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  349. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  350. data/sig/ibex/ir/parser_contract.rbs +69 -0
  351. data/sig/ibex/ir/serialize.rbs +63 -28
  352. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  353. data/sig/ibex/ir/validator/base.rbs +35 -31
  354. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  355. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  356. data/sig/ibex/ir.rbs +9 -3
  357. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  358. data/sig/ibex/lalr/builder.rbs +75 -24
  359. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  360. data/sig/ibex/lalr/counterexample.rbs +17 -14
  361. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  362. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  363. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  364. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  365. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  366. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  367. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  368. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  369. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  370. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  371. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  372. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  373. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  374. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  375. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  376. data/sig/ibex/lalr.rbs +11 -1
  377. data/sig/ibex/location.rbs +6 -6
  378. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  379. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  380. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  381. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  382. data/sig/ibex/lsp/position_codec.rbs +8 -4
  383. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  384. data/sig/ibex/lsp/request_support.rbs +8 -8
  385. data/sig/ibex/lsp/server.rbs +16 -16
  386. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  387. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  388. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  389. data/sig/ibex/lsp/transport.rbs +18 -6
  390. data/sig/ibex/lsp/workspace.rbs +3 -2
  391. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  392. data/sig/ibex/messages.rbs +26 -0
  393. data/sig/ibex/metrics.rbs +50 -0
  394. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  395. data/sig/ibex/normalize/expander.rbs +2 -2
  396. data/sig/ibex/normalize/expression.rbs +10 -8
  397. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  398. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  399. data/sig/ibex/normalize/lexer.rbs +4 -4
  400. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  401. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  402. data/sig/ibex/normalize/parameters.rbs +13 -8
  403. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  404. data/sig/ibex/normalize.rbs +11 -5
  405. data/sig/ibex/racc_migration/report.rbs +8 -6
  406. data/sig/ibex/rake_task.rbs +1 -1
  407. data/sig/ibex/samples.rbs +13 -4
  408. data/sig/ibex/table_artifact/builder.rbs +93 -0
  409. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  410. data/sig/ibex/table_artifact/document.rbs +27 -0
  411. data/sig/ibex/table_artifact/executor.rbs +66 -0
  412. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  413. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  414. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  415. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  416. data/sig/ibex/table_artifact/validator.rbs +66 -0
  417. data/sig/ibex/table_artifact.rbs +32 -0
  418. data/sig/ibex/table_simulation/result.rbs +8 -4
  419. data/sig/ibex/table_simulation/step.rbs +4 -2
  420. data/sig/ibex/tables.rbs +10 -9
  421. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  422. data/sig/ibex/verification_report/builder.rbs +53 -0
  423. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  424. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  425. data/sig/ibex/verification_report/validator.rbs +114 -0
  426. data/sig/ibex/verification_report.rbs +44 -0
  427. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  428. data/sig/ibex/verify/language_witness.rbs +112 -0
  429. data/sig/ibex/verify/reference_collection.rbs +63 -0
  430. data/sig/ibex/verify/result.rbs +52 -0
  431. data/sig/ibex/verify/verifier.rbs +170 -0
  432. data/sig/ibex/verify.rbs +6 -0
  433. data/sig/ibex/watch/runner.rbs +12 -2
  434. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  435. metadata +232 -12
  436. data/lib/ibex/ir/migration.rb +0 -120
  437. data/sig/ibex/ir/migration.rbs +0 -34
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ class MetadataValidator
6
+ include ValidationSupport
7
+
8
+ # @rbs (Hash[String, ValidationSupport::json_value] payload, symbols: Array[Hash[String, ValidationSupport::json_value]],
9
+ # productions: Array[Hash[String, ValidationSupport::json_value]]) -> void
10
+ def initialize(payload, symbols:, productions:)
11
+ @payload = payload
12
+ @symbols = symbols
13
+ @productions = productions
14
+ @terminal_ids = ids_for_kind(symbols, "terminal")
15
+ @nonterminal_ids = ids_for_kind(symbols, "nonterminal")
16
+ end
17
+
18
+ # @rbs () -> void
19
+ def validate!
20
+ validate_semantic_actions(@payload.fetch("semantic_actions"))
21
+ validate_recovery(@payload.fetch("recovery"))
22
+ validate_cst(@payload.fetch("cst"))
23
+ end
24
+
25
+ private
26
+
27
+ # @rbs (Array[Hash[String, ValidationSupport::json_value]] symbols, String kind) -> Set[Integer]
28
+ def ids_for_kind(symbols, kind)
29
+ symbols.filter_map do |symbol|
30
+ next unless symbol.fetch("kind") == kind
31
+
32
+ symbol.fetch("id") #: Integer
33
+ end.to_set
34
+ end
35
+
36
+ # @rbs (ValidationSupport::json_value data) -> void
37
+ def validate_semantic_actions(data)
38
+ path = "$.payload.semantic_actions"
39
+ data = record(data, path, %w[binding verified slots])
40
+ enum(data.fetch("binding"), "#{path}.binding", ["opaque-wrapper-production-id-v1"])
41
+ invalid("#{path}.verified", "must be false") unless boolean(data.fetch("verified"), "#{path}.verified") == false
42
+ slots = integer_ids(data.fetch("slots"), "#{path}.slots")
43
+ expected = @productions.filter_map { |production| production.fetch("action_slot") }
44
+ invalid("#{path}.slots", "must match production action slots") unless slots == expected
45
+ end
46
+
47
+ # @rbs (ValidationSupport::json_value data) -> void
48
+ def validate_recovery(data)
49
+ path = "$.payload.recovery"
50
+ data = record(data, path, %w[sync_token_ids on_error_reduce_symbol_ids])
51
+ sync_ids = integer_ids(data.fetch("sync_token_ids"), "#{path}.sync_token_ids", sorted: false)
52
+ sync_ids.each do |id|
53
+ invalid("#{path}.sync_token_ids", "must reference terminals") unless @terminal_ids.include?(id)
54
+ end
55
+ groups = array(data.fetch("on_error_reduce_symbol_ids"), "#{path}.on_error_reduce_symbol_ids")
56
+ groups.each_with_index do |group, index|
57
+ ids = integer_ids(group, "#{path}.on_error_reduce_symbol_ids[#{index}]", sorted: false)
58
+ invalid("#{path}.on_error_reduce_symbol_ids[#{index}]", "must not be empty") if ids.empty?
59
+ ids.each do |id|
60
+ invalid("#{path}.on_error_reduce_symbol_ids[#{index}]", "must reference nonterminals") unless
61
+ @nonterminal_ids.include?(id)
62
+ end
63
+ end
64
+ end
65
+
66
+ # @rbs (ValidationSupport::json_value data) -> void
67
+ def validate_cst(data)
68
+ return if data.nil?
69
+
70
+ path = "$.payload.cst"
71
+ data = record(data, path, %w[version trivia_policy kinds slots])
72
+ invalid("#{path}.version", "must be 1") unless integer(data.fetch("version"), "#{path}.version") == 1
73
+ enum(data.fetch("trivia_policy"), "#{path}.trivia_policy", %w[leading balanced drop])
74
+ kind_count = validate_cst_kinds(data.fetch("kinds"), "#{path}.kinds")
75
+ validate_cst_slots(data.fetch("slots"), "#{path}.slots", kind_count)
76
+ end
77
+
78
+ # @rbs (ValidationSupport::json_value data, String path) -> Integer
79
+ def validate_cst_kinds(data, path)
80
+ keys = %w[names terminal_range nonterminal_range named named_nonterminals trivia synthetic]
81
+ data = record(data, path, keys)
82
+ names = array(data.fetch("names"), "#{path}.names") #: Array[String]
83
+ names.each_with_index { |name, index| string(name, "#{path}.names[#{index}]") }
84
+ validate_range(data.fetch("terminal_range"), "#{path}.terminal_range", names.length)
85
+ validate_range(data.fetch("nonterminal_range"), "#{path}.nonterminal_range", names.length)
86
+ %w[named trivia synthetic].each { |key| validate_named_kinds(data.fetch(key), "#{path}.#{key}", names) }
87
+ validate_named_nonterminals(data.fetch("named_nonterminals"), "#{path}.named_nonterminals", names.length)
88
+ names.length
89
+ end
90
+
91
+ # @rbs (ValidationSupport::json_value data, String path, Integer limit) -> void
92
+ def validate_range(data, path, limit)
93
+ values = array(data, path)
94
+ invalid(path, "must contain two bounds") unless values.length == 2
95
+ start = integer(values.fetch(0), "#{path}[0]", minimum: 0)
96
+ finish = integer(values.fetch(1), "#{path}[1]", minimum: 0)
97
+ invalid(path, "must be an ordered half-open range") unless finish.between?(start, limit)
98
+ end
99
+
100
+ # @rbs (ValidationSupport::json_value data, String path, Array[String] names) -> void
101
+ def validate_named_kinds(data, path, names)
102
+ entries = array(data, path)
103
+ ids = entries.map.with_index do |entry, index|
104
+ entry_path = "#{path}[#{index}]"
105
+ entry = record(entry, entry_path, %w[name id])
106
+ name = string(entry.fetch("name"), "#{entry_path}.name")
107
+ id = integer(entry.fetch("id"), "#{entry_path}.id", minimum: 0)
108
+ invalid(entry_path, "does not match the kind name table") unless names[id] == name
109
+ id
110
+ end
111
+ sorted_unique!(ids, path)
112
+ end
113
+
114
+ # @rbs (ValidationSupport::json_value data, String path, Integer kind_count) -> void
115
+ def validate_named_nonterminals(data, path, kind_count)
116
+ pairs = array(data, path).map.with_index do |entry, index|
117
+ entry_path = "#{path}[#{index}]"
118
+ entry = record(entry, entry_path, %w[kind_id symbol_id])
119
+ kind_id = integer(entry.fetch("kind_id"), "#{entry_path}.kind_id", minimum: 0)
120
+ invalid("#{entry_path}.kind_id", "references a missing kind") unless kind_id < kind_count
121
+ symbol_id = integer(entry.fetch("symbol_id"), "#{entry_path}.symbol_id", minimum: 0)
122
+ invalid("#{entry_path}.symbol_id", "must reference a nonterminal") unless @nonterminal_ids.include?(symbol_id)
123
+ [kind_id, symbol_id]
124
+ end
125
+ return if pairs == pairs.sort && pairs.map(&:first).uniq.length == pairs.length
126
+
127
+ invalid(path,
128
+ "must be sorted with unique kind ids")
129
+ end
130
+
131
+ # @rbs (ValidationSupport::json_value data, String path, Integer kind_count) -> void
132
+ def validate_cst_slots(data, path, kind_count)
133
+ production_ids = array(data, path).map.with_index do |slot, index|
134
+ slot_path = "#{path}[#{index}]"
135
+ slot = record(slot, slot_path, %w[production_id node_kind_id node_name fields])
136
+ production_id = integer(slot.fetch("production_id"), "#{slot_path}.production_id", minimum: 0)
137
+ unless production_id < @productions.length
138
+ invalid("#{slot_path}.production_id",
139
+ "references a missing production")
140
+ end
141
+ node_kind = integer(slot.fetch("node_kind_id"), "#{slot_path}.node_kind_id", minimum: 0)
142
+ invalid("#{slot_path}.node_kind_id", "references a missing kind") unless node_kind < kind_count
143
+ string(slot.fetch("node_name"), "#{slot_path}.node_name")
144
+ validate_fields(slot.fetch("fields"), "#{slot_path}.fields", @productions.fetch(production_id))
145
+ production_id
146
+ end
147
+ sorted_unique!(production_ids, path)
148
+ end
149
+
150
+ # @rbs (ValidationSupport::json_value data, String path,
151
+ # Hash[String, ValidationSupport::json_value] production) -> void
152
+ def validate_fields(data, path, production)
153
+ names = array(data, path).map.with_index do |field, index|
154
+ field_path = "#{path}[#{index}]"
155
+ field = record(field, field_path, %w[name index extraction])
156
+ name = string(field.fetch("name"), "#{field_path}.name")
157
+ field_index = integer(field.fetch("index"), "#{field_path}.index", minimum: 0)
158
+ rhs_length = production.fetch("rhs_length") #: Integer
159
+ invalid("#{field_path}.index", "exceeds production rhs") unless field_index < rhs_length
160
+ extraction = field.fetch("extraction")
161
+ enum(extraction, "#{field_path}.extraction", %w[repetition separated_list]) unless extraction.nil?
162
+ name
163
+ end
164
+ invalid(path, "must have unique field names") unless names.uniq.length == names.length
165
+ end
166
+
167
+ # @rbs (ValidationSupport::json_value data, String path, ?sorted: bool) -> Array[Integer]
168
+ def integer_ids(data, path, sorted: true)
169
+ ids = array(data, path).map.with_index { |id, index| integer(id, "#{path}[#{index}]", minimum: 0) }
170
+ if sorted
171
+ sorted_unique!(ids, path)
172
+ else
173
+ invalid(path, "must contain unique ids") unless ids.uniq.length == ids.length
174
+ end
175
+ ids
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ module ValidationSupport
6
+ # @rbs!
7
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
8
+
9
+ DIGEST = /\Asha256:[0-9a-f]{64}\z/ #: Regexp
10
+
11
+ private
12
+
13
+ # @rbs (json_value value, String path, Array[String] keys) -> Hash[String, json_value]
14
+ def record(value, path, keys)
15
+ invalid(path, "must be an object") unless value.is_a?(Hash)
16
+ invalid(path, "must use string keys") unless value.keys.all?(String)
17
+
18
+ missing = keys - value.keys
19
+ extra = value.keys - keys
20
+ invalid(path, "is missing #{missing.join(', ')}") unless missing.empty?
21
+ invalid(path, "contains unknown fields #{extra.join(', ')}") unless extra.empty?
22
+ value #: Hash[String, json_value]
23
+ end
24
+
25
+ # @rbs (json_value value, String path) -> Array[json_value]
26
+ def array(value, path)
27
+ invalid(path, "must be an array") unless value.is_a?(Array)
28
+ value #: Array[json_value]
29
+ end
30
+
31
+ # @rbs (json_value value, String path, ?allow_empty: bool) -> String
32
+ def string(value, path, allow_empty: false)
33
+ invalid(path, "must be a string") unless value.is_a?(String)
34
+ invalid(path, "must not be empty") if !allow_empty && value.empty?
35
+ value #: String
36
+ end
37
+
38
+ # @rbs (json_value value, String path, ?minimum: Integer?) -> Integer
39
+ def integer(value, path, minimum: nil)
40
+ invalid(path, "must be an integer") unless value.is_a?(Integer)
41
+ invalid(path, "must be at least #{minimum}") if minimum && value < minimum
42
+ value #: Integer
43
+ end
44
+
45
+ # @rbs (json_value value, String path) -> bool
46
+ def boolean(value, path)
47
+ invalid(path, "must be true or false") unless [true, false].include?(value)
48
+ value #: bool
49
+ end
50
+
51
+ # @rbs [T] (T value, String path, Array[T] values) -> T
52
+ def enum(value, path, values)
53
+ invalid(path, "must be one of #{values.join(', ')}") unless values.include?(value)
54
+ value
55
+ end
56
+
57
+ # @rbs (json_value value, String path) -> String
58
+ def digest(value, path)
59
+ invalid(path, "must be a sha256 digest") unless value.is_a?(String) && DIGEST.match?(value)
60
+ value
61
+ end
62
+
63
+ # @rbs (json_value value, String path) -> String?
64
+ def nullable_string(value, path)
65
+ string(value, path, allow_empty: true) unless value.nil?
66
+ value #: String?
67
+ end
68
+
69
+ # @rbs (json_value value, String path, ?minimum: Integer?) -> Integer?
70
+ def nullable_integer(value, path, minimum: nil)
71
+ integer(value, path, minimum: minimum) unless value.nil?
72
+ value #: Integer?
73
+ end
74
+
75
+ # @rbs (Array[json_value] values, String path) -> void
76
+ def sorted_unique!(values, path)
77
+ invalid(path, "must be sorted and unique") unless values == values.sort.uniq
78
+ end
79
+
80
+ # @rbs (String path, String message) -> bot
81
+ def invalid(path, message)
82
+ raise ValidationError, "#{path} #{message}"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ class TableValidator
6
+ include ValidationSupport
7
+
8
+ # @rbs!
9
+ # private def integer_array: (ValidationSupport::json_value value, String path,
10
+ # allow_nil: false, minimum: Integer?) -> Array[Integer]
11
+ # | (ValidationSupport::json_value value, String path,
12
+ # allow_nil: true, minimum: Integer?) -> Array[Integer?]
13
+
14
+ # @rbs (Hash[String, ValidationSupport::json_value] data, state_count: Integer, production_count: Integer,
15
+ # terminal_ids: Set[Integer], nonterminal_ids: Set[Integer], representation: String) -> void
16
+ def initialize(data, state_count:, production_count:, terminal_ids:, nonterminal_ids:, representation:)
17
+ @data = data
18
+ @state_count = state_count
19
+ @production_count = production_count
20
+ @terminal_ids = terminal_ids
21
+ @nonterminal_ids = nonterminal_ids
22
+ @representation = representation
23
+ end
24
+
25
+ # @rbs () -> void
26
+ def validate!
27
+ record(@data, "$.payload.tables", %w[actions gotos default_actions])
28
+ validate_actions(@data.fetch("actions"))
29
+ validate_gotos(@data.fetch("gotos"))
30
+ validate_defaults(@data.fetch("default_actions"))
31
+ end
32
+
33
+ private
34
+
35
+ # @rbs (ValidationSupport::json_value table) -> void
36
+ def validate_actions(table)
37
+ invalid("$.payload.tables.actions", "must be an object") unless table.is_a?(Hash)
38
+ table_data = table #: Hash[String, ValidationSupport::json_value]
39
+ encoding = table_data["encoding"]
40
+ expected = @representation == "compact" ? "signed-row-displacement-v1" : "signed-sparse-rows-v1"
41
+ invalid("$.payload.tables.actions.encoding", "does not match table representation") unless encoding == expected
42
+ case encoding
43
+ when "signed-sparse-rows-v1" then validate_sparse_actions(table_data)
44
+ when "signed-row-displacement-v1" then validate_compact_actions(table_data)
45
+ else invalid("$.payload.tables.actions.encoding", "is unsupported")
46
+ end
47
+ end
48
+
49
+ # @rbs (Hash[String, ValidationSupport::json_value] table) -> void
50
+ def validate_sparse_actions(table)
51
+ record(table, "$.payload.tables.actions", %w[encoding rows])
52
+ rows = array(table.fetch("rows"), "$.payload.tables.actions.rows")
53
+ invalid("$.payload.tables.actions.rows", "must contain one row per state") unless rows.length == @state_count
54
+ rows.each_with_index do |row, state|
55
+ token_ids = array(row, "$.payload.tables.actions.rows[#{state}]").map.with_index do |cell, index|
56
+ path = "$.payload.tables.actions.rows[#{state}][#{index}]"
57
+ cell = record(cell, path, %w[token_id code])
58
+ token_id = integer(cell.fetch("token_id"), "#{path}.token_id", minimum: 0)
59
+ invalid("#{path}.token_id", "must reference a terminal") unless @terminal_ids.include?(token_id)
60
+ validate_token_action(cell.fetch("code"), token_id, "#{path}.code")
61
+ token_id
62
+ end
63
+ sorted_unique!(token_ids, "$.payload.tables.actions.rows[#{state}]")
64
+ end
65
+ end
66
+
67
+ # @rbs (Hash[String, ValidationSupport::json_value] table) -> void
68
+ def validate_compact_actions(table)
69
+ keys = %w[encoding row_count column_count offsets codes checks]
70
+ record(table, "$.payload.tables.actions", keys)
71
+ enum(table.fetch("encoding"), "$.payload.tables.actions.encoding", ["signed-row-displacement-v1"])
72
+ validate_row_count(table, "$.payload.tables.actions")
73
+ column_count = nullable_integer(table.fetch("column_count"), "$.payload.tables.actions.column_count",
74
+ minimum: 1)
75
+ rows = validate_displacement(table, value_key: "codes", path: "$.payload.tables.actions") do |column, code,
76
+ cell_path|
77
+ invalid(cell_path, "column must reference a terminal") unless @terminal_ids.include?(column)
78
+ invalid(cell_path, "column exceeds column_count") if column_count && column >= column_count
79
+ validate_token_action(code, column, "#{cell_path}.code")
80
+ end
81
+ validate_canonical_displacement(
82
+ table, rows, value_key: "codes", width_key: "column_count", width: column_count, dense: false,
83
+ path: "$.payload.tables.actions"
84
+ )
85
+ end
86
+
87
+ # @rbs (ValidationSupport::json_value table) -> void
88
+ def validate_gotos(table)
89
+ invalid("$.payload.tables.gotos", "must be an object") unless table.is_a?(Hash)
90
+ table_data = table #: Hash[String, ValidationSupport::json_value]
91
+ encoding = table_data["encoding"]
92
+ expected = @representation == "compact" ? "row-displacement-v1" : "sparse-rows-v1"
93
+ invalid("$.payload.tables.gotos.encoding", "does not match table representation") unless encoding == expected
94
+ case encoding
95
+ when "sparse-rows-v1" then validate_sparse_gotos(table_data)
96
+ when "row-displacement-v1" then validate_compact_gotos(table_data)
97
+ else invalid("$.payload.tables.gotos.encoding", "is unsupported")
98
+ end
99
+ end
100
+
101
+ # @rbs (Hash[String, ValidationSupport::json_value] table) -> void
102
+ def validate_sparse_gotos(table)
103
+ record(table, "$.payload.tables.gotos", %w[encoding rows])
104
+ rows = array(table.fetch("rows"), "$.payload.tables.gotos.rows")
105
+ invalid("$.payload.tables.gotos.rows", "must contain one row per state") unless rows.length == @state_count
106
+ rows.each_with_index do |row, state|
107
+ symbol_ids = array(row, "$.payload.tables.gotos.rows[#{state}]").map.with_index do |cell, index|
108
+ path = "$.payload.tables.gotos.rows[#{state}][#{index}]"
109
+ cell = record(cell, path, %w[symbol_id state])
110
+ symbol_id = integer(cell.fetch("symbol_id"), "#{path}.symbol_id", minimum: 0)
111
+ invalid("#{path}.symbol_id", "must reference a nonterminal") unless @nonterminal_ids.include?(symbol_id)
112
+ validate_state(cell.fetch("state"), "#{path}.state")
113
+ symbol_id
114
+ end
115
+ sorted_unique!(symbol_ids, "$.payload.tables.gotos.rows[#{state}]")
116
+ end
117
+ end
118
+
119
+ # @rbs (Hash[String, ValidationSupport::json_value] table) -> void
120
+ def validate_compact_gotos(table)
121
+ keys = %w[encoding row_count dense_width offsets values checks]
122
+ record(table, "$.payload.tables.gotos", keys)
123
+ enum(table.fetch("encoding"), "$.payload.tables.gotos.encoding", ["row-displacement-v1"])
124
+ validate_row_count(table, "$.payload.tables.gotos")
125
+ dense_width = nullable_integer(table.fetch("dense_width"), "$.payload.tables.gotos.dense_width", minimum: 1)
126
+ rows = validate_displacement(table, value_key: "values", path: "$.payload.tables.gotos") do |column, state,
127
+ cell_path|
128
+ invalid(cell_path, "column must reference a nonterminal") unless @nonterminal_ids.include?(column)
129
+ invalid(cell_path, "column exceeds dense_width") if dense_width && column >= dense_width
130
+ validate_state(state, "#{cell_path}.state")
131
+ end
132
+ validate_canonical_displacement(
133
+ table, rows, value_key: "values", width_key: "dense_width", width: dense_width, dense: true,
134
+ path: "$.payload.tables.gotos"
135
+ )
136
+ end
137
+
138
+ # @rbs (Hash[String, ValidationSupport::json_value] table, String path) -> void
139
+ def validate_row_count(table, path)
140
+ count = integer(table.fetch("row_count"), "#{path}.row_count", minimum: 1)
141
+ invalid("#{path}.row_count", "must equal state_count") unless count == @state_count
142
+ end
143
+
144
+ # @rbs (Hash[String, ValidationSupport::json_value] table, value_key: String, path: String)
145
+ # { (Integer, ValidationSupport::json_value, String) -> void } -> Array[Hash[Integer, ValidationSupport::json_value]]
146
+ def validate_displacement(table, value_key:, path:)
147
+ offsets = integer_array(table.fetch("offsets"), "#{path}.offsets", allow_nil: false, minimum: 0)
148
+ invalid("#{path}.offsets", "must contain one offset per state") unless offsets.length == @state_count
149
+ minimum = value_key == "codes" ? nil : 0
150
+ values = integer_array(table.fetch(value_key), "#{path}.#{value_key}", allow_nil: true, minimum: minimum)
151
+ checks = integer_array(table.fetch("checks"), "#{path}.checks", allow_nil: true, minimum: 0)
152
+ invalid(path, "#{value_key} and checks must have equal length") unless values.length == checks.length
153
+
154
+ rows = Array.new(@state_count) { {} } #: Array[Hash[Integer, ValidationSupport::json_value]]
155
+ checks.each_index do |index|
156
+ row = checks[index]
157
+ value = values[index]
158
+ invalid("#{path}[#{index}]", "value and check occupancy must match") unless row.nil? == value.nil?
159
+ next unless row
160
+
161
+ invalid("#{path}.checks[#{index}]", "references a missing state") unless row.between?(0, @state_count - 1)
162
+ column = index - offsets.fetch(row)
163
+ invalid("#{path}[#{index}]", "has a negative column") if column.negative?
164
+ yield(column, value, "#{path}[#{index}]")
165
+ rows.fetch(row)[column] = value
166
+ end
167
+ rows
168
+ end
169
+
170
+ # @rbs (Hash[String, ValidationSupport::json_value] table,
171
+ # Array[Hash[Integer, ValidationSupport::json_value]] rows, value_key: String,
172
+ # width_key: String, width: Integer?, dense: bool, path: String) -> void
173
+ def validate_canonical_displacement(table, rows, value_key:, width_key:, width:, dense:, path:)
174
+ canonical = Tables::Compact.build(rows, dense: dense)
175
+ expected = [canonical.offsets, canonical.values, canonical.checks]
176
+ actual = [table.fetch("offsets"), table.fetch(value_key), table.fetch("checks")]
177
+ invalid(path, "must use the canonical minimal row-displacement layout") unless actual == expected
178
+
179
+ expected_width = if width_key == "column_count"
180
+ rows.flat_map(&:keys).max.to_i + 1
181
+ else
182
+ canonical.dense_width
183
+ end
184
+ expected_width = nil if expected_width && (@state_count * expected_width) > Tables::Compact::DENSE_CELL_LIMIT
185
+ invalid("#{path}.#{width_key}", "does not match the canonical table width") unless width == expected_width
186
+ end
187
+
188
+ # @rbs skip
189
+ def integer_array(value, path, allow_nil:, minimum:)
190
+ array(value, path).map.with_index do |item, index|
191
+ next if allow_nil && item.nil?
192
+
193
+ integer(item, "#{path}[#{index}]", minimum: minimum)
194
+ end
195
+ end
196
+
197
+ # @rbs (ValidationSupport::json_value defaults) -> void
198
+ def validate_defaults(defaults)
199
+ values = array(defaults, "$.payload.tables.default_actions")
200
+ unless values.length == @state_count
201
+ invalid("$.payload.tables.default_actions",
202
+ "must contain one entry per state")
203
+ end
204
+ values.each_with_index do |code, state|
205
+ next if code.nil?
206
+
207
+ path = "$.payload.tables.default_actions[#{state}]"
208
+ code = integer(code, path)
209
+ invalid(path, "must be an error or reduction") if code >= 0
210
+ end
211
+ end
212
+
213
+ # @rbs (ValidationSupport::json_value code, Integer token_id, String path) -> void
214
+ def validate_token_action(code, token_id, path)
215
+ code = integer(code, path)
216
+ invalid(path, "accept is only valid for $eof") if code.zero? && !token_id.zero?
217
+ invalid(path, "$eof cannot be shifted") if token_id.zero? && code.positive?
218
+ validate_action_code(code, path)
219
+ end
220
+
221
+ # @rbs (ValidationSupport::json_value code, String path) -> void
222
+ def validate_action_code(code, path)
223
+ code = integer(code, path)
224
+ return if [0, -1].include?(code)
225
+ return validate_state(code - 1, path) if code.positive?
226
+
227
+ production = -2 - code
228
+ invalid(path, "references a missing production") unless production.between?(0, @production_count - 1)
229
+ end
230
+
231
+ # @rbs (ValidationSupport::json_value state, String path) -> void
232
+ def validate_state(state, path)
233
+ state = integer(state, path, minimum: 0)
234
+ invalid(path, "references a missing state") unless state.between?(0, @state_count - 1)
235
+ end
236
+ end
237
+ end
238
+ end