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,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "validator/support"
4
+ require_relative "validator/tables"
5
+ require_relative "validator/metadata"
6
+
7
+ module Ibex
8
+ module TableArtifact
9
+ # Closed structural and referential validation for untrusted sidecars.
10
+ class Validator
11
+ include ValidationSupport
12
+
13
+ ROOT_KEYS = %w[artifact_type schema_version identity payload cost].freeze #: Array[String]
14
+ PAYLOAD_KEYS = %w[
15
+ table_format source state_count symbols tokens entry_states tables productions cst recovery semantic_actions
16
+ ].freeze #: Array[String]
17
+
18
+ class << self
19
+ # @rbs (Hash[String, ValidationSupport::json_value] data) -> true
20
+ def validate!(data)
21
+ new(data).validate!
22
+ end
23
+ end
24
+
25
+ # @rbs (Hash[String, ValidationSupport::json_value] data) -> void
26
+ def initialize(data)
27
+ @data = data
28
+ end
29
+
30
+ # rubocop:disable Naming/PredicateMethod -- bang denotes fail-fast validation.
31
+ # @rbs () -> true
32
+ def validate!
33
+ record(@data, "$", ROOT_KEYS)
34
+ payload = record(@data.fetch("payload"), "$.payload", PAYLOAD_KEYS)
35
+ validate_root_identity
36
+ representation = validate_table_format(payload.fetch("table_format"))
37
+ validate_source(payload.fetch("source"))
38
+ state_count = integer(payload.fetch("state_count"), "$.payload.state_count", minimum: 1)
39
+ symbols = validate_symbols(payload.fetch("symbols"))
40
+ validate_tokens(payload.fetch("tokens"), symbols)
41
+ validate_entries(payload.fetch("entry_states"), symbols, state_count)
42
+ productions = validate_productions(payload.fetch("productions"), symbols)
43
+ tables = payload.fetch("tables") #: Hash[String, ValidationSupport::json_value]
44
+ TableValidator.new(
45
+ tables,
46
+ state_count: state_count,
47
+ production_count: productions.length,
48
+ terminal_ids: symbol_ids(symbols, "terminal"),
49
+ nonterminal_ids: symbol_ids(symbols, "nonterminal"),
50
+ representation: representation
51
+ ).validate!
52
+ MetadataValidator.new(payload, symbols: symbols, productions: productions).validate!
53
+ validate_cost(@data.fetch("cost"), payload)
54
+ true
55
+ end
56
+ # rubocop:enable Naming/PredicateMethod
57
+
58
+ private
59
+
60
+ # @rbs () -> void
61
+ def validate_root_identity
62
+ invalid("$.artifact_type", "is unsupported") unless @data.fetch("artifact_type") == ARTIFACT_TYPE
63
+ invalid("$.schema_version", "is unsupported") unless @data.fetch("schema_version") == SCHEMA_VERSION
64
+ identity_keys = %w[
65
+ grammar_digest automaton_digest payload_digest cst_metadata_digest recovery_metadata_digest
66
+ ]
67
+ identity = record(@data.fetch("identity"), "$.identity", identity_keys)
68
+ identity.each { |key, value| digest(value, "$.identity.#{key}") }
69
+ expected = Serializer.digest(@data.fetch("payload"))
70
+ invalid("$.identity.payload_digest", "does not match the canonical payload") unless
71
+ identity.fetch("payload_digest") == expected
72
+ payload = @data.fetch("payload")
73
+ validate_named_digest(identity, payload, "cst_metadata", "cst")
74
+ validate_named_digest(identity, payload, "recovery_metadata", "recovery")
75
+ end
76
+
77
+ # @rbs (ValidationSupport::json_value data) -> String
78
+ def validate_table_format(data)
79
+ path = "$.payload.table_format"
80
+ keys = %w[family version representation action_encoding goto_encoding]
81
+ data = record(data, path, keys)
82
+ enum(data.fetch("family"), "#{path}.family", ["ibex-runtime-parser-table"])
83
+ invalid("#{path}.version", "must be 6") unless integer(data.fetch("version"), "#{path}.version") == 6
84
+ representation = enum(data.fetch("representation"), "#{path}.representation", %w[plain compact]) #: String
85
+ expected = if representation == "compact"
86
+ %w[signed-row-displacement-v1 row-displacement-v1]
87
+ else
88
+ %w[signed-sparse-rows-v1 sparse-rows-v1]
89
+ end
90
+ actual = [data.fetch("action_encoding"), data.fetch("goto_encoding")]
91
+ invalid(path, "encodings do not match representation") unless actual == expected
92
+ representation
93
+ end
94
+
95
+ # @rbs (ValidationSupport::json_value data) -> void
96
+ def validate_source(data)
97
+ source = record(data, "$.payload.source", %w[grammar_digest automaton_digest])
98
+ source.each { |key, value| digest(value, "$.payload.source.#{key}") }
99
+ identity = @data.fetch("identity")
100
+ invalid("$.payload.source", "must match root identity") unless
101
+ source.fetch("grammar_digest") == identity.fetch("grammar_digest") &&
102
+ source.fetch("automaton_digest") == identity.fetch("automaton_digest")
103
+ end
104
+
105
+ # @rbs (ValidationSupport::json_value data) -> Array[Hash[String, ValidationSupport::json_value]]
106
+ def validate_symbols(data)
107
+ symbols = array(data, "$.payload.symbols") #: Array[Hash[String, ValidationSupport::json_value]]
108
+ invalid("$.payload.symbols", "must not be empty") if symbols.empty?
109
+ names = [] #: Array[String]
110
+ symbols.each_with_index do |symbol, index|
111
+ path = "$.payload.symbols[#{index}]"
112
+ record(symbol, path, %w[id name kind display_name])
113
+ invalid("#{path}.id", "must be contiguous and ordered") unless symbol.fetch("id") == index
114
+ names << string(symbol.fetch("name"), "#{path}.name")
115
+ enum(symbol.fetch("kind"), "#{path}.kind", %w[terminal nonterminal])
116
+ nullable_string(symbol.fetch("display_name"), "#{path}.display_name")
117
+ end
118
+ invalid("$.payload.symbols", "names must be unique") unless names.uniq.length == names.length
119
+ symbols
120
+ end
121
+
122
+ # @rbs (ValidationSupport::json_value data, Array[Hash[String, ValidationSupport::json_value]] symbols) -> void
123
+ def validate_tokens(data, symbols)
124
+ tokens = array(data, "$.payload.tokens") #: Array[Hash[String, ValidationSupport::json_value]]
125
+ expected = symbols.select { |symbol| symbol.fetch("kind") == "terminal" }.map do |symbol|
126
+ symbol.slice("id", "name", "display_name")
127
+ end
128
+ tokens.each_with_index do |token, index|
129
+ record(token, "$.payload.tokens[#{index}]", %w[id name display_name])
130
+ end
131
+ invalid("$.payload.tokens", "must exactly index terminal symbols") unless tokens == expected
132
+ token_id = tokens.first&.fetch("id")
133
+ invalid("$.payload.tokens", "token id 0 must be $eof") unless token_id.is_a?(Integer) && token_id.zero? &&
134
+ tokens.first.fetch("name") == "$eof"
135
+ end
136
+
137
+ # @rbs (ValidationSupport::json_value data,
138
+ # Array[Hash[String, ValidationSupport::json_value]] symbols, Integer state_count) -> void
139
+ def validate_entries(data, symbols, state_count)
140
+ entries = array(data, "$.payload.entry_states") #: Array[Hash[String, ValidationSupport::json_value]]
141
+ invalid("$.payload.entry_states", "must not be empty") if entries.empty?
142
+ names = entries.map.with_index do |entry, index|
143
+ path = "$.payload.entry_states[#{index}]"
144
+ record(entry, path, %w[name state])
145
+ name = string(entry.fetch("name"), "#{path}.name")
146
+ symbol = symbols.find { |candidate| candidate.fetch("name") == name }
147
+ invalid("#{path}.name", "must reference a nonterminal") unless symbol&.fetch("kind") == "nonterminal"
148
+ state = integer(entry.fetch("state"), "#{path}.state", minimum: 0)
149
+ invalid("#{path}.state", "references a missing state") unless state < state_count
150
+ name
151
+ end
152
+ invalid("$.payload.entry_states", "names must be unique") unless names.uniq.length == names.length
153
+ end
154
+
155
+ # @rbs (ValidationSupport::json_value data,
156
+ # Array[Hash[String, ValidationSupport::json_value]] symbols) -> Array[Hash[String, ValidationSupport::json_value]]
157
+ def validate_productions(data, symbols)
158
+ productions = array(data, "$.payload.productions") #: Array[Hash[String, ValidationSupport::json_value]]
159
+ nonterminals = symbol_ids(symbols, "nonterminal")
160
+ productions.each_with_index do |production, index|
161
+ path = "$.payload.productions[#{index}]"
162
+ record(production, path, %w[id lhs rhs rhs_length action_slot])
163
+ invalid("#{path}.id", "must be contiguous and ordered") unless production.fetch("id") == index
164
+ lhs = integer(production.fetch("lhs"), "#{path}.lhs", minimum: 0)
165
+ invalid("#{path}.lhs", "must reference a nonterminal") unless nonterminals.include?(lhs)
166
+ rhs = array(production.fetch("rhs"), "#{path}.rhs")
167
+ rhs.each_with_index do |symbol_id, rhs_index|
168
+ symbol_id = integer(symbol_id, "#{path}.rhs[#{rhs_index}]", minimum: 0)
169
+ invalid("#{path}.rhs[#{rhs_index}]", "references a missing symbol") unless symbol_id < symbols.length
170
+ end
171
+ invalid("#{path}.rhs_length", "must match rhs") unless production.fetch("rhs_length") == rhs.length
172
+ slot = nullable_integer(production.fetch("action_slot"), "#{path}.action_slot", minimum: 0)
173
+ invalid("#{path}.action_slot", "must equal production id") if slot && slot != index
174
+ end
175
+ productions
176
+ end
177
+
178
+ # @rbs (ValidationSupport::json_value data, Hash[String, ValidationSupport::json_value] payload) -> void
179
+ def validate_cost(data, payload)
180
+ path = "$.cost"
181
+ keys = %w[
182
+ canonical_payload_bytes action_cells goto_cells lookup_cost recognition_cost measurement bounded_by_max_steps
183
+ ]
184
+ data = record(data, path, keys)
185
+ bytes = integer(data.fetch("canonical_payload_bytes"), "#{path}.canonical_payload_bytes", minimum: 1)
186
+ unless bytes == Serializer.compact(payload).bytesize
187
+ invalid("#{path}.canonical_payload_bytes",
188
+ "does not match payload")
189
+ end
190
+ action_cells = integer(data.fetch("action_cells"), "#{path}.action_cells", minimum: 0)
191
+ goto_cells = integer(data.fetch("goto_cells"), "#{path}.goto_cells", minimum: 0)
192
+ tables = payload.fetch("tables") #: Hash[String, ValidationSupport::json_value]
193
+ actions = tables.fetch("actions") #: Hash[String, ValidationSupport::json_value]
194
+ gotos = tables.fetch("gotos") #: Hash[String, ValidationSupport::json_value]
195
+ invalid("#{path}.action_cells", "does not match actions") unless action_cells == occupied(actions, "codes")
196
+ invalid("#{path}.goto_cells", "does not match gotos") unless goto_cells == occupied(gotos, "values")
197
+ expected_lookup, expected_recognition = expected_costs(payload)
198
+ invalid("#{path}.lookup_cost", "does not match table representation") unless
199
+ data.fetch("lookup_cost") == expected_lookup
200
+ invalid("#{path}.recognition_cost", "does not match table representation") unless
201
+ data.fetch("recognition_cost") == expected_recognition
202
+ enum(data.fetch("measurement"), "#{path}.measurement", ["not-measured"])
203
+ invalid("#{path}.bounded_by_max_steps", "must be true") unless boolean(data.fetch("bounded_by_max_steps"),
204
+ "#{path}.bounded_by_max_steps")
205
+ end
206
+
207
+ # @rbs (Hash[String, ValidationSupport::json_value] payload) -> [String, String]
208
+ def expected_costs(payload)
209
+ table_format = payload.fetch("table_format") #: Hash[String, ValidationSupport::json_value]
210
+ compact = table_format.fetch("representation") == "compact"
211
+ lookup = compact ? "O(1) row-displacement probe" : "O(log row width) binary search"
212
+ recognition = if compact
213
+ "O(input tokens + reductions)"
214
+ else
215
+ "O((input tokens + reductions) * log maximum row width)"
216
+ end
217
+ [lookup, "#{recognition}; lexer, recovery search, and semantic actions excluded"]
218
+ end
219
+
220
+ # @rbs (Hash[String, ValidationSupport::json_value] table, String values_key) -> Integer
221
+ def occupied(table, values_key)
222
+ if table.key?("rows")
223
+ rows = table.fetch("rows") #: Array[Array[ValidationSupport::json_value]]
224
+ return rows.sum(&:length)
225
+ end
226
+
227
+ values = table.fetch(values_key) #: Array[ValidationSupport::json_value]
228
+ values.compact.length
229
+ end
230
+
231
+ # @rbs (Array[Hash[String, ValidationSupport::json_value]] symbols, String kind) -> Set[Integer]
232
+ def symbol_ids(symbols, kind)
233
+ symbols.filter_map do |symbol|
234
+ next unless symbol.fetch("kind") == kind
235
+
236
+ symbol.fetch("id") #: Integer
237
+ end.to_set
238
+ end
239
+
240
+ # @rbs (Hash[String, ValidationSupport::json_value] identity,
241
+ # Hash[String, ValidationSupport::json_value] payload, String identity_name,
242
+ # String payload_name) -> void
243
+ def validate_named_digest(identity, payload, identity_name, payload_name)
244
+ expected = Serializer.digest(payload.fetch(payload_name))
245
+ path = "$.identity.#{identity_name}_digest"
246
+ return if identity.fetch("#{identity_name}_digest") == expected
247
+
248
+ invalid(path,
249
+ "does not match payload #{payload_name}")
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "json"
5
+ require "set"
6
+ require_relative "error"
7
+ require_relative "ir"
8
+ require_relative "tables"
9
+ require_relative "runtime/table_format"
10
+ require_relative "codegen/cst_metadata"
11
+ require_relative "table_artifact/serializer"
12
+ require_relative "table_artifact/validator"
13
+ require_relative "table_artifact/document"
14
+ require_relative "table_artifact/cst_projection"
15
+ require_relative "table_artifact/builder"
16
+ require_relative "table_artifact/executor"
17
+
18
+ module Ibex
19
+ # Experimental data-only authority for executable parser tables.
20
+ module TableArtifact
21
+ # @rbs!
22
+ # interface _Reader
23
+ # def read: (Integer length) -> String?
24
+ # end
25
+
26
+ ARTIFACT_TYPE = "ibex.parser-table" #: String
27
+ SCHEMA_VERSION = 1 #: Integer
28
+ DEFAULT_MAX_BYTES = 16 * 1024 * 1024 #: Integer
29
+
30
+ class ValidationError < Ibex::Error; end
31
+
32
+ class << self
33
+ # @rbs (IR::Automaton automaton, ?representation: Symbol | String, ?cst_trivia: Symbol | String?,
34
+ # ?omit_action_call: bool?) -> Document
35
+ def build(automaton, representation: :compact, cst_trivia: nil, omit_action_call: nil)
36
+ Builder.new(
37
+ automaton,
38
+ representation: representation,
39
+ cst_trivia: cst_trivia,
40
+ omit_action_call: omit_action_call
41
+ ).build
42
+ end
43
+
44
+ # @rbs (String | _Reader source, ?max_bytes: Integer) -> Document
45
+ def load(source, max_bytes: DEFAULT_MAX_BYTES)
46
+ bytes = read_bounded(source, max_bytes)
47
+ unless bytes.dup.force_encoding(Encoding::UTF_8).valid_encoding?
48
+ raise ValidationError, "table artifact must be UTF-8 JSON"
49
+ end
50
+
51
+ Document.new(JSON.parse(bytes))
52
+ rescue JSON::ParserError => e
53
+ raise ValidationError, "invalid table artifact JSON: #{e.message}"
54
+ end
55
+
56
+ private
57
+
58
+ # @rbs (untyped source, Integer max_bytes) -> String
59
+ def read_bounded(source, max_bytes)
60
+ raise ArgumentError, "max_bytes must be positive" unless max_bytes.is_a?(Integer) && max_bytes.positive?
61
+
62
+ bytes = if source.respond_to?(:read)
63
+ read_from(source, max_bytes)
64
+ else
65
+ String(source)
66
+ end
67
+ raise ValidationError, "table artifact exceeds #{max_bytes} bytes" if bytes.bytesize > max_bytes
68
+
69
+ bytes
70
+ end
71
+
72
+ # @rbs (_Reader source, Integer max_bytes) -> String
73
+ def read_from(source, max_bytes)
74
+ bytes = +"".b
75
+ loop do
76
+ chunk = source.read((max_bytes + 1) - bytes.bytesize)
77
+ break if chunk.nil?
78
+ raise ValidationError, "table artifact reader must return strings" unless chunk.is_a?(String)
79
+ break if chunk.empty?
80
+
81
+ bytes << chunk
82
+ break if bytes.bytesize > max_bytes
83
+ end
84
+ bytes
85
+ end
86
+ end
87
+ end
88
+ end
@@ -7,6 +7,10 @@ module Ibex
7
7
  module TableSimulation
