ibex 0.1.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 (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -123
  3. data/docs/architecture.md +477 -31
  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 +106 -0
  12. data/docs/cst.md +237 -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 +306 -0
  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 +26 -0
  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 +114 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +629 -18
  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 +56 -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 +45 -6
  63. data/docs/release-readiness.md +221 -0
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +234 -0
  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/examples/README.md +60 -0
  75. data/examples/calculator.y +44 -0
  76. data/examples/csv.y +32 -0
  77. data/examples/ini.y +70 -0
  78. data/examples/json.y +48 -0
  79. data/examples/tiny_language.y +75 -0
  80. data/lib/ibex/analysis/digraph.rb +128 -0
  81. data/lib/ibex/analysis/sets.rb +5 -4
  82. data/lib/ibex/analysis.rb +1 -0
  83. data/lib/ibex/artifact_set.rb +63 -0
  84. data/lib/ibex/bison_import/importer.rb +516 -0
  85. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  86. data/lib/ibex/bison_import.rb +200 -0
  87. data/lib/ibex/bounded_subprocess.rb +171 -0
  88. data/lib/ibex/cli/ambiguity.rb +75 -0
  89. data/lib/ibex/cli/analysis.rb +134 -0
  90. data/lib/ibex/cli/bison_import.rb +121 -0
  91. data/lib/ibex/cli/config.rb +128 -0
  92. data/lib/ibex/cli/counterexample_options.rb +6 -2
  93. data/lib/ibex/cli/coverage.rb +173 -0
  94. data/lib/ibex/cli/debug.rb +106 -0
  95. data/lib/ibex/cli/diagnostics.rb +136 -0
  96. data/lib/ibex/cli/documentation.rb +70 -0
  97. data/lib/ibex/cli/equiv.rb +169 -0
  98. data/lib/ibex/cli/error_messages.rb +184 -0
  99. data/lib/ibex/cli/explain.rb +85 -0
  100. data/lib/ibex/cli/fix.rb +203 -0
  101. data/lib/ibex/cli/formatting.rb +387 -0
  102. data/lib/ibex/cli/fuzz.rb +200 -0
  103. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  104. data/lib/ibex/cli/generation_artifacts.rb +189 -0
  105. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  106. data/lib/ibex/cli/grammar_tests.rb +140 -0
  107. data/lib/ibex/cli/ir_tools.rb +128 -0
  108. data/lib/ibex/cli/lsp.rb +33 -0
  109. data/lib/ibex/cli/outputs.rb +133 -23
  110. data/lib/ibex/cli/racc_migration.rb +87 -0
  111. data/lib/ibex/cli/reduce.rb +202 -0
  112. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  113. data/lib/ibex/cli/samples.rb +118 -0
  114. data/lib/ibex/cli/verify.rb +116 -0
  115. data/lib/ibex/cli/watch.rb +104 -0
  116. data/lib/ibex/cli.rb +825 -55
  117. data/lib/ibex/codegen/action_locations.rb +103 -0
  118. data/lib/ibex/codegen/action_method_source.rb +209 -0
  119. data/lib/ibex/codegen/action_source.rb +119 -0
  120. data/lib/ibex/codegen/ambiguity.rb +174 -0
  121. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  122. data/lib/ibex/codegen/documentation.rb +170 -0
  123. data/lib/ibex/codegen/explain.rb +380 -0
  124. data/lib/ibex/codegen/generated_action_abi.rb +293 -0
  125. data/lib/ibex/codegen/html.rb +94 -10
  126. data/lib/ibex/codegen/mermaid.rb +43 -0
  127. data/lib/ibex/codegen/railroad.rb +197 -0
  128. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  129. data/lib/ibex/codegen/rbs.rb +331 -2
  130. data/lib/ibex/codegen/report.rb +67 -15
  131. data/lib/ibex/codegen/ruby.rb +305 -66
  132. data/lib/ibex/codegen/ruby_actions.rb +146 -0
  133. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  134. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  135. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  136. data/lib/ibex/codegen/ruby_syntax.rb +96 -0
  137. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  138. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  139. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  140. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  141. data/lib/ibex/configuration/explanation.rb +421 -0
  142. data/lib/ibex/configuration/inspector.rb +232 -0
  143. data/lib/ibex/configuration.rb +580 -0
  144. data/lib/ibex/coverage/collector.rb +198 -0
  145. data/lib/ibex/coverage/event_stream.rb +103 -0
  146. data/lib/ibex/coverage/report.rb +267 -0
  147. data/lib/ibex/coverage/runtime_event_validator.rb +169 -0
  148. data/lib/ibex/coverage.rb +13 -0
  149. data/lib/ibex/delta_reducer.rb +99 -0
  150. data/lib/ibex/diff.rb +140 -0
  151. data/lib/ibex/equiv/machine.rb +135 -0
  152. data/lib/ibex/equiv.rb +373 -0
  153. data/lib/ibex/error_messages/parser.rb +159 -0
  154. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  155. data/lib/ibex/error_messages/renderer.rb +65 -0
  156. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  157. data/lib/ibex/error_messages/update.rb +169 -0
  158. data/lib/ibex/error_messages.rb +165 -0
  159. data/lib/ibex/fix.rb +557 -0
  160. data/lib/ibex/frontend/ast.rb +166 -7
  161. data/lib/ibex/frontend/bootstrap_parser.rb +40 -4
  162. data/lib/ibex/frontend/diagnostic.rb +81 -0
  163. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  164. data/lib/ibex/frontend/dsl.rb +66 -7
  165. data/lib/ibex/frontend/formatter.rb +453 -0
  166. data/lib/ibex/frontend/generated_parser.rb +644 -190
  167. data/lib/ibex/frontend/generated_parser_base.rb +170 -32
  168. data/lib/ibex/frontend/generated_parser_includes.rb +51 -0
  169. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  170. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  171. data/lib/ibex/frontend/generation.rb +33 -0
  172. data/lib/ibex/frontend/lexer.rb +162 -10
  173. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  174. data/lib/ibex/frontend/parser/declarations.rb +262 -11
  175. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  176. data/lib/ibex/frontend/parser/rules.rb +43 -6
  177. data/lib/ibex/frontend/parser.rb +182 -4
  178. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  179. data/lib/ibex/frontend/regenerator.rb +27 -1
  180. data/lib/ibex/frontend/resolution.rb +70 -0
  181. data/lib/ibex/frontend/resolver.rb +217 -0
  182. data/lib/ibex/frontend/rule_documentation.rb +104 -0
  183. data/lib/ibex/frontend/source_cursor.rb +133 -9
  184. data/lib/ibex/frontend/source_document.rb +229 -0
  185. data/lib/ibex/frontend/source_loader.rb +150 -0
  186. data/lib/ibex/frontend/source_span.rb +89 -0
  187. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  188. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  189. data/lib/ibex/frontend/token_adapter/declaration_state.rb +244 -22
  190. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  191. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  192. data/lib/ibex/frontend/token_adapter.rb +18 -3
  193. data/lib/ibex/frontend.rb +14 -2
  194. data/lib/ibex/fuzz.rb +219 -0
  195. data/lib/ibex/generation_input.rb +57 -0
  196. data/lib/ibex/generation_manifest.rb +211 -0
  197. data/lib/ibex/generation_transaction.rb +261 -0
  198. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  199. data/lib/ibex/generation_transaction_validation.rb +196 -0
  200. data/lib/ibex/grammar_tests.rb +206 -0
  201. data/lib/ibex/ir/automaton_ir.rb +86 -11
  202. data/lib/ibex/ir/grammar_ir.rb +170 -26
  203. data/lib/ibex/ir/lexer_ir.rb +76 -0
  204. data/lib/ibex/ir/parser_contract.rb +135 -0
  205. data/lib/ibex/ir/serialize.rb +268 -50
  206. data/lib/ibex/ir/validator/automaton.rb +375 -0
  207. data/lib/ibex/ir/validator/base.rb +147 -0
  208. data/lib/ibex/ir/validator/grammar.rb +634 -0
  209. data/lib/ibex/ir/validator/lexer.rb +113 -0
  210. data/lib/ibex/ir/validator.rb +63 -0
  211. data/lib/ibex/ir.rb +71 -5
  212. data/lib/ibex/lalr/build_metrics.rb +71 -0
  213. data/lib/ibex/lalr/builder.rb +514 -52
  214. data/lib/ibex/lalr/conflict.rb +1 -0
  215. data/lib/ibex/lalr/conflict_search.rb +26 -9
  216. data/lib/ibex/lalr/counterexample.rb +38 -18
  217. data/lib/ibex/lalr/direct_lookaheads.rb +289 -0
  218. data/lib/ibex/lalr/goto_follows.rb +229 -0
  219. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  220. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  221. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  222. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  223. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  224. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  225. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  226. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  227. data/lib/ibex/lalr/ielr_partition.rb +166 -0
  228. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  229. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  230. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  231. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  232. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  233. data/lib/ibex/lalr.rb +59 -1
  234. data/lib/ibex/location.rb +129 -0
  235. data/lib/ibex/lsp/document_handlers.rb +68 -0
  236. data/lib/ibex/lsp/document_store.rb +264 -0
  237. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  238. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  239. data/lib/ibex/lsp/initialization_handlers.rb +80 -0
  240. data/lib/ibex/lsp/navigation_handlers.rb +70 -0
  241. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  242. data/lib/ibex/lsp/position_codec.rb +113 -0
  243. data/lib/ibex/lsp/protocol_error.rb +33 -0
  244. data/lib/ibex/lsp/request_handlers.rb +46 -0
  245. data/lib/ibex/lsp/request_support.rb +94 -0
  246. data/lib/ibex/lsp/server.rb +148 -0
  247. data/lib/ibex/lsp/symbol_index.rb +241 -0
  248. data/lib/ibex/lsp/symbol_index_builder.rb +277 -0
  249. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  250. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  251. data/lib/ibex/lsp/symbol_occurrence.rb +23 -0
  252. data/lib/ibex/lsp/transport.rb +128 -0
  253. data/lib/ibex/lsp/workspace.rb +128 -0
  254. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  255. data/lib/ibex/lsp.rb +33 -0
  256. data/lib/ibex/messages/en.yml +28 -0
  257. data/lib/ibex/messages/ja.yml +28 -0
  258. data/lib/ibex/messages.rb +68 -0
  259. data/lib/ibex/metrics.rb +175 -0
  260. data/lib/ibex/normalize/declarations.rb +142 -7
  261. data/lib/ibex/normalize/diagnostics.rb +94 -24
  262. data/lib/ibex/normalize/expander.rb +64 -59
  263. data/lib/ibex/normalize/expression.rb +63 -40
  264. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  265. data/lib/ibex/normalize/inline_expansion.rb +465 -0
  266. data/lib/ibex/normalize/inline_validation.rb +174 -0
  267. data/lib/ibex/normalize/lexer.rb +131 -0
  268. data/lib/ibex/normalize/named_references.rb +60 -0
  269. data/lib/ibex/normalize/nodes.rb +46 -0
  270. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +70 -0
  271. data/lib/ibex/normalize/parameter_lowering.rb +143 -0
  272. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  273. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  274. data/lib/ibex/normalize/parameters.rb +226 -0
  275. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  276. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  277. data/lib/ibex/normalize.rb +172 -17
  278. data/lib/ibex/racc_migration/checker.rb +122 -0
  279. data/lib/ibex/racc_migration/harness.rb +177 -0
  280. data/lib/ibex/racc_migration/report.rb +97 -0
  281. data/lib/ibex/racc_migration.rb +12 -0
  282. data/lib/ibex/rake_task.rb +116 -0
  283. data/lib/ibex/samples.rb +216 -0
  284. data/lib/ibex/table_artifact/builder.rb +266 -0
  285. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  286. data/lib/ibex/table_artifact/document.rb +39 -0
  287. data/lib/ibex/table_artifact/executor.rb +187 -0
  288. data/lib/ibex/table_artifact/serializer.rb +57 -0
  289. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  290. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  291. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  292. data/lib/ibex/table_artifact/validator.rb +253 -0
  293. data/lib/ibex/table_artifact.rb +88 -0
  294. data/lib/ibex/table_simulation/result.rb +55 -0
  295. data/lib/ibex/table_simulation/simulator.rb +253 -0
  296. data/lib/ibex/table_simulation/step.rb +62 -0
  297. data/lib/ibex/table_simulation/text.rb +31 -0
  298. data/lib/ibex/table_simulation.rb +13 -0
  299. data/lib/ibex/tables.rb +22 -69
  300. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  301. data/lib/ibex/verification_report/builder.rb +159 -0
  302. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  303. data/lib/ibex/verification_report/logical_path.rb +68 -0
  304. data/lib/ibex/verification_report/validator.rb +329 -0
  305. data/lib/ibex/verification_report.rb +69 -0
  306. data/lib/ibex/verify/action_correspondence.rb +138 -0
  307. data/lib/ibex/verify/language_witness.rb +295 -0
  308. data/lib/ibex/verify/reference_collection.rb +190 -0
  309. data/lib/ibex/verify/result.rb +73 -0
  310. data/lib/ibex/verify/verifier.rb +598 -0
  311. data/lib/ibex/verify.rb +15 -0
  312. data/lib/ibex/version.rb +1 -1
  313. data/lib/ibex/watch/runner.rb +181 -0
  314. data/lib/ibex/watch/source_snapshot.rb +98 -0
  315. data/lib/ibex/watch.rb +11 -0
  316. data/lib/ibex.rb +35 -1
  317. data/schema/automaton-ir-definitions.schema.json +401 -0
  318. data/schema/automaton-ir.schema.json +65 -0
  319. data/schema/benchmark-v1.schema.json +212 -0
  320. data/schema/benchmark-v2.schema.json +61 -0
  321. data/schema/bison-import-v1.schema.json +106 -0
  322. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  323. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  324. data/schema/construction-profile-v1.schema.json +779 -0
  325. data/schema/cst-v1.json +170 -0
  326. data/schema/diff-v1.schema.json +41 -0
  327. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  328. data/schema/equiv-v1.schema.json +64 -0
  329. data/schema/error-ux-review-v1.schema.json +609 -0
  330. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  331. data/schema/error-ux-round2-v1.schema.json +519 -0
  332. data/schema/error-ux-v1.schema.json +258 -0
  333. data/schema/explain-v1.schema.json +495 -0
  334. data/schema/fix-v1.schema.json +64 -0
  335. data/schema/fix-v2.schema.json +66 -0
  336. data/schema/fix-v3.schema.json +101 -0
  337. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  338. data/schema/fuzz-regression-v1.schema.json +79 -0
  339. data/schema/fuzz-v1.schema.json +114 -0
  340. data/schema/generation-manifest-v1.schema.json +115 -0
  341. data/schema/grammar-ir-extensions.schema.json +734 -0
  342. data/schema/grammar-ir-foundation.schema.json +426 -0
  343. data/schema/grammar-ir.schema.json +271 -0
  344. data/schema/ielr-benchmark-v1.schema.json +85 -0
  345. data/schema/lexer-ir-v1.schema.json +215 -0
  346. data/schema/lexer-profile-v1.schema.json +491 -0
  347. data/schema/metrics-v1.schema.json +60 -0
  348. data/schema/migration-check-v1.schema.json +60 -0
  349. data/schema/performance-comparison-v1.schema.json +395 -0
  350. data/schema/public-performance-comparison-v1.schema.json +506 -0
  351. data/schema/public-performance-profile-v1.schema.json +360 -0
  352. data/schema/reduce-v1.schema.json +22 -0
  353. data/schema/reduce-v2.schema.json +63 -0
  354. data/schema/runtime-coverage-v1.schema.json +86 -0
  355. data/schema/runtime-event-v1.schema.json +308 -0
  356. data/schema/table-artifact-v1.schema.json +377 -0
  357. data/schema/table-simulation-v1.schema.json +70 -0
  358. data/schema/verification-report-v1.schema.json +222 -0
  359. data/schema/verify-v1.schema.json +42 -0
  360. data/sig/ibex/analysis/digraph.rbs +19 -0
  361. data/sig/ibex/artifact_set.rbs +37 -0
  362. data/sig/ibex/bison_import/importer.rbs +140 -0
  363. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  364. data/sig/ibex/bison_import.rbs +95 -0
  365. data/sig/ibex/bounded_subprocess.rbs +76 -0
  366. data/sig/ibex/cli/ambiguity.rbs +28 -0
  367. data/sig/ibex/cli/analysis.rbs +43 -0
  368. data/sig/ibex/cli/bison_import.rbs +32 -0
  369. data/sig/ibex/cli/config.rbs +35 -0
  370. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  371. data/sig/ibex/cli/coverage.rbs +53 -0
  372. data/sig/ibex/cli/debug.rbs +28 -0
  373. data/sig/ibex/cli/diagnostics.rbs +51 -0
  374. data/sig/ibex/cli/documentation.rbs +29 -0
  375. data/sig/ibex/cli/equiv.rbs +49 -0
  376. data/sig/ibex/cli/error_messages.rbs +63 -0
  377. data/sig/ibex/cli/explain.rbs +31 -0
  378. data/sig/ibex/cli/fix.rbs +53 -0
  379. data/sig/ibex/cli/formatting.rbs +107 -0
  380. data/sig/ibex/cli/fuzz.rbs +47 -0
  381. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  382. data/sig/ibex/cli/generation_artifacts.rbs +69 -0
  383. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  384. data/sig/ibex/cli/grammar_tests.rbs +48 -0
  385. data/sig/ibex/cli/ir_tools.rbs +39 -0
  386. data/sig/ibex/cli/lsp.rbs +14 -0
  387. data/sig/ibex/cli/outputs.rbs +26 -3
  388. data/sig/ibex/cli/racc_migration.rbs +30 -0
  389. data/sig/ibex/cli/reduce.rbs +60 -0
  390. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  391. data/sig/ibex/cli/samples.rbs +38 -0
  392. data/sig/ibex/cli/verify.rbs +28 -0
  393. data/sig/ibex/cli/watch.rbs +43 -0
  394. data/sig/ibex/cli.rbs +182 -9
  395. data/sig/ibex/codegen/action_locations.rbs +45 -0
  396. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  397. data/sig/ibex/codegen/action_source.rbs +50 -0
  398. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  399. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  400. data/sig/ibex/codegen/documentation.rbs +50 -0
  401. data/sig/ibex/codegen/explain.rbs +105 -0
  402. data/sig/ibex/codegen/generated_action_abi.rbs +105 -0
  403. data/sig/ibex/codegen/html.rbs +18 -2
  404. data/sig/ibex/codegen/mermaid.rbs +16 -0
  405. data/sig/ibex/codegen/railroad.rbs +82 -0
  406. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  407. data/sig/ibex/codegen/rbs.rbs +86 -4
  408. data/sig/ibex/codegen/report.rbs +16 -8
  409. data/sig/ibex/codegen/ruby.rbs +105 -28
  410. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  411. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  412. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  413. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  414. data/sig/ibex/codegen/ruby_syntax.rbs +27 -0
  415. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  416. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  417. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  418. data/sig/ibex/configuration/explanation.rbs +195 -0
  419. data/sig/ibex/configuration/inspector.rbs +53 -0
  420. data/sig/ibex/configuration.rbs +242 -0
  421. data/sig/ibex/coverage/collector.rbs +78 -0
  422. data/sig/ibex/coverage/event_stream.rbs +44 -0
  423. data/sig/ibex/coverage/report.rbs +102 -0
  424. data/sig/ibex/coverage/runtime_event_validator.rbs +70 -0
  425. data/sig/ibex/coverage.rbs +7 -0
  426. data/sig/ibex/delta_reducer.rbs +38 -0
  427. data/sig/ibex/diff.rbs +47 -0
  428. data/sig/ibex/equiv/machine.rbs +54 -0
  429. data/sig/ibex/equiv.rbs +129 -0
  430. data/sig/ibex/error_messages/parser.rbs +58 -0
  431. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  432. data/sig/ibex/error_messages/renderer.rbs +23 -0
  433. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  434. data/sig/ibex/error_messages/update.rbs +43 -0
  435. data/sig/ibex/error_messages.rbs +85 -0
  436. data/sig/ibex/fix.rbs +138 -0
  437. data/sig/ibex/frontend/ast.rbs +237 -22
  438. data/sig/ibex/frontend/bootstrap_parser.rbs +13 -0
  439. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  440. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  441. data/sig/ibex/frontend/dsl.rbs +33 -4
  442. data/sig/ibex/frontend/formatter.rbs +143 -0
  443. data/sig/ibex/frontend/generated_parser.rbs +228 -68
  444. data/sig/ibex/frontend/generated_parser_base.rbs +65 -12
  445. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  446. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  447. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  448. data/sig/ibex/frontend/generation.rbs +6 -0
  449. data/sig/ibex/frontend/lexer.rbs +49 -2
  450. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  451. data/sig/ibex/frontend/parser/declarations.rbs +57 -0
  452. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  453. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  454. data/sig/ibex/frontend/parser.rbs +66 -0
  455. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  456. data/sig/ibex/frontend/regenerator.rbs +11 -0
  457. data/sig/ibex/frontend/resolution.rbs +34 -0
  458. data/sig/ibex/frontend/resolver.rbs +91 -0
  459. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  460. data/sig/ibex/frontend/source_cursor.rbs +38 -5
  461. data/sig/ibex/frontend/source_document.rbs +119 -0
  462. data/sig/ibex/frontend/source_loader.rbs +66 -0
  463. data/sig/ibex/frontend/source_span.rbs +56 -0
  464. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  465. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  466. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +83 -3
  467. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  468. data/sig/ibex/frontend/token_adapter.rbs +11 -2
  469. data/sig/ibex/frontend.rbs +1 -1
  470. data/sig/ibex/fuzz.rbs +71 -0
  471. data/sig/ibex/generation_input.rbs +37 -0
  472. data/sig/ibex/generation_manifest.rbs +70 -0
  473. data/sig/ibex/generation_transaction.rbs +82 -0
  474. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  475. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  476. data/sig/ibex/grammar_tests.rbs +93 -0
  477. data/sig/ibex/ir/automaton_ir.rbs +35 -10
  478. data/sig/ibex/ir/grammar_ir.rbs +96 -22
  479. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  480. data/sig/ibex/ir/parser_contract.rbs +69 -0
  481. data/sig/ibex/ir/serialize.rbs +75 -23
  482. data/sig/ibex/ir/validator/automaton.rbs +112 -0
  483. data/sig/ibex/ir/validator/base.rbs +69 -0
  484. data/sig/ibex/ir/validator/grammar.rbs +195 -0
  485. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  486. data/sig/ibex/ir/validator.rbs +16 -0
  487. data/sig/ibex/ir.rbs +45 -5
  488. data/sig/ibex/lalr/build_metrics.rbs +60 -0
  489. data/sig/ibex/lalr/builder.rbs +150 -19
  490. data/sig/ibex/lalr/conflict_search.rbs +12 -5
  491. data/sig/ibex/lalr/counterexample.rbs +20 -14
  492. data/sig/ibex/lalr/direct_lookaheads.rbs +93 -0
  493. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  494. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  495. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  496. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  497. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  498. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  499. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  500. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  501. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  502. data/sig/ibex/lalr/ielr_partition.rbs +71 -0
  503. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  504. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  505. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  506. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  507. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  508. data/sig/ibex/lalr.rbs +17 -1
  509. data/sig/ibex/location.rbs +67 -0
  510. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  511. data/sig/ibex/lsp/document_store.rbs +94 -0
  512. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  513. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  514. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  515. data/sig/ibex/lsp/navigation_handlers.rbs +33 -0
  516. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  517. data/sig/ibex/lsp/position_codec.rbs +44 -0
  518. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  519. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  520. data/sig/ibex/lsp/request_support.rbs +40 -0
  521. data/sig/ibex/lsp/server.rbs +55 -0
  522. data/sig/ibex/lsp/symbol_index.rbs +77 -0
  523. data/sig/ibex/lsp/symbol_index_builder.rbs +88 -0
  524. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  525. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  526. data/sig/ibex/lsp/symbol_occurrence.rbs +31 -0
  527. data/sig/ibex/lsp/transport.rbs +47 -0
  528. data/sig/ibex/lsp/workspace.rbs +42 -0
  529. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  530. data/sig/ibex/lsp.rbs +7 -0
  531. data/sig/ibex/messages.rbs +26 -0
  532. data/sig/ibex/metrics.rbs +50 -0
  533. data/sig/ibex/normalize/declarations.rbs +31 -0
  534. data/sig/ibex/normalize/diagnostics.rbs +13 -4
  535. data/sig/ibex/normalize/expander.rbs +22 -16
  536. data/sig/ibex/normalize/expression.rbs +11 -9
  537. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  538. data/sig/ibex/normalize/inline_expansion.rbs +129 -0
  539. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  540. data/sig/ibex/normalize/lexer.rbs +37 -0
  541. data/sig/ibex/normalize/named_references.rbs +20 -0
  542. data/sig/ibex/normalize/nodes.rbs +14 -0
  543. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +33 -0
  544. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  545. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  546. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  547. data/sig/ibex/normalize/parameters.rbs +51 -0
  548. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  549. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  550. data/sig/ibex/normalize.rbs +129 -18
  551. data/sig/ibex/racc_migration/checker.rbs +36 -0
  552. data/sig/ibex/racc_migration/harness.rbs +16 -0
  553. data/sig/ibex/racc_migration/report.rbs +55 -0
  554. data/sig/ibex/racc_migration.rbs +8 -0
  555. data/sig/ibex/rake_task.rbs +51 -0
  556. data/sig/ibex/samples.rbs +60 -0
  557. data/sig/ibex/table_artifact/builder.rbs +93 -0
  558. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  559. data/sig/ibex/table_artifact/document.rbs +27 -0
  560. data/sig/ibex/table_artifact/executor.rbs +66 -0
  561. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  562. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  563. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  564. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  565. data/sig/ibex/table_artifact/validator.rbs +66 -0
  566. data/sig/ibex/table_artifact.rbs +32 -0
  567. data/sig/ibex/table_simulation/result.rbs +36 -0
  568. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  569. data/sig/ibex/table_simulation/step.rbs +42 -0
  570. data/sig/ibex/table_simulation/text.rbs +14 -0
  571. data/sig/ibex/table_simulation.rbs +7 -0
  572. data/sig/ibex/tables.rbs +10 -35
  573. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  574. data/sig/ibex/verification_report/builder.rbs +53 -0
  575. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  576. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  577. data/sig/ibex/verification_report/validator.rbs +114 -0
  578. data/sig/ibex/verification_report.rbs +44 -0
  579. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  580. data/sig/ibex/verify/language_witness.rbs +112 -0
  581. data/sig/ibex/verify/reference_collection.rbs +63 -0
  582. data/sig/ibex/verify/result.rbs +52 -0
  583. data/sig/ibex/verify/verifier.rbs +170 -0
  584. data/sig/ibex/verify.rbs +6 -0
  585. data/sig/ibex/watch/runner.rbs +58 -0
  586. data/sig/ibex/watch/source_snapshot.rbs +46 -0
  587. data/sig/ibex/watch.rbs +7 -0
  588. data/sig/ibex.rbs +2 -0
  589. metadata +524 -19
  590. data/.rubocop.yml +0 -43
  591. data/CHANGELOG.md +0 -30
  592. data/Rakefile +0 -25
  593. data/Steepfile +0 -10
  594. data/docs/compat-notes.md +0 -37
  595. data/docs/lexer-coverage.md +0 -14
  596. data/docs/phase10-extensions.md +0 -27
  597. data/gemfiles/Gemfile +0 -7
  598. data/gemfiles/Gemfile.lock +0 -98
  599. data/lib/ibex/frontend/grammar.y +0 -156
  600. data/lib/ibex/runtime/parser.rb +0 -360
  601. data/lib/ibex/runtime.rb +0 -8
  602. data/sig/ibex/runtime/parser.rbs +0 -167
  603. data/sig/ibex/runtime.rbs +0 -6
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "rbconfig"
5
+ require "tempfile"
6
+ require "timeout"
7
+ require "tmpdir"
8
+
9
+ module Ibex
10
+ # Executes grammar-declared source examples against one generated parser.
11
+ module GrammarTests
12
+ DEFAULT_TIMEOUT = 10
13
+ RESULT_MARKER = "IBEX_GRAMMAR_TEST_RESULT="
14
+
15
+ Result = Struct.new(
16
+ :expectation, #: Symbol
17
+ :actual, #: Symbol
18
+ :error_class, #: String?
19
+ :error_message, #: String?
20
+ :location, #: IR::location
21
+ :production_ids, #: Array[Integer]
22
+ keyword_init: true
23
+ )
24
+
25
+ class Result
26
+ # @rbs () -> bool
27
+ def passed? = expectation == actual
28
+ end
29
+
30
+ ProductionCoverage = Struct.new(
31
+ :covered_ids, #: Array[Integer]
32
+ :missing_ids, #: Array[Integer]
33
+ :production_count, #: Integer
34
+ keyword_init: true
35
+ )
36
+
37
+ class ProductionCoverage
38
+ # @rbs () -> Float
39
+ def percentage
40
+ return 100.0 if production_count.zero?
41
+
42
+ covered_ids.length.fdiv(production_count) * 100
43
+ end
44
+
45
+ # @rbs (Integer minimum) -> bool
46
+ def meets?(minimum)
47
+ covered_ids.length * 100 >= production_count * minimum
48
+ end
49
+
50
+ # @rbs () -> bool
51
+ def complete? = missing_ids.empty?
52
+ end
53
+
54
+ # Runs all examples in one isolated process while creating a fresh parser
55
+ # instance for every case.
56
+ class Runner
57
+ CHILD_RUNNER = <<~'RUBY'
58
+ require "json"
59
+
60
+ load ARGV.fetch(0)
61
+ parser_class = ARGV.fetch(1).split("::").reject(&:empty?).reduce(Object) do |scope, name|
62
+ scope.const_get(name, false)
63
+ end
64
+ tests = JSON.parse(ARGV.fetch(2))
65
+ results = tests.map do |test|
66
+ production_ids = []
67
+ begin
68
+ parser = parser_class.new
69
+ raise NoMethodError, "#{parser_class} must define parse(source)" unless parser.respond_to?(:parse)
70
+
71
+ parser.observe do |event|
72
+ production_ids << event.data.fetch("production_id") if event.type == :reduce
73
+ end
74
+ parser.parse(test.fetch("source"))
75
+ result = { "actual" => "accept", "error_class" => nil, "error_message" => nil }
76
+ rescue Ibex::Runtime::ParseError => error
77
+ result = { "actual" => "reject", "error_class" => error.class.name, "error_message" => error.message }
78
+ rescue SystemExit, SignalException, StandardError => error
79
+ result = { "actual" => "error", "error_class" => error.class.name, "error_message" => error.message }
80
+ end
81
+ result.merge("production_ids" => production_ids)
82
+ end
83
+ puts "IBEX_GRAMMAR_TEST_RESULT=#{JSON.generate(results)}"
84
+ RUBY
85
+
86
+ # @rbs @automaton: IR::Automaton
87
+ # @rbs @timeout: Integer
88
+
89
+ # @rbs (IR::Automaton automaton, ?timeout: Integer) -> void
90
+ def initialize(automaton, timeout: DEFAULT_TIMEOUT)
91
+ raise ArgumentError, "timeout must be a positive Integer" unless timeout.is_a?(Integer) && timeout.positive?
92
+
93
+ @automaton = automaton
94
+ @timeout = timeout
95
+ end
96
+
97
+ # @rbs () -> Array[Result]
98
+ def run
99
+ grammar = @automaton.grammar
100
+ raise Ibex::Error, "(test):1:1: grammar declares no %test cases" if grammar.grammar_tests.empty?
101
+ unless grammar.parser_parameters.empty?
102
+ raise Ibex::Error, "(test):1:1: %test cannot instantiate a parser with required %param declarations"
103
+ end
104
+
105
+ Dir.mktmpdir("ibex-grammar-tests") do |directory|
106
+ parser_path = File.join(directory, "parser.rb")
107
+ runner_path = File.join(directory, "runner.rb")
108
+ File.binwrite(parser_path, generated_parser)
109
+ File.binwrite(runner_path, CHILD_RUNNER)
110
+ execute_child(parser_path, runner_path)
111
+ end
112
+ end
113
+
114
+ # @rbs (Array[Result] results) -> ProductionCoverage
115
+ def production_coverage(results)
116
+ production_count = @automaton.grammar.productions.length
117
+ covered_ids = results.flat_map(&:production_ids).uniq.sort.freeze
118
+ missing_ids = ((0...production_count).to_a - covered_ids).freeze
119
+ ProductionCoverage.new(
120
+ covered_ids: covered_ids, missing_ids: missing_ids, production_count: production_count
121
+ ).freeze
122
+ end
123
+
124
+ private
125
+
126
+ # @rbs () -> String
127
+ def generated_parser
128
+ Codegen::Ruby.new(@automaton, table: :compact, embedded: true, line_convert: true).generate
129
+ end
130
+
131
+ # @rbs (String parser_path, String runner_path) -> Array[Result]
132
+ def execute_child(parser_path, runner_path)
133
+ payload = JSON.generate(@automaton.grammar.grammar_tests)
134
+ stdout = Tempfile.new("ibex-grammar-test-stdout")
135
+ stderr = Tempfile.new("ibex-grammar-test-stderr")
136
+ stdout_path = stdout.path || raise(Ibex::Error, "(test):1:1: missing temporary stdout path")
137
+ stderr_path = stderr.path || raise(Ibex::Error, "(test):1:1: missing temporary stderr path")
138
+ pid = spawn(
139
+ RbConfig.ruby, runner_path, parser_path, @automaton.grammar.class_name, payload,
140
+ out: stdout_path, err: stderr_path
141
+ )
142
+ status = wait_for_child(pid)
143
+ output = File.binread(stdout_path)
144
+ errors = File.binread(stderr_path)
145
+ raise_child_failure(status, errors) unless status.success?
146
+
147
+ decode_results(output)
148
+ ensure
149
+ stdout&.close!
150
+ stderr&.close!
151
+ end
152
+
153
+ # @rbs (Integer pid) -> Process::Status
154
+ def wait_for_child(pid)
155
+ Timeout.timeout(@timeout) { Process.wait2(pid).fetch(1) }
156
+ rescue Timeout::Error
157
+ Process.kill("KILL", pid)
158
+ Process.wait(pid)
159
+ raise Ibex::Error, "(test):1:1: grammar tests exceeded #{@timeout} seconds"
160
+ end
161
+
162
+ # @rbs (Process::Status status, String errors) -> bot
163
+ def raise_child_failure(status, errors)
164
+ detail = errors.lines.last&.strip
165
+ suffix = detail && !detail.empty? ? ": #{detail}" : ""
166
+ raise Ibex::Error, "(test):1:1: grammar test process exited with status #{status.exitstatus}#{suffix}"
167
+ end
168
+
169
+ # @rbs (String output) -> Array[Result]
170
+ def decode_results(output)
171
+ line = output.lines.reverse.find { |candidate| candidate.start_with?(RESULT_MARKER) }
172
+ raise Ibex::Error, "(test):1:1: grammar test process produced no result" unless line
173
+
174
+ documents = JSON.parse(line.delete_prefix(RESULT_MARKER).strip)
175
+ tests = @automaton.grammar.grammar_tests
176
+ unless documents.is_a?(Array) && documents.length == tests.length
177
+ raise Ibex::Error, "(test):1:1: grammar test process returned an invalid result count"
178
+ end
179
+
180
+ tests.zip(documents).map { |test, document| build_result(test, document) }
181
+ rescue JSON::ParserError, ArgumentError => e
182
+ raise Ibex::Error, "(test):1:1: invalid grammar test process result: #{e.message}"
183
+ end
184
+
185
+ # @rbs (IR::grammar_test test, Object? document) -> Result
186
+ def build_result(test, document)
187
+ unless document.is_a?(Hash) && %w[accept reject error].include?(document["actual"])
188
+ raise Ibex::Error, "(test):1:1: grammar test process returned an invalid case result"
189
+ end
190
+
191
+ production_ids = document["production_ids"]
192
+ production_count = @automaton.grammar.productions.length
193
+ unless production_ids.is_a?(Array) &&
194
+ production_ids.all? { |id| id.is_a?(Integer) && id.between?(0, production_count - 1) }
195
+ raise Ibex::Error, "(test):1:1: grammar test process returned invalid production coverage"
196
+ end
197
+
198
+ Result.new(
199
+ expectation: test[:expectation], actual: document.fetch("actual").to_sym,
200
+ error_class: document["error_class"], error_message: document["error_message"],
201
+ location: test[:loc], production_ids: production_ids.freeze
202
+ ).freeze
203
+ end
204
+ end
205
+ end
206
+ end
@@ -16,7 +16,7 @@ module Ibex
16
16
  freeze
17
17
  end
18
18
 
19
- # @rbs (Grammar grammar) -> Hash[Symbol, untyped]
19
+ # @rbs (Grammar grammar) -> Hash[Symbol, Object?]
20
20
  def to_h(grammar)
21
21
  { production: @production, dot: @dot,
22
22
  lookaheads: @lookaheads.map do |id|
@@ -50,7 +50,7 @@ module Ibex
50
50
  freeze
51
51
  end
52
52
 
53
- # @rbs (Grammar grammar) -> Hash[Symbol, untyped]
53
+ # @rbs (Grammar grammar) -> Hash[Symbol, Object?]
54
54
  def to_h(grammar)
55
55
  { id: @id, items: @items.map { |item| item.to_h(grammar) },
56
56
  transitions: named_keys(@transitions, grammar), actions: named_keys(@actions, grammar),
@@ -59,7 +59,7 @@ module Ibex
59
59
 
60
60
  private
61
61
 
62
- # @rbs (Hash[Integer, untyped] values, Grammar grammar) -> Hash[String, untyped]
62
+ # @rbs (Hash[Integer, Object?] values, Grammar grammar) -> Hash[String, Object?]
63
63
  def named_keys(values, grammar)
64
64
  values.to_h do |symbol_id, value|
65
65
  symbol = grammar.symbol_by_id(symbol_id) || raise(Ibex::Error, "missing grammar symbol id #{symbol_id}")
@@ -74,36 +74,111 @@ module Ibex
74
74
  attr_reader :grammar_digest #: String
75
75
  attr_reader :grammar #: Grammar
76
76
  attr_reader :states #: Array[AutomatonState]
77
+ attr_reader :entry_states #: Hash[String, Integer]
77
78
  attr_reader :conflict_summary #: conflict_summary
78
79
  attr_reader :schema_version #: Integer
80
+ attr_reader :entry_construction #: String
79
81
 
80
82
  # @rbs (grammar: Grammar, states: Array[AutomatonState], conflict_summary: conflict_summary,
81
- # ?algorithm: String, ?grammar_digest: String?, ?schema_version: Integer) -> void
83
+ # ?algorithm: String, ?grammar_digest: String?, ?entry_states: Hash[String, Integer]?,
84
+ # ?entry_construction: String) -> void
82
85
  def initialize(grammar:, states:, conflict_summary:, algorithm: "lalr1", grammar_digest: nil,
83
- schema_version: SCHEMA_VERSION)
86
+ entry_states: nil, entry_construction: "shared")
87
+ initialize_current(
88
+ grammar: grammar, states: states, conflict_summary: conflict_summary, algorithm: algorithm,
89
+ grammar_digest: grammar_digest, entry_states: entry_states, entry_construction: entry_construction
90
+ )
91
+ end
92
+
93
+ # @rbs (grammar: Grammar, states: Array[AutomatonState], conflict_summary: conflict_summary,
94
+ # algorithm: String, grammar_digest: String?, entry_states: Hash[String, Integer]?,
95
+ # entry_construction: String) -> void
96
+ def initialize_current(grammar:, states:, conflict_summary:, algorithm:, grammar_digest:, entry_states:,
97
+ entry_construction:)
98
+ unless grammar.schema_version == SCHEMA_VERSION
99
+ raise Ibex::Error, "automaton requires the current Grammar IR format"
100
+ end
101
+
84
102
  @algorithm = algorithm.freeze
85
103
  @grammar = grammar
86
- @grammar_digest = (grammar_digest || digest_for(grammar)).freeze
104
+ expected_digest = digest_for(grammar)
105
+ if grammar_digest && grammar_digest != expected_digest
106
+ raise Ibex::Error,
107
+ "(ir):1:1: $.grammar_digest does not match the embedded grammar; expected #{expected_digest.inspect}"
108
+ end
109
+ @grammar_digest = (grammar_digest || expected_digest).freeze
87
110
  @states = states.freeze
111
+ @entry_states = IR.deep_freeze(entry_states || { grammar.start => 0 })
112
+ validate_entry_states
88
113
  @conflict_summary = IR.deep_freeze(conflict_summary)
89
- @schema_version = schema_version
114
+ @schema_version = SCHEMA_VERSION
115
+ @entry_construction = validate_entry_construction(entry_construction)
116
+ validate_parser_contract
90
117
  freeze
91
118
  end
119
+ private :initialize_current
92
120
 
93
- # @rbs () -> Hash[Symbol, untyped]
121
+ # @rbs () -> Hash[Symbol, Object?]
94
122
  def to_h
95
- { ibex_ir: "automaton", schema_version: @schema_version, algorithm: @algorithm,
96
- grammar_digest: @grammar_digest, grammar: @grammar.to_h,
97
- states: @states.map { |state| state.to_h(@grammar) }, conflict_summary: @conflict_summary }
123
+ value = { ibex_ir: "automaton", schema_version: @schema_version, algorithm: @algorithm,
124
+ grammar_digest: @grammar_digest, grammar: @grammar.to_h,
125
+ states: @states.map { |state| state.to_h(@grammar) },
126
+ conflict_summary: @conflict_summary } #: Hash[Symbol, Object?]
127
+ value[:entry_states] = @entry_states unless @entry_states == { @grammar.start => 0 }
128
+ value[:entry_construction] = @entry_construction
129
+ value
98
130
  end
99
131
 
100
132
  private
101
133
 
134
+ # @rbs () -> void
135
+ def validate_entry_states
136
+ ordered = @grammar.starts.select { |name| @entry_states.key?(name) }
137
+ unless @entry_states.any? && @entry_states.keys == ordered
138
+ raise Ibex::Error, "(ir):1:1: automaton entry states must be an ordered subset of grammar starts"
139
+ end
140
+
141
+ @entry_states.each do |name, state|
142
+ next if state.between?(0, @states.length - 1)
143
+
144
+ raise Ibex::Error, "(ir):1:1: entry #{name} references missing state #{state}"
145
+ end
146
+ end
147
+
102
148
  # @rbs (Grammar grammar) -> String
103
149
  def digest_for(grammar)
104
150
  require "digest"
105
151
  "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(grammar))}"
106
152
  end
153
+
154
+ # @rbs (String value) -> String
155
+ def validate_entry_construction(value)
156
+ raise Ibex::Error, "entry construction must be shared or isolated" unless %w[shared isolated].include?(value)
157
+
158
+ value.dup.freeze
159
+ end
160
+
161
+ # @rbs () -> void
162
+ def validate_parser_contract
163
+ contract = @grammar.parser_contract
164
+ validate_parser_algorithm(contract)
165
+ validate_parser_entries(contract)
166
+ end
167
+
168
+ # @rbs (ParserContract contract) -> void
169
+ def validate_parser_algorithm(contract)
170
+ selected_algorithm = { "lalr1" => :lalr, "ielr1" => :ielr }.fetch(@algorithm, @algorithm.to_sym)
171
+ return unless contract.algorithm.explicit && contract.algorithm.value != selected_algorithm
172
+
173
+ raise Ibex::Error, "automaton algorithm conflicts with the embedded parser contract"
174
+ end
175
+
176
+ # @rbs (ParserContract contract) -> void
177
+ def validate_parser_entries(contract)
178
+ return unless contract.entries.explicit && contract.entries.value.to_s != @entry_construction
179
+
180
+ raise Ibex::Error, "automaton entry construction conflicts with the embedded parser contract"
181
+ end
107
182
  end
108
183
  end
109
184
  end
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "parser_contract"
4
+
3
5
  module Ibex
4
6
  module IR
7
+ # Grammar IR has one active wire format. A future incompatible format
8
+ # change may advance this number, but the runtime never treats old
9
+ # formats as an alternate compatibility surface.
5
10
  SCHEMA_VERSION = 1
11
+ SUPPORTED_SCHEMA_VERSIONS = [SCHEMA_VERSION].freeze #: Array[Integer]
6
12
 
7
- # @rbs (untyped value) -> untyped
13
+ # @rbs (Object? value) -> Object?
8
14
  def deep_freeze(value)
9
15
  case value
10
16
  when Array then value.each { |item| deep_freeze(item) }
@@ -26,16 +32,23 @@ module Ibex
26
32
  attr_reader :reserved #: bool
27
33
  attr_reader :precedence #: precedence?
28
34
  attr_reader :location #: location?
35
+ attr_reader :display_name #: String?
36
+ attr_reader :semantic_type #: String?
37
+ attr_reader :documentation #: String?
29
38
 
30
39
  # @rbs (id: Integer, name: String, kind: Symbol, ?reserved: bool, ?precedence: precedence?,
31
- # ?location: location?) -> void
32
- def initialize(id:, name:, kind:, reserved: false, precedence: nil, location: nil)
40
+ # ?location: location?, ?display_name: String?, ?semantic_type: String?, ?documentation: String?) -> void
41
+ def initialize(id:, name:, kind:, reserved: false, precedence: nil, location: nil, display_name: nil,
42
+ semantic_type: nil, documentation: nil)
33
43
  @id = id
34
44
  @name = name.freeze
35
45
  @kind = kind.to_sym
36
46
  @reserved = reserved
37
47
  @precedence = IR.deep_freeze(precedence)
38
48
  @location = IR.deep_freeze(location)
49
+ @display_name = display_name&.freeze
50
+ @semantic_type = semantic_type&.freeze
51
+ @documentation = documentation&.freeze
39
52
  freeze
40
53
  end
41
54
 
@@ -44,9 +57,14 @@ module Ibex
44
57
  # @rbs () -> bool
45
58
  def nonterminal? = @kind == :nonterminal
46
59
 
47
- # @rbs () -> Hash[Symbol, untyped]
60
+ # @rbs () -> Hash[Symbol, Object?]
48
61
  def to_h
49
- { id: @id, name: @name, kind: @kind, reserved: @reserved, prec: @precedence, loc: @location }
62
+ value = { id: @id, name: @name, kind: @kind, reserved: @reserved,
63
+ prec: @precedence, loc: @location } #: Hash[Symbol, Object?]
64
+ value[:display_name] = @display_name if @display_name
65
+ value[:semantic_type] = @semantic_type if @semantic_type
66
+ value[:doc] = @documentation
67
+ value
50
68
  end
51
69
  end
52
70
 
@@ -56,19 +74,25 @@ module Ibex
56
74
  attr_reader :location #: location
57
75
  attr_reader :named_refs #: Array[named_ref]
58
76
  attr_reader :context_length #: Integer
77
+ attr_reader :composition #: action_composition?
59
78
 
60
- # @rbs (code: String, location: location, ?named_refs: Array[named_ref], ?context_length: Integer) -> void
61
- def initialize(code:, location:, named_refs: [], context_length: 0)
79
+ # @rbs (code: String, location: location, ?named_refs: Array[named_ref], ?context_length: Integer,
80
+ # ?composition: action_composition?) -> void
81
+ def initialize(code:, location:, named_refs: [], context_length: 0, composition: nil)
62
82
  @code = code.freeze
63
83
  @location = IR.deep_freeze(location)
64
84
  @named_refs = IR.deep_freeze(named_refs)
65
85
  @context_length = context_length
86
+ @composition = IR.deep_freeze(composition)
66
87
  freeze
67
88
  end
68
89
 
69
- # @rbs () -> Hash[Symbol, untyped]
90
+ # @rbs () -> Hash[Symbol, Object?]
70
91
  def to_h
71
- { code: @code, loc: @location, named_refs: @named_refs, context_length: @context_length }
92
+ value = { code: @code, loc: @location, named_refs: @named_refs,
93
+ context_length: @context_length } #: Hash[Symbol, Object?]
94
+ value[:composition] = @composition
95
+ value
72
96
  end
73
97
  end
74
98
 
@@ -79,24 +103,36 @@ module Ibex
79
103
  attr_reader :rhs #: Array[Integer]
80
104
  attr_reader :action #: Action?
81
105
  attr_reader :precedence_override #: Integer?
82
- attr_reader :origin #: Hash[Symbol, untyped]
106
+ attr_reader :origin #: Hash[Symbol, Object?]
107
+ attr_reader :documentation #: String?
108
+ attr_reader :expansion #: production_expansion?
109
+ attr_reader :node #: node_annotation?
83
110
 
84
111
  # @rbs (id: Integer, lhs: Integer, rhs: Array[Integer], action: Action?, precedence_override: Integer?,
85
- # origin: Hash[Symbol, untyped]) -> void
86
- def initialize(id:, lhs:, rhs:, action:, precedence_override:, origin:)
112
+ # origin: Hash[Symbol, Object?], ?documentation: String?, ?expansion: production_expansion?,
113
+ # ?node: node_annotation?) -> void
114
+ def initialize(id:, lhs:, rhs:, action:, precedence_override:, origin:, documentation: nil, expansion: nil,
115
+ node: nil)
87
116
  @id = id
88
117
  @lhs = lhs
89
118
  @rhs = rhs.freeze
90
119
  @action = action
91
120
  @precedence_override = precedence_override
92
121
  @origin = IR.deep_freeze(origin)
122
+ @documentation = documentation&.freeze
123
+ @expansion = IR.deep_freeze(expansion)
124
+ @node = IR.deep_freeze(node)
93
125
  freeze
94
126
  end
95
127
 
96
- # @rbs () -> Hash[Symbol, untyped]
128
+ # @rbs () -> Hash[Symbol, Object?]
97
129
  def to_h
98
- { id: @id, lhs: @lhs, rhs: @rhs, action: @action&.to_h, prec_override: @precedence_override,
99
- origin: @origin }
130
+ value = { id: @id, lhs: @lhs, rhs: @rhs, action: @action&.to_h,
131
+ prec_override: @precedence_override, origin: @origin } #: Hash[Symbol, Object?]
132
+ value[:doc] = @documentation
133
+ value[:expansion] = @expansion
134
+ value[:node] = @node if @node
135
+ value
100
136
  end
101
137
  end
102
138
 
@@ -112,7 +148,7 @@ module Ibex
112
148
  freeze
113
149
  end
114
150
 
115
- # @rbs () -> Hash[Symbol, untyped]
151
+ # @rbs () -> Hash[Symbol, Object?]
116
152
  def to_h
117
153
  { code: @code, loc: @location }
118
154
  end
@@ -123,7 +159,15 @@ module Ibex
123
159
  attr_reader :class_name #: String
124
160
  attr_reader :superclass #: String?
125
161
  attr_reader :start #: String
162
+ attr_reader :starts #: Array[String]
163
+ attr_reader :mode #: grammar_mode
126
164
  attr_reader :expect #: Integer
165
+ attr_reader :expect_rr #: Integer?
166
+ attr_reader :parser_parameters #: Array[parser_parameter]
167
+ attr_reader :value_printers #: Array[value_printer]
168
+ attr_reader :grammar_tests #: Array[grammar_test]
169
+ attr_reader :lexer #: Lexer?
170
+ attr_reader :recovery #: recovery_policy
127
171
  attr_reader :options #: grammar_options
128
172
  attr_reader :symbols #: Array[GrammarSymbol]
129
173
  attr_reader :productions #: Array[Production]
@@ -132,17 +176,61 @@ module Ibex
132
176
  attr_reader :conversions #: Hash[String, String]
133
177
  attr_reader :warnings #: Array[grammar_warning]
134
178
  attr_reader :schema_version #: Integer
179
+ attr_reader :source_provenance #: source_provenance?
180
+ attr_reader :parser_contract #: ParserContract
135
181
 
136
- # @rbs (class_name: String, superclass: String?, start: String, expect: Integer, options: grammar_options,
182
+ # @rbs (class_name: String, superclass: String?, start: String, expect: Integer, ?expect_rr: Integer?,
183
+ # options: grammar_options,
137
184
  # symbols: Array[GrammarSymbol], productions: Array[Production], user_code: Hash[String, String],
138
185
  # conversions: Hash[String, String], warnings: Array[grammar_warning], ?user_code_chunks: user_code_chunks?,
139
- # ?schema_version: Integer) -> void
186
+ # ?parser_parameters: Array[parser_parameter],
187
+ # ?value_printers: Array[value_printer], ?grammar_tests: Array[grammar_test],
188
+ # ?recovery: recovery_policy?, ?lexer: Lexer?,
189
+ # ?mode: grammar_mode, ?starts: Array[String]?, ?parser_contract: ParserContract,
190
+ # ?source_provenance: source_provenance?) -> void
191
+ # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
192
+ # Immutable current IR is constructed from explicit public fields.
140
193
  def initialize(class_name:, superclass:, start:, expect:, options:, symbols:, productions:, user_code:,
141
- conversions:, warnings:, user_code_chunks: nil, schema_version: SCHEMA_VERSION)
194
+ conversions:, warnings:, user_code_chunks: nil,
195
+ source_provenance: nil, expect_rr: nil, parser_parameters: [], value_printers: [],
196
+ grammar_tests: [], recovery: nil, lexer: nil, mode: :default, starts: nil,
197
+ parser_contract: ParserContract.new)
198
+ initialize_current(
199
+ class_name: class_name, superclass: superclass, start: start, expect: expect, options: options,
200
+ symbols: symbols, productions: productions, user_code: user_code, conversions: conversions,
201
+ warnings: warnings, user_code_chunks: user_code_chunks,
202
+ source_provenance: source_provenance, expect_rr: expect_rr,
203
+ parser_parameters: parser_parameters, value_printers: value_printers, grammar_tests: grammar_tests,
204
+ recovery: recovery, lexer: lexer, mode: mode, starts: starts, parser_contract: parser_contract
205
+ )
206
+ end
207
+
208
+ # @rbs (class_name: String, superclass: String?, start: String, expect: Integer, options: grammar_options,
209
+ # symbols: Array[GrammarSymbol], productions: Array[Production], user_code: Hash[String, String],
210
+ # conversions: Hash[String, String], warnings: Array[grammar_warning], user_code_chunks: user_code_chunks?,
211
+ # source_provenance: source_provenance?, expect_rr: Integer?, parser_parameters: Array[parser_parameter],
212
+ # value_printers: Array[value_printer], grammar_tests: Array[grammar_test], recovery: recovery_policy?,
213
+ # lexer: Lexer?, mode: grammar_mode, starts: Array[String]?, parser_contract: ParserContract) -> void
214
+ def initialize_current(class_name:, superclass:, start:, expect:, options:, symbols:, productions:, user_code:,
215
+ conversions:, warnings:, user_code_chunks:, source_provenance:, expect_rr:,
216
+ parser_parameters:, value_printers:, grammar_tests:, recovery:, lexer:, mode:, starts:,
217
+ parser_contract:)
218
+ validate_mode(mode)
219
+ normalized_starts = validate_starts(start, starts, mode)
220
+ validate_current_metadata(parser_contract)
221
+
142
222
  @class_name = class_name.freeze
143
223
  @superclass = superclass&.freeze
144
224
  @start = start.freeze
225
+ @starts = normalized_starts.map(&:freeze).freeze
226
+ @mode = mode
145
227
  @expect = expect
228
+ @expect_rr = expect_rr
229
+ @parser_parameters = IR.deep_freeze(parser_parameters)
230
+ @value_printers = IR.deep_freeze(value_printers)
231
+ @grammar_tests = IR.deep_freeze(grammar_tests)
232
+ @lexer = lexer
233
+ @recovery = IR.deep_freeze(recovery || { sync_tokens: [], on_error_reduce: [] })
146
234
  @options = IR.deep_freeze(options)
147
235
  @symbols = symbols.freeze
148
236
  @productions = productions.freeze
@@ -151,11 +239,15 @@ module Ibex
151
239
  validate_user_code_chunks
152
240
  @conversions = IR.deep_freeze(conversions)
153
241
  @warnings = IR.deep_freeze(warnings)
154
- @schema_version = schema_version
242
+ @schema_version = SCHEMA_VERSION
243
+ @source_provenance = IR.deep_freeze(source_provenance)
244
+ @parser_contract = IR.deep_freeze(parser_contract)
155
245
  @symbols_by_name = @symbols.to_h { |symbol| [symbol.name, symbol] }.freeze
156
246
  @symbols_by_id = @symbols.to_h { |symbol| [symbol.id, symbol] }.freeze
157
247
  freeze
158
248
  end
249
+ # rubocop:enable Metrics/AbcSize, Metrics/ParameterLists
250
+ private :initialize_current
159
251
 
160
252
  # @rbs (String name) -> GrammarSymbol?
161
253
  def symbol(name) = @symbols_by_name[name]
@@ -166,19 +258,64 @@ module Ibex
166
258
  # @rbs () -> Array[GrammarSymbol]
167
259
  def nonterminals = @symbols.select(&:nonterminal?)
168
260
 
169
- # @rbs () -> Hash[Symbol, untyped]
261
+ # @rbs () -> Hash[Symbol, Object?]
170
262
  def to_h
171
263
  value = { ibex_ir: "grammar", schema_version: @schema_version, class_name: @class_name, superclass: @superclass,
172
- start: @start, expect: @expect, options: @options, symbols: @symbols.map(&:to_h),
173
- productions: @productions.map(&:to_h), user_code: @user_code, conversions: @conversions,
174
- warnings: @warnings } #: Hash[Symbol, untyped]
175
- value[:user_code_chunks] = @user_code_chunks.transform_values { |chunks| chunks.map(&:to_h) } \
176
- unless @user_code_chunks.empty?
264
+ start: @start, expect: @expect, options: @options,
265
+ symbols: @symbols.map(&:to_h),
266
+ productions: @productions.map(&:to_h),
267
+ user_code: @user_code, conversions: @conversions,
268
+ warnings: @warnings } #: Hash[Symbol, Object?]
269
+ append_optional_metadata(value)
177
270
  value
