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,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ # Projects validated Automaton IR into an action-free executable sidecar.
6
+ class Builder
7
+ # @rbs!
8
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
9
+ # type json_hash = Hash[String, untyped]
10
+ # type action_rows = Array[Hash[Integer, IR::runtime_action]]
11
+ # type action_table = action_rows | Tables::CompactActions
12
+ # type goto_rows = Array[Hash[Integer, Integer]]
13
+ # type goto_table = goto_rows | Tables::Compact
14
+
15
+ include CSTProjection
16
+
17
+ REPRESENTATIONS = %i[plain compact].freeze #: Array[Symbol]
18
+
19
+ # @rbs (IR::Automaton automaton, ?representation: Symbol | String, ?cst_trivia: Symbol | String?,
20
+ # ?omit_action_call: bool?) -> void
21
+ def initialize(automaton, representation: :compact, cst_trivia: nil, omit_action_call: nil)
22
+ @automaton = automaton
23
+ @grammar = automaton.grammar
24
+ @representation = representation.to_sym
25
+ @cst_trivia = effective_cst_trivia(cst_trivia)
26
+ @omit_action_call = omit_action_call.nil? ? @grammar.options.fetch(:omit_action_call) : omit_action_call
27
+ return if REPRESENTATIONS.include?(@representation)
28
+
29
+ raise ArgumentError, "representation must be :plain or :compact"
30
+ end
31
+
32
+ # @rbs () -> Document
33
+ def build
34
+ source_identity = {
35
+ "grammar_digest" => @automaton.grammar_digest,
36
+ "automaton_digest" => automaton_digest
37
+ }
38
+ payload = build_payload(source_identity)
39
+ cost = build_cost(payload)
40
+ identity = source_identity.merge(
41
+ "payload_digest" => Serializer.digest(payload),
42
+ "cst_metadata_digest" => Serializer.digest(payload.fetch("cst")),
43
+ "recovery_metadata_digest" => Serializer.digest(payload.fetch("recovery"))
44
+ )
45
+ Document.new(
46
+ "artifact_type" => ARTIFACT_TYPE,
47
+ "schema_version" => SCHEMA_VERSION,
48
+ "identity" => identity,
49
+ "payload" => payload,
50
+ "cost" => cost
51
+ )
52
+ end
53
+
54
+ private
55
+
56
+ # @rbs (Hash[String, String] source_identity) -> json_hash
57
+ def build_payload(source_identity)
58
+ {
59
+ "table_format" => table_format,
60
+ "source" => source_identity,
61
+ "state_count" => @automaton.states.length,
62
+ "symbols" => symbols,
63
+ "tokens" => tokens,
64
+ "entry_states" => entry_states,
65
+ "tables" => tables,
66
+ "productions" => productions,
67
+ "cst" => cst,
68
+ "recovery" => recovery,
69
+ "semantic_actions" => semantic_actions
70
+ }
71
+ end
72
+
73
+ # @rbs () -> String
74
+ def automaton_digest
75
+ "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(@automaton))}"
76
+ end
77
+
78
+ # @rbs () -> json_hash
79
+ def table_format
80
+ {
81
+ "family" => "ibex-runtime-parser-table",
82
+ "version" => Runtime::PARSER_TABLE_FORMAT_VERSION,
83
+ "representation" => @representation.to_s,
84
+ "action_encoding" => @representation == :compact ? "signed-row-displacement-v1" : "signed-sparse-rows-v1",
85
+ "goto_encoding" => @representation == :compact ? "row-displacement-v1" : "sparse-rows-v1"
86
+ }
87
+ end
88
+
89
+ # @rbs () -> Array[json_hash]
90
+ def symbols
91
+ @grammar.symbols.sort_by(&:id).map do |symbol|
92
+ {
93
+ "id" => symbol.id,
94
+ "name" => symbol.name,
95
+ "kind" => symbol.kind.to_s,
96
+ "display_name" => symbol.display_name
97
+ }
98
+ end
99
+ end
100
+
101
+ # @rbs () -> Array[json_hash]
102
+ def tokens
103
+ @grammar.terminals.sort_by(&:id).map do |symbol|
104
+ { "id" => symbol.id, "name" => symbol.name, "display_name" => symbol.display_name }
105
+ end
106
+ end
107
+
108
+ # @rbs () -> Array[json_hash]
109
+ def entry_states
110
+ @automaton.entry_states.map { |name, state| { "name" => name, "state" => state } }
111
+ end
112
+
113
+ # @rbs () -> json_hash
114
+ def tables
115
+ table_set = Tables.build(@automaton, format: @representation) #: Tables::TableSet
116
+ {
117
+ "actions" => action_table(table_set.actions),
118
+ "gotos" => goto_table(table_set.gotos),
119
+ "default_actions" => table_set.default_actions.map { |action| Tables::CompactActions.pack(action) }
120
+ }
121
+ end
122
+
123
+ # @rbs (action_table table) -> json_hash
124
+ def action_table(table)
125
+ return compact_action_table(table) if table.is_a?(Tables::CompactActions)
126
+
127
+ {
128
+ "encoding" => "signed-sparse-rows-v1",
129
+ "rows" => table.map do |row|
130
+ row.sort.map { |token_id, action| { "token_id" => token_id, "code" => Tables::CompactActions.pack(action) } }
131
+ end
132
+ }
133
+ end
134
+
135
+ # @rbs (Tables::CompactActions table) -> json_hash
136
+ def compact_action_table(table)
137
+ {
138
+ "encoding" => "signed-row-displacement-v1",
139
+ "row_count" => table.row_count,
140
+ "column_count" => table.column_count,
141
+ "offsets" => table.offsets,
142
+ "codes" => table.codes,
143
+ "checks" => table.checks
144
+ }
145
+ end
146
+
147
+ # @rbs (goto_table table) -> json_hash
148
+ def goto_table(table)
149
+ return compact_goto_table(table) if table.is_a?(Tables::Compact)
150
+
151
+ {
152
+ "encoding" => "sparse-rows-v1",
153
+ "rows" => table.map do |row|
154
+ row.sort.map { |symbol_id, state| { "symbol_id" => symbol_id, "state" => state } }
155
+ end
156
+ }
157
+ end
158
+
159
+ # @rbs (Tables::Compact table) -> json_hash
160
+ def compact_goto_table(table)
161
+ {
162
+ "encoding" => "row-displacement-v1",
163
+ "row_count" => table.row_count,
164
+ "dense_width" => table.dense_width,
165
+ "offsets" => table.offsets,
166
+ "values" => table.values,
167
+ "checks" => table.checks
168
+ }
169
+ end
170
+
171
+ # @rbs () -> Array[json_hash]
172
+ def productions
173
+ @grammar.productions.sort_by(&:id).map do |production|
174
+ {
175
+ "id" => production.id,
176
+ "lhs" => production.lhs,
177
+ "rhs" => production.rhs,
178
+ "rhs_length" => production.rhs.length,
179
+ "action_slot" => action_slot?(production) ? production.id : nil
180
+ }
181
+ end
182
+ end
183
+
184
+ # @rbs (IR::Production production) -> bool
185
+ def action_slot?(production)
186
+ !!(production.node || production.action || !@omit_action_call)
187
+ end
188
+
189
+ # @rbs () -> json_hash
190
+ def semantic_actions
191
+ {
192
+ "binding" => "opaque-wrapper-production-id-v1",
193
+ "verified" => false,
194
+ "slots" => @grammar.productions.select { |production| action_slot?(production) }.map(&:id)
195
+ }
196
+ end
197
+
198
+ # @rbs () -> json_hash
199
+ def recovery
200
+ sync_tokens = @grammar.recovery.fetch(:sync_tokens) #: Array[String]
201
+ on_error_reduce = @grammar.recovery.fetch(:on_error_reduce) #: Array[Array[String]]
202
+ {
203
+ "sync_token_ids" => sync_tokens.map { |name| @grammar.symbol(name).id },
204
+ "on_error_reduce_symbol_ids" => on_error_reduce.map do |group|
205
+ group.map { |name| @grammar.symbol(name).id }
206
+ end
207
+ }
208
+ end
209
+
210
+ # @rbs (json_hash payload) -> json_hash
211
+ def build_cost(payload)
212
+ tables = payload.fetch("tables") #: json_hash
213
+ action_table = tables.fetch("actions") #: json_hash
214
+ goto_table = tables.fetch("gotos") #: json_hash
215
+ {
216
+ "canonical_payload_bytes" => Serializer.compact(payload).bytesize,
217
+ "action_cells" => occupied_cells(action_table, "codes"),
218
+ "goto_cells" => occupied_cells(goto_table, "values"),
219
+ "lookup_cost" => lookup_cost,
220
+ "recognition_cost" => recognition_cost,
221
+ "measurement" => "not-measured",
222
+ "bounded_by_max_steps" => true
223
+ }
224
+ end
225
+
226
+ # @rbs (json_hash table, String values_key) -> Integer
227
+ def occupied_cells(table, values_key)
228
+ if table.key?("rows")
229
+ rows = table.fetch("rows") #: Array[json_value]
230
+ return rows.sum { |row| row.is_a?(Array) ? row.length : 0 }
231
+ end
232
+
233
+ values = table.fetch(values_key) #: Array[json_value]
234
+ values.compact.length
235
+ end
236
+
237
+ # @rbs () -> String
238
+ def lookup_cost
239
+ return "O(1) row-displacement probe" if @representation == :compact
240
+
241
+ "O(log row width) binary search"
242
+ end
243
+
244
+ # @rbs () -> String
245
+ def recognition_cost
246
+ prefix = if @representation == :compact
247
+ "O(input tokens + reductions)"
248
+ else
249
+ "O((input tokens + reductions) * log maximum row width)"
250
+ end
251
+ "#{prefix}; lexer, recovery search, and semantic actions excluded"
252
+ end
253
+
254
+ # @rbs (Symbol | String? requested) -> Symbol
255
+ def effective_cst_trivia(requested)
256
+ contract_entry = @grammar.parser_contract&.cst_trivia
257
+ persisted = contract_entry.value if contract_entry&.explicit
258
+ if requested && persisted && requested.to_sym != persisted
259
+ raise ArgumentError, "cst_trivia conflicts with the persisted parser contract"
260
+ end
261
+
262
+ (requested || persisted || :leading).to_sym
263
+ end
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ # Converts code-generation CST metadata into closed JSON records.
6
+ module CSTProjection
7
+ # @rbs!
8
+ # private def cst: () -> Hash[String, Builder::json_value]?
9
+ # private def normalize_cst: (Codegen::CSTMetadata::metadata metadata) -> Hash[String, Builder::json_value]
10
+ # private def normalize_cst_kinds: (Codegen::CSTMetadata::kinds kinds) -> Hash[String, Builder::json_value]
11
+ # private def named_kinds: (Hash[String, Integer] values) -> Array[Hash[String, String | Integer]]
12
+ # private def normalize_cst_slot: (Integer production_id,
13
+ # Codegen::CSTMetadata::slot slot) -> Hash[String, Builder::json_value]
14
+
15
+ private
16
+
17
+ # @rbs skip
18
+ def cst
19
+ return unless @grammar.options[:cst]
20
+
21
+ normalize_cst(Codegen::CSTMetadata.new(@grammar, trivia_policy: @cst_trivia).build)
22
+ end
23
+
24
+ # @rbs skip
25
+ def normalize_cst(metadata)
26
+ kinds = metadata.fetch(:kinds)
27
+ {
28
+ "version" => metadata.fetch(:version),
29
+ "trivia_policy" => metadata.fetch(:trivia_policy).to_s,
30
+ "kinds" => normalize_cst_kinds(kinds),
31
+ "slots" => metadata.fetch(:slots).sort.map { |production_id, slot| normalize_cst_slot(production_id, slot) }
32
+ }
33
+ end
34
+
35
+ # @rbs skip
36
+ def normalize_cst_kinds(kinds)
37
+ {
38
+ "names" => kinds.fetch(:names),
39
+ "terminal_range" => kinds.fetch(:terminal_range),
40
+ "nonterminal_range" => kinds.fetch(:nonterminal_range),
41
+ "named" => named_kinds(kinds.fetch(:named)),
42
+ "named_nonterminals" => kinds.fetch(:named_nonterminals).sort.map do |kind_id, symbol_id|
43
+ { "kind_id" => kind_id, "symbol_id" => symbol_id }
44
+ end,
45
+ "trivia" => named_kinds(kinds.fetch(:trivia)),
46
+ "synthetic" => named_kinds(kinds.fetch(:synthetic))
47
+ }
48
+ end
49
+
50
+ # @rbs skip
51
+ def named_kinds(values)
52
+ values.sort_by { |_name, id| id }.map { |name, id| { "name" => name, "id" => id } }
53
+ end
54
+
55
+ # @rbs skip
56
+ def normalize_cst_slot(production_id, slot)
57
+ fields = slot.fetch(:fields).map do |name, value|
58
+ value = { index: value, extraction: nil } if value.is_a?(Integer)
59
+ { "name" => name, "index" => value.fetch(:index), "extraction" => value[:extraction]&.to_s }
60
+ end
61
+ {
62
+ "production_id" => production_id,
63
+ "node_kind_id" => slot.fetch(:node_kind),
64
+ "node_name" => slot.fetch(:node_name),
65
+ "fields" => fields
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ # An immutable, validated table artifact document.
6
+ class Document
7
+ # @rbs!
8
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
9
+
10
+ attr_reader :data #: Hash[String, json_value]
11
+
12
+ # @rbs (Hash[String, json_value] data) -> void
13
+ def initialize(data)
14
+ Validator.validate!(data)
15
+ @data = Serializer.deep_freeze(data)
16
+ end
17
+
18
+ # @rbs () -> String
19
+ def dump
20
+ Serializer.dump(@data)
21
+ end
22
+
23
+ # @rbs () -> Hash[String, json_value]
24
+ def payload
25
+ @data.fetch("payload") #: Hash[String, json_value]
26
+ end
27
+
28
+ # @rbs () -> Hash[String, String]
29
+ def identity
30
+ @data.fetch("identity") #: Hash[String, String]
31
+ end
32
+
33
+ # @rbs () -> Hash[String, json_value]
34
+ def cost
35
+ @data.fetch("cost") #: Hash[String, json_value]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ # Recognition-only driver over internal token ids; it never loads wrapper code.
6
+ class Executor
7
+ class Result
8
+ attr_reader :status #: Symbol
9
+ attr_reader :steps #: Integer
10
+ attr_reader :consumed_tokens #: Integer
11
+ attr_reader :reason #: String?
12
+
13
+ # @rbs (status: Symbol, steps: Integer, consumed_tokens: Integer, reason: String?) -> void
14
+ def initialize(status:, steps:, consumed_tokens:, reason:)
15
+ @status = status
16
+ @steps = steps
17
+ @consumed_tokens = consumed_tokens
18
+ @reason = reason
19
+ freeze
20
+ end
21
+
22
+ # @rbs () -> bool
23
+ def accepted? = status == :accepted
24
+ # @rbs () -> bool
25
+ def rejected? = status == :rejected
26
+ # @rbs () -> bool
27
+ def exhausted? = status == :exhausted
28
+ end
29
+
30
+ # @rbs (Document | Hash[String, ValidationSupport::json_value] document) -> void
31
+ def initialize(document)
32
+ @payload = document.is_a?(Document) ? document.payload : Document.new(document).payload
33
+ @tables = @payload.fetch("tables") #: Hash[String, ValidationSupport::json_value]
34
+ @productions = @payload.fetch("productions") #: Array[Hash[String, ValidationSupport::json_value]]
35
+ tokens = @payload.fetch("tokens") #: Array[Hash[String, ValidationSupport::json_value]]
36
+ @terminal_ids = tokens.map do |token|
37
+ token.fetch("id") #: Integer
38
+ end
39
+ end
40
+
41
+ # @rbs (Array[Integer] token_ids, ?entry: String | Symbol?, ?max_steps: Integer) -> Result
42
+ def recognize(token_ids, entry: nil, max_steps: 1_000_000)
43
+ raise ArgumentError, "max_steps must be positive" unless max_steps.is_a?(Integer) && max_steps.positive?
44
+
45
+ input = validate_input(token_ids)
46
+ input << 0 unless input.last&.zero?
47
+ stack = [entry_state(entry)]
48
+ execute(input, stack, max_steps)
49
+ end
50
+
51
+ private
52
+
53
+ # @rbs (Array[Integer] input, Array[Integer] stack, Integer max_steps) -> Result
54
+ def execute(input, stack, max_steps)
55
+ cursor = 0
56
+ max_steps.times do |step|
57
+ return result(:rejected, step + 1, cursor, "parser shifted past end of input") if cursor >= input.length
58
+
59
+ code = action(stack.last, input.fetch(cursor))
60
+ immediate = immediate_result(code, input.fetch(cursor), step, cursor)
61
+ return immediate if immediate
62
+
63
+ code ||= -1
64
+ if code.positive?
65
+ stack << (code - 1)
66
+ cursor += 1
67
+ next
68
+ end
69
+
70
+ reason = reduce_stack(stack, code)
71
+ return result(:rejected, step + 1, cursor, reason) if reason
72
+ end
73
+ result(:exhausted, max_steps, cursor, "max_steps exceeded")
74
+ end
75
+
76
+ # @rbs (Integer? code, Integer token_id, Integer step, Integer cursor) -> Result?
77
+ def immediate_result(code, token_id, step, cursor)
78
+ return result(:rejected, step + 1, cursor, "no parser action") if code.nil? || code == -1
79
+ return unless code.zero?
80
+ return result(:rejected, step + 1, cursor, "accept action before end of input") unless token_id.zero?
81
+
82
+ result(:accepted, step + 1, cursor, nil)
83
+ end
84
+
85
+ # @rbs (Array[Integer] token_ids) -> Array[Integer]
86
+ def validate_input(token_ids)
87
+ raise ArgumentError, "token_ids must be an array" unless token_ids.is_a?(Array)
88
+
89
+ token_ids.map.with_index do |token_id, index|
90
+ raise ArgumentError, "token_ids[#{index}] must be an internal terminal id" unless
91
+ token_id.is_a?(Integer) && @terminal_ids.include?(token_id)
92
+ raise ArgumentError, "$eof may only appear at the end" if token_id.zero? && index != token_ids.length - 1
93
+
94
+ token_id
95
+ end
96
+ end
97
+
98
+ # @rbs (String | Symbol? requested) -> Integer
99
+ def entry_state(requested)
100
+ entries = @payload.fetch("entry_states") #: Array[Hash[String, ValidationSupport::json_value]]
101
+ default_state = entries.first.fetch("state") #: Integer
102
+ return default_state unless requested
103
+
104
+ entry = entries.find { |candidate| candidate.fetch("name") == requested.to_s }
105
+ raise ArgumentError, "unknown parser entry #{requested.inspect}" unless entry
106
+
107
+ entry.fetch("state") #: Integer
108
+ end
109
+
110
+ # @rbs (Integer row, Integer column) -> Integer?
111
+ def action(row, column)
112
+ table = @tables.fetch("actions") #: Hash[String, ValidationSupport::json_value]
113
+ code = if table.fetch("encoding") == "signed-sparse-rows-v1"
114
+ rows = table.fetch("rows") #: Array[Array[Hash[String, ValidationSupport::json_value]]]
115
+ cells = rows.fetch(row)
116
+ cell = cells.bsearch do |candidate|
117
+ token_id = candidate.fetch("token_id") #: Integer
118
+ token_id >= column
119
+ end
120
+ if cell
121
+ token_id = cell.fetch("token_id") #: Integer
122
+ if token_id == column
123
+ cell.fetch("code") #: Integer
124
+ end
125
+ end
126
+ else
127
+ displacement_lookup(table, row, column, "codes")
128
+ end
129
+ defaults = @tables.fetch("default_actions") #: Array[Integer?]
130
+ code.nil? ? defaults.fetch(row) : code
131
+ end
132
+
133
+ # @rbs (Integer row, Integer column) -> Integer?
134
+ def goto_state(row, column)
135
+ table = @tables.fetch("gotos") #: Hash[String, ValidationSupport::json_value]
136
+ if table.fetch("encoding") == "sparse-rows-v1"
137
+ rows = table.fetch("rows") #: Array[Array[Hash[String, ValidationSupport::json_value]]]
138
+ cells = rows.fetch(row)
139
+ cell = cells.bsearch do |candidate|
140
+ symbol_id = candidate.fetch("symbol_id") #: Integer
141
+ symbol_id >= column
142
+ end
143
+ if cell
144
+ symbol_id = cell.fetch("symbol_id") #: Integer
145
+ if symbol_id == column
146
+ cell.fetch("state") #: Integer
147
+ end
148
+ end
149
+ else
150
+ displacement_lookup(table, row, column, "values")
151
+ end
152
+ end
153
+
154
+ # @rbs (Hash[String, ValidationSupport::json_value] table, Integer row, Integer column,
155
+ # String value_key) -> Integer?
156
+ def displacement_lookup(table, row, column, value_key)
157
+ offsets = table.fetch("offsets") #: Array[Integer]
158
+ index = offsets.fetch(row) + column
159
+ checks = table.fetch("checks") #: Array[Integer?]
160
+ return if index.negative? || index >= checks.length || checks[index] != row
161
+
162
+ values = table.fetch(value_key) #: Array[Integer?]
163
+ values.fetch(index)
164
+ end
165
+
166
+ # @rbs (Array[Integer] stack, Integer code) -> String?
167
+ def reduce_stack(stack, code)
168
+ production = @productions.fetch(-2 - code)
169
+ length = production.fetch("rhs_length") #: Integer
170
+ return "reduction underflow" if length >= stack.length
171
+
172
+ stack.pop(length)
173
+ lhs = production.fetch("lhs") #: Integer
174
+ state = goto_state(stack.last, lhs)
175
+ return "missing goto after reduction" unless state
176
+
177
+ stack << state
178
+ nil
179
+ end
180
+
181
+ # @rbs (Symbol status, Integer steps, Integer consumed, String? reason) -> Result
182
+ def result(status, steps, consumed, reason)
183
+ Result.new(status: status, steps: steps, consumed_tokens: consumed, reason: reason)
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module TableArtifact
5
+ # Canonical JSON ordering and digest primitives shared by writer and loader.
6
+ module Serializer
7
+ # @rbs!
8
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[json_value, json_value]
9
+
10
+ class << self
11
+ # @rbs (json_value value) -> json_value
12
+ def canonical(value)
13
+ case value
14
+ when Hash
15
+ value.keys.sort.to_h { |key| [key, canonical(value.fetch(key))] }
16
+ when Array
17
+ value.map { |item| canonical(item) }
18
+ when String
19
+ normalized = value.dup.force_encoding(Encoding::UTF_8)
20
+ normalized.valid_encoding? ? normalized : normalized.scrub
21
+ else
22
+ value
23
+ end
24
+ end
25
+
26
+ # @rbs (json_value value) -> String
27
+ def compact(value)
28
+ JSON.generate(canonical(value))
29
+ end
30
+
31
+ # @rbs (json_value value) -> String
32
+ def dump(value)
33
+ "#{JSON.pretty_generate(canonical(value))}\n"
34
+ end
35
+
36
+ # @rbs (json_value value) -> String
37
+ def digest(value)
38
+ "sha256:#{Digest::SHA256.hexdigest(compact(value))}"
39
+ end
40
+
41
+ # @rbs (json_value value) -> json_value
42
+ def deep_freeze(value)
43
+ case value
44
+ when Hash
45
+ value.each do |key, item|
46
+ deep_freeze(key)
47
+ deep_freeze(item)
48
+ end
49
+ when Array
50
+ value.each { |item| deep_freeze(item) }
51
+ end
52
+ value.freeze
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end