8
8
  # Final immutable simulation document.
9
9
  class Result
10
+ # @rbs!
11
+ # type step_document = Hash[String, Step::document_value]
12
+ # type document_value = String | Integer | Array[String] | Array[step_document]
13
+
10
14
  IDENTIFIER = "table-simulation" #: String
11
15
  SCHEMA_VERSION = 1 #: Integer
12
16
 
@@ -29,7 +33,7 @@ module Ibex
29
33
  freeze
30
34
  end
31
35
 
32
- # @rbs () -> Hash[String, untyped]
36
+ # @rbs () -> Hash[String, document_value]
33
37
  def to_h
34
38
  {
35
39
  "ibex_table_simulation" => IDENTIFIER,
@@ -42,7 +46,7 @@ module Ibex
42
46
  }
43
47
  end
44
48
 
45
- # @rbs (*untyped) -> String
49
+ # @rbs (*Object?) -> String
46
50
  def to_json(*)
47
51
  "#{JSON.pretty_generate(to_h)}\n"
48
52
  end
@@ -5,6 +5,8 @@ module Ibex
5
5
  module TableSimulation
6
6
  # One immutable parser-table action.
7
7
  class Step
8
+ # @rbs! type document_value = String | Integer | nil
9
+
8
10
  attr_reader :sequence #: Integer