178
271
  end
179
272
 
180
273
  private
181
274
 
275
+ # @rbs (grammar_mode mode) -> void
276
+ def validate_mode(mode)
277
+ raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
278
+ end
279
+
280
+ # @rbs (String start, Array[String]? starts, grammar_mode mode) -> Array[String]
281
+ def validate_starts(start, starts, mode)
282
+ values = starts || [start]
283
+ raise ArgumentError, "starts must contain the primary start symbol" if values.empty?
284
+ raise ArgumentError, "start must be the first entry in starts" unless values.first == start
285
+ raise ArgumentError, "starts must be unique" unless values.uniq.length == values.length
286
+ raise ArgumentError, "multiple start symbols require extended mode" if values.length > 1 && mode != :extended
287
+
288
+ values
289
+ end
290
+
291
+ # @rbs (Hash[Symbol, Object?] value) -> void
292
+ def append_optional_metadata(value)
293
+ append_parser_metadata(value)
294
+ append_recovery_metadata(value)
295
+ value[:user_code_chunks] = @user_code_chunks.transform_values { |chunks| chunks.map(&:to_h) } \
296
+ unless @user_code_chunks.empty?
297
+ value[:source_provenance] = @source_provenance
298
+ value[:parser_contract] = @parser_contract.to_h
299
+ end
300
+
301
+ # @rbs (Hash[Symbol, Object?] value) -> void
302
+ def append_parser_metadata(value)
303
+ value[:expect_rr] = @expect_rr unless @expect_rr.nil?
304
+ value[:mode] = @mode if @mode == :extended
305
+ value[:starts] = @starts if @starts.length > 1
306
+ value[:params] = @parser_parameters unless @parser_parameters.empty?
307
+ value[:printers] = @value_printers unless @value_printers.empty?
308
+ value[:tests] = @grammar_tests unless @grammar_tests.empty?
309
+ value[:lexer] = @lexer.to_h if @lexer
310
+ end
311
+
312
+ # @rbs (Hash[Symbol, Object?] value) -> void
313
+ def append_recovery_metadata(value)
314
+ return if @recovery[:sync_tokens].empty? && @recovery[:on_error_reduce].empty?
315
+
316
+ value[:recovery] = @recovery
317
+ end
318
+
182
319
  # @rbs () -> void
183
320
  def validate_user_code_chunks
184
321
  @user_code_chunks.each do |name, chunks|
@@ -187,6 +324,13 @@ module Ibex
187
324
  raise Ibex::Error, "(ir):1:1: user-code chunks do not match the concatenated #{name} section"
188
325
  end
189
326
  end
327
+
328
+ # @rbs (ParserContract parser_contract) -> void
329
+ def validate_current_metadata(parser_contract)
330
+ return if parser_contract.is_a?(ParserContract)
331
+
332
+ raise ArgumentError, "parser_contract must be a ParserContract"
333
+ end
190
334
  end
191
335
  end
192
336
  end