9
11
  attr_reader :state #: Integer
10
12
  attr_reader :token_id #: Integer
@@ -38,7 +40,7 @@ module Ibex
38
40
  freeze
39
41
  end
40
42
 
41
- # @rbs () -> Hash[String, untyped]
43
+ # @rbs () -> Hash[String, document_value]
42
44
  def to_h
43
45
  {
44
46
  "sequence" => @sequence,
data/lib/ibex/tables.rb CHANGED
@@ -8,11 +8,25 @@ module Ibex
8
8
  # Parser table construction and row-displacement compression.
9
9
  module Tables
10
10
  # @rbs!
11
+ # type action_table = Array[Hash[Integer, IR::runtime_action]] | CompactActions
12
+ # type goto_table = Array[Hash[Integer, Integer]] | Compact
11
13
  # private def runtime_action: (IR::parser_action action) -> IR::runtime_action
12
14
  # private def self.runtime_action: (IR::parser_action action) -> IR::runtime_action
13
15
 
16
+ # @rbs skip
14
17
  TableSet = Struct.new(:actions, :gotos, :default_actions, keyword_init: true)
15
18
 
19
+ # @rbs!
20
+ # class TableSet < Struct[Object?]
21
+ # attr_accessor actions: action_table
22
+ # attr_accessor gotos: goto_table
23
+ # attr_accessor default_actions: Array[IR::runtime_action?]
24
+ # def self.new: (?actions: action_table, ?gotos: goto_table,
25
+ # ?default_actions: Array[IR::runtime_action?]) -> instance
26
+ # | ({ ?actions: action_table, ?gotos: goto_table,
27
+ # ?default_actions: Array[IR::runtime_action?] }) -> instance
28
+ # end
29
+
16
30
  # @rbs (IR::Automaton automaton, ?format: Symbol | String) -> TableSet
17
31
  def build(automaton, format: :compact)
18
32
  action_rows = automaton.states.map do |state|
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "artifact_set"
5
+ require_relative "generation_manifest"
6
+ require_relative "generation_transaction"
7
+ require_relative "table_artifact"
8
+ require_relative "verification_report"
9
+
10
+ module Ibex
11
+ # Explicitly renders and publishes one manifest-bound verification bundle.
12
+ class VerifiableGenerationBundle
13
+ # rubocop:disable Metrics/ParameterLists -- persisted artifact paths and verification bounds are explicit inputs.
14
+ # @rbs (IR::Automaton automaton, wrapper_path: String, wrapper_source: String,
15
+ # table_path: String, report_path: String, manifest_path: String,
16
+ # source_records: Array[GenerationInput], ?manifest_options: Hash[String, Object?],
17
+ # ?wrapper_mode: Integer?, ?representation: Symbol | String, ?cst_trivia: Symbol | String?,
18
+ # ?omit_action_call: bool?, ?strict: bool, ?max_states: Integer, ?max_items: Integer) -> void
19
+ def initialize(automaton, wrapper_path:, wrapper_source:, table_path:, report_path:, manifest_path:,
20
+ source_records:, manifest_options: {}, wrapper_mode: nil, representation: :compact,
21
+ cst_trivia: nil, omit_action_call: nil, strict: false,
22
+ max_states: VerificationReport::DEFAULT_MAX_STATES,
23
+ max_items: VerificationReport::DEFAULT_MAX_ITEMS)
24
+ @automaton = automaton
25
+ @wrapper_path = wrapper_path
26
+ @wrapper_source = wrapper_source
27
+ @table_path = table_path
28
+ VerificationReport::LogicalPath.table(table_path)
29
+ if source_records.length > VerificationReport::LogicalPath::MAX_INPUT_FILES
30
+ raise ArgumentError,
31
+ "verification reports support at most #{VerificationReport::LogicalPath::MAX_INPUT_FILES} input files"
32
+ end
33
+ @report_path = report_path
34
+ @manifest_path = manifest_path
35
+ @source_records = source_records
36
+ @manifest_options = manifest_options
37
+ @wrapper_mode = wrapper_mode
38
+ @representation = representation
39
+ @cst_trivia = cst_trivia
40
+ @omit_action_call = omit_action_call
41
+ @strict = strict
42
+ @max_states = max_states
43
+ @max_items = max_items
44
+ end
45
+ # rubocop:enable Metrics/ParameterLists
46
+
47
+ # Render table, wrapper, and report before the non-cyclic manifest marker.
48
+ # @rbs () -> ArtifactSet
49
+ def render
50
+ canonical_automaton = VerificationReport.canonical_automaton(
51
+ @automaton, source_records: @source_records
52
+ )
53
+ table = TableArtifact.build(
54
+ canonical_automaton, representation: @representation, cst_trivia: @cst_trivia,
55
+ omit_action_call: @omit_action_call
56
+ )
57
+ artifacts = ArtifactSet.new
58
+ artifacts.add(kind: :parser_table, path: @table_path, content: table.dump)
59
+ artifacts.add(kind: :parser, path: @wrapper_path, content: @wrapper_source, mode: @wrapper_mode)
60
+ report = VerificationReport.render(
61
+ canonical_automaton, table: table, source_records: @source_records, table_path: @table_path,
62
+ strict: @strict, max_states: @max_states, max_items: @max_items
63
+ )
64
+ artifacts.add(kind: :verification_report, path: @report_path, content: report)
65
+ manifest = GenerationManifest.render(
66
+ artifacts, source_records: @source_records, options: @manifest_options
67
+ )
68
+ artifacts.add(kind: :manifest, path: @manifest_path, content: manifest)
69
+ artifacts
70
+ end
71
+
72
+ # @rbs (?warning: ^(String) -> void, ?stability_check: ^() -> bool) -> ArtifactSet
73
+ def publish(warning: ->(_message) {}, stability_check: -> { @source_records.all?(&:current?) })
74
+ artifacts = render
75
+ GenerationTransaction.new(
76
+ artifacts, warning: warning, stability_check: stability_check, source_records: @source_records
77
+ ).commit
78
+ artifacts
79
+ end
80
+
81
+ # @rbs (String manifest_path) -> Hash[String, VerificationReport::Validator::json_value]
82
+ def self.validate_file(manifest_path)
83
+ VerificationReport.validate_bundle_file(manifest_path)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "digest"
5
+ require_relative "../ir"
6
+ require_relative "../verify"
7
+ require_relative "../version"
8
+
9
+ module Ibex
10
+ module VerificationReport
11
+ # Builds deterministic evidence from an Automaton IR verification run.
12
+ class Builder
13
+ # @rbs!
14
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
15
+
16
+ # @rbs (IR::Automaton automaton, table: TableArtifact::Document,
17
+ # source_records: Array[GenerationInput], table_path: String, strict: bool,
18
+ # max_states: Integer, max_items: Integer) -> void
19
+ def initialize(automaton, table:, source_records:, table_path:, strict:, max_states:, max_items:)
20
+ raise ArgumentError, "source_records must not be empty" if source_records.empty?
21
+ if source_records.length > LogicalPath::MAX_INPUT_FILES
22
+ raise ArgumentError, "verification reports support at most #{LogicalPath::MAX_INPUT_FILES} input files"
23
+ end
24
+ raise ArgumentError, "max_states must be positive" unless max_states.is_a?(Integer) && max_states.positive?
25
+ raise ArgumentError, "max_items must be positive" unless max_items.is_a?(Integer) && max_items.positive?
26
+
27
+ @automaton = CanonicalIR.new(automaton, source_records: source_records).build
28
+ @table = table
29
+ @source_records = source_records
30
+ @table_path = LogicalPath.table(table_path)
31
+ @strict = strict
32
+ @max_states = max_states
33
+ @max_items = max_items
34
+ end
35
+
36
+ # @rbs () -> String
37
+ def render
38
+ validate_table_identity!
39
+ document = {
40
+ "ibex_report" => IDENTIFIER,
41
+ "schema_version" => SCHEMA_VERSION,
42
+ "checker" => { "name" => "ibex.verify", "version" => Ibex::VERSION },
43
+ "profile" => @strict ? "strict" : "default",
44
+ "bounds" => bounds.transform_keys(&:to_s),
45
+ "input" => input_identity,
46
+ "ir" => ir_identity,
47
+ "table" => table_identity,
48
+ "outcome" => verification_outcome,
49
+ "excluded_trust" => EXCLUDED_TRUST
50
+ }
51
+ document["evidence_digest"] = TableArtifact::Serializer.digest(document)
52
+ TableArtifact::Serializer.dump(document)
53
+ end
54
+
55
+ private
56
+
57
+ # @rbs () -> Hash[Symbol, Integer]
58
+ def bounds
59
+ { max_states: @max_states, max_items: @max_items }
60
+ end
61
+
62
+ # @rbs () -> Array[String]
63
+ def requested_checks
64
+ Verify::Verifier::DEFAULT_CHECKS + (@strict ? Verify::Verifier::STRICT_CHECKS : [])
65
+ end
66
+
67
+ # @rbs () -> Hash[String, json_value]
68
+ def verification_outcome
69
+ result = Verify::Verifier.new(
70
+ @automaton, strict: @strict, max_states: @max_states, max_items: @max_items
71
+ ).verify
72
+ {
73
+ "status" => result.valid? ? "pass" : "violations",
74
+ "requested_checks" => requested_checks,
75
+ "executed_checks" => result.checks,
76
+ "violations" => result.violations.map { |violation| stringify_keys(violation.to_h) },
77
+ "exhaustion" => nil
78
+ }
79
+ rescue Verify::BudgetExceeded => e
80
+ {
81
+ "status" => "exhausted",
82
+ "requested_checks" => requested_checks,
83
+ "executed_checks" => [],
84
+ "violations" => [],
85
+ "exhaustion" => { "kind" => "reference_collection_budget", "message" => e.message }
86
+ }
87
+ end
88
+
89
+ # @rbs () -> Hash[String, json_value]
90
+ def input_identity
91
+ files = @source_records.map.with_index do |record, index|
92
+ {
93
+ "logical_path" => LogicalPath.input(record.path, index),
94
+ "sha256" => prefixed_digest(record.sha256),
95
+ "bytesize" => record.bytesize
96
+ }
97
+ end
98
+ { "digest" => TableArtifact::Serializer.digest(files), "files" => files }
99
+ end
100
+
101
+ # @rbs () -> Hash[String, json_value]
102
+ def ir_identity
103
+ {
104
+ "identity_scope" => IR_IDENTITY_SCOPE,
105
+ "grammar" => {
106
+ "schema_version" => @automaton.grammar.schema_version,
107
+ "digest" => grammar_digest
108
+ },
109
+ "automaton" => {
110
+ "schema_version" => @automaton.schema_version,
111
+ "algorithm" => @automaton.algorithm,
112
+ "digest" => automaton_digest
113
+ }
114
+ }
115
+ end
116
+
117
+ # @rbs () -> Hash[String, json_value]
118
+ def table_identity
119
+ {
120
+ "logical_path" => @table_path,
121
+ "artifact_type" => TableArtifact::ARTIFACT_TYPE,
122
+ "schema_version" => TableArtifact::SCHEMA_VERSION,
123
+ "representation" => @table.payload.dig("table_format", "representation"),
124
+ "artifact_digest" => prefixed_digest(Digest::SHA256.hexdigest(@table.dump)),
125
+ "payload_digest" => @table.identity.fetch("payload_digest")
126
+ }
127
+ end
128
+
129
+ # @rbs () -> void
130
+ def validate_table_identity!
131
+ identity = @table.identity
132
+ return if identity.fetch("grammar_digest") == grammar_digest &&
133
+ identity.fetch("automaton_digest") == automaton_digest
134
+
135
+ raise ArgumentError, "table artifact does not match the supplied Automaton IR"
136
+ end
137
+
138
+ # @rbs () -> String
139
+ def grammar_digest
140
+ @grammar_digest ||= "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(@automaton.grammar))}"
141
+ end
142
+
143
+ # @rbs () -> String
144
+ def automaton_digest
145
+ @automaton_digest ||= "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(@automaton))}"
146
+ end
147
+
148
+ # @rbs (String digest) -> String
149
+ def prefixed_digest(digest)
150
+ digest.start_with?("sha256:") ? digest : "sha256:#{digest}"
151
+ end
152
+
153
+ # @rbs (Hash[Symbol, String] value) -> Hash[String, String]
154
+ def stringify_keys(value)
155
+ value.to_h { |key, child| [key.to_s, child] }
156
+ end
157
+ end
158
+ end
159
+ end