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
@@ -6,7 +6,11 @@ require "json"
6
6
 
7
7
  module Ibex
8
8
  # Deterministic description of one published parser generation.
9
+ # rubocop:disable Metrics/ModuleLength -- manifest schema validation is kept in one auditable unit.
9
10
  module GenerationManifest
11
+ # @rbs!
12
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
13
+
10
14
  SCHEMA_VERSION = 1 #: Integer
11
15
  IDENTIFIER = "generation" #: String
12
16
  ROOT_KEYS = %w[ibex_manifest schema_version input options artifacts].freeze #: Array[String]
@@ -16,7 +20,7 @@ module Ibex
16
20
 
17
21
  module_function
18
22
 
19
- # @rbs (ArtifactSet artifacts, source_records: Array[GenerationInput], options: Hash[String, untyped]) -> String
23
+ # @rbs (ArtifactSet artifacts, source_records: Array[GenerationInput], options: Hash[String, Object?]) -> String
20
24
  def render(artifacts, source_records:, options:)
21
25
  files = source_records.map(&:to_h)
22
26
  document = {
@@ -34,24 +38,26 @@ module Ibex
34
38
  end
35
39
 
36
40
  # Validate the manifest shape and, by default, every published artifact digest.
37
- # @rbs (String source, ?verify_artifacts: bool) -> Hash[String, untyped]
41
+ # @rbs (String source, ?verify_artifacts: bool) -> Hash[String, json_value]
38
42
  def validate(source, verify_artifacts: true)
39
- document = JSON.parse(source)
43
+ document = JSON.parse(source) #: Object?
40
44
  validate_document(document)
41
- verify_entries(document.fetch("artifacts")) if verify_artifacts
42
- document
45
+ document_hash = document #: Hash[String, json_value]
46
+ artifacts = document_hash.fetch("artifacts") #: Array[Object?]
47
+ verify_entries(artifacts) if verify_artifacts
48
+ document_hash
43
49
  rescue JSON::ParserError, KeyError, TypeError, ArgumentError => e
44
50
  raise Ibex::Error, "(manifest):1:1: invalid generation manifest: #{e.message}"
45
51
  end
46
52
 
47
- # @rbs (String path, ?verify_artifacts: bool) -> Hash[String, untyped]
53
+ # @rbs (String path, ?verify_artifacts: bool) -> Hash[String, json_value]
48
54
  def validate_file(path, verify_artifacts: true)
49
55
  validate(File.binread(path), verify_artifacts: verify_artifacts)
50
56
  rescue SystemCallError, ArgumentError => e
51
57
  raise Ibex::Error, "#{path}:1:1: cannot read generation manifest: #{e.message}"
52
58
  end
53
59
 
54
- # @rbs (Artifact artifact) -> Hash[String, untyped]
60
+ # @rbs (Artifact artifact) -> Hash[String, json_value]
55
61
  def artifact_entry(artifact)
56
62
  {
57
63
  "kind" => artifact.kind.to_s,
@@ -62,19 +68,20 @@ module Ibex
62
68
  end
63
69
  private_class_method :artifact_entry
64
70
 
65
- # @rbs (Array[Hash[String, untyped]] files) -> String
71
+ # @rbs (Array[Hash[String, json_value]] files) -> String
66
72
  def input_digest(files)
67
73
  digest = Digest::SHA256.new
68
74
  files.each do |entry|
69
- digest << entry.fetch("path") << "\0"
70
- digest << entry.fetch("sha256") << "\0"
71
- digest << entry.fetch("bytesize").to_s << "\0"
75
+ path = entry.fetch("path") # @type var path: String
76
+ sha256 = entry.fetch("sha256") # @type var sha256: String
77
+ bytesize = entry.fetch("bytesize") # @type var bytesize: Integer
78
+ digest << path << "\0" << sha256 << "\0" << bytesize.to_s << "\0"
72
79
  end
73
80
  digest.hexdigest
74
81
  end
75
82
  private_class_method :input_digest
76
83
 
77
- # @rbs (Hash[String, untyped] value) -> Hash[String, untyped]
84
+ # @rbs (Hash[String, Object?] value) -> Hash[String, Object?]
78
85
  def sorted_hash(value)
79
86
  value.keys.sort.to_h do |key|
80
87
  child = value.fetch(key)
@@ -84,7 +91,7 @@ module Ibex
84
91
  end
85
92
  private_class_method :sorted_hash
86
93
 
87
- # @rbs (untyped document) -> void
94
+ # @rbs (Object? document) -> void
88
95
  def validate_document(document)
89
96
  raise TypeError, "document must be an object" unless document.is_a?(Hash)
90
97
 
@@ -100,14 +107,14 @@ module Ibex
100
107
  end
101
108
  private_class_method :validate_document
102
109
 
103
- # @rbs (Hash[untyped, untyped] document) -> void
110
+ # @rbs (Hash[Object?, Object?] document) -> void
104
111
  def validate_identity(document)
105
112
  raise TypeError, "unsupported manifest identifier" unless document["ibex_manifest"] == IDENTIFIER
106
113
  raise TypeError, "unsupported schema_version" unless document["schema_version"] == SCHEMA_VERSION
107
114
  end
108
115
  private_class_method :validate_identity
109
116
 
110
- # @rbs (untyped input) -> void
117
+ # @rbs (Object? input) -> void
111
118
  def validate_input(input)
112
119
  raise TypeError, "input must be an object" unless input.is_a?(Hash)
113
120
 
@@ -125,13 +132,13 @@ module Ibex
125
132
  end
126
133
  private_class_method :validate_input
127
134
 
128
- # @rbs (Array[untyped] entries, String label, ?require_kind: bool) -> void
135
+ # @rbs (Array[Object?] entries, String label, ?require_kind: bool) -> void
129
136
  def validate_entries(entries, label, require_kind: false)
130
137
  entries.each { |entry| validate_entry(entry, label, require_kind) }
131
138
  end
132
139
  private_class_method :validate_entries
133
140
 
134
- # @rbs (untyped entry, String label, bool require_kind) -> void
141
+ # @rbs (Object? entry, String label, bool require_kind) -> void
135
142
  def validate_entry(entry, label, require_kind)
136
143
  raise TypeError, "#{label} must be an object" unless entry.is_a?(Hash)
137
144
 
@@ -146,7 +153,7 @@ module Ibex
146
153
  end
147
154
  private_class_method :validate_entry
148
155
 
149
- # @rbs (untyped path, String label) -> void
156
+ # @rbs (Object? path, String label) -> void
150
157
  def validate_path(path, label)
151
158
  raise TypeError, "#{label} path must be a non-empty string" unless path.is_a?(String) && !path.empty?
152
159
  return unless path.include?("\0") || path.match?(/[[:cntrl:]]/)
@@ -155,7 +162,7 @@ module Ibex
155
162
  end
156
163
  private_class_method :validate_path
157
164
 
158
- # @rbs (untyped kind, String label) -> void
165
+ # @rbs (Object? kind, String label) -> void
159
166
  def validate_kind(kind, label)
160
167
  return if kind.is_a?(String) && !kind.empty?
161
168
 
@@ -163,7 +170,7 @@ module Ibex
163
170
  end
164
171
  private_class_method :validate_kind
165
172
 
166
- # @rbs (untyped digest, String label) -> void
173
+ # @rbs (Object? digest, String label) -> void
167
174
  def validate_digest(digest, label)
168
175
  return if digest.is_a?(String) && digest.match?(/\A[0-9a-f]{64}\z/)
169
176
 
@@ -171,7 +178,7 @@ module Ibex
171
178
  end
172
179
  private_class_method :validate_digest
173
180
 
174
- # @rbs (Hash[untyped, untyped] value, Array[String] expected, String label) -> void
181
+ # @rbs (Hash[Object?, Object?] value, Array[String] expected, String label) -> void
175
182
  def exact_keys!(value, expected, label)
176
183
  keys = value.keys
177
184
  raise TypeError, "#{label} keys must be strings" unless keys.all?(String)
@@ -183,18 +190,22 @@ module Ibex
183
190
  end
184
191
  private_class_method :exact_keys!
185
192
 
186
- # @rbs (Array[untyped] entries) -> void
193
+ # @rbs (Array[Object?] entries) -> void
187
194
  def verify_entries(entries)
188
195
  entries.each do |entry|
189
- path = entry.fetch("path")
196
+ entry_hash = entry #: Hash[String, json_value]
197
+ path = entry_hash.fetch("path") #: String
190
198
  source = File.binread(path)
191
- raise TypeError, "artifact bytesize mismatch for #{path}" unless source.bytesize == entry.fetch("bytesize")
199
+ bytesize = entry_hash.fetch("bytesize") #: Integer
200
+ sha256 = entry_hash.fetch("sha256") #: String
201
+ raise TypeError, "artifact bytesize mismatch for #{path}" unless source.bytesize == bytesize
192
202
  raise TypeError, "artifact digest mismatch for #{path}" unless
193
- Digest::SHA256.hexdigest(source) == entry.fetch("sha256")
203
+ Digest::SHA256.hexdigest(source) == sha256
194
204
  end
195
205
  rescue SystemCallError => e
196
206
  raise TypeError, "cannot read artifact: #{e.message}"
197
207
  end
198
208
  private_class_method :verify_entries
199
209
  end
210
+ # rubocop:enable Metrics/ModuleLength
200
211
  end
@@ -37,7 +37,7 @@ module Ibex
37
37
  @lock_sleeper = lock_sleeper
38
38
  @locks = [] #: Array[File]
39
39
  @locked_paths = [] #: Array[String]
40
- @records = [] #: Array[Hash[Symbol, untyped]]
40
+ @records = [] #: Array[Hash[Symbol, Object?]]
41
41
  @committed = false
42
42
  @temporary_sequence = 0
43
43
  end
@@ -57,14 +57,14 @@ module Ibex
57
57
  failures
58
58
  end
59
59
 
60
- # @rbs (Hash[Symbol, untyped] record) -> void
60
+ # @rbs (Hash[Symbol, Object?] record) -> void
61
61
  def rollback_record(record)
62
- target = record.fetch(:target)
62
+ target = record.fetch(:target) #: String
63
63
  if record.fetch(:backed_up)
64
64
  if record.fetch(:installed)
65
- File.rename(record.fetch(:backup), target)
65
+ File.rename(record.fetch(:backup).to_s, target)
66
66
  else
67
- File.unlink(record.fetch(:backup))
67
+ File.unlink(record.fetch(:backup).to_s)
68
68
  end
69
69
  record[:backup] = nil
70
70
  record[:backed_up] = false
@@ -170,9 +170,9 @@ module Ibex
170
170
  "(generation):1:1: cannot revalidate artifact target #{phase}: #{e.message}"
171
171
  end
172
172
 
173
- # @rbs (Hash[Symbol, untyped] record, Array[String] sources, String phase) -> void
173
+ # @rbs (Hash[Symbol, Object?] record, Array[String] sources, String phase) -> void
174
174
  def validate_current_target!(record, sources, phase)
175
- artifact = record.fetch(:artifact)
175
+ artifact = record.fetch(:artifact) #: Artifact
176
176
  current = resolved_target(artifact.path, validate_links: false)
177
177
  unless current == record.fetch(:target)
178
178
  raise GenerationTransaction::SourceChanged,
@@ -182,7 +182,7 @@ module Ibex
182
182
  raise Ibex::Error, "(test):1:1: invalid grammar test process result: #{e.message}"
183
183
  end
184
184
 
185
- # @rbs (IR::grammar_test test, untyped document) -> Result
185
+ # @rbs (IR::grammar_test test, Object? document) -> Result
186
186
  def build_result(test, document)
187
187
  unless document.is_a?(Hash) && %w[accept reject error].include?(document["actual"])
188
188
  raise Ibex::Error, "(test):1:1: grammar test process returned an invalid case result"
@@ -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}")
@@ -77,42 +77,55 @@ module Ibex
77
77
  attr_reader :entry_states #: Hash[String, Integer]
78
78
  attr_reader :conflict_summary #: conflict_summary
79
79
  attr_reader :schema_version #: Integer
80
+ attr_reader :entry_construction #: String
80
81
 
81
82
  # @rbs (grammar: Grammar, states: Array[AutomatonState], conflict_summary: conflict_summary,
82
- # ?algorithm: String, ?grammar_digest: String?, ?schema_version: Integer,
83
- # ?entry_states: Hash[String, Integer]?) -> void
83
+ # ?algorithm: String, ?grammar_digest: String?, ?entry_states: Hash[String, Integer]?,
84
+ # ?entry_construction: String) -> void
84
85
  def initialize(grammar:, states:, conflict_summary:, algorithm: "lalr1", grammar_digest: nil,
85
- schema_version: SCHEMA_VERSION, entry_states: nil)
86
- unless SUPPORTED_SCHEMA_VERSIONS.include?(schema_version)
87
- raise Ibex::Error, "unsupported automaton schema_version #{schema_version.inspect}"
88
- end
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
89
92
 
90
- grammar = Migration.to_v2(grammar) if schema_version >= 2 && grammar.schema_version != schema_version
91
- raise Ibex::Error, "automaton migration did not produce a grammar" unless grammar.is_a?(Grammar)
92
- unless grammar.schema_version == schema_version
93
- raise Ibex::Error,
94
- "automaton schema_version #{schema_version} requires Grammar IR v#{schema_version}, " \
95
- "got v#{grammar.schema_version}"
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"
96
100
  end
97
101
 
98
102
  @algorithm = algorithm.freeze
99
103
  @grammar = grammar
100
- @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
101
110
  @states = states.freeze
102
111
  @entry_states = IR.deep_freeze(entry_states || { grammar.start => 0 })
103
112
  validate_entry_states
104
113
  @conflict_summary = IR.deep_freeze(conflict_summary)
105
- @schema_version = schema_version
114
+ @schema_version = SCHEMA_VERSION
115
+ @entry_construction = validate_entry_construction(entry_construction)
116
+ validate_parser_contract
106
117
  freeze
107
118
  end
119
+ private :initialize_current
108
120
 
109
- # @rbs () -> Hash[Symbol, untyped]
121
+ # @rbs () -> Hash[Symbol, Object?]
110
122
  def to_h
111
123
  value = { ibex_ir: "automaton", schema_version: @schema_version, algorithm: @algorithm,
112
124
  grammar_digest: @grammar_digest, grammar: @grammar.to_h,
113
125
  states: @states.map { |state| state.to_h(@grammar) },
114
- conflict_summary: @conflict_summary } #: Hash[Symbol, untyped]
126
+ conflict_summary: @conflict_summary } #: Hash[Symbol, Object?]
115
127
  value[:entry_states] = @entry_states unless @entry_states == { @grammar.start => 0 }
128
+ value[:entry_construction] = @entry_construction
116
129
  value
117
130
  end
118
131
 
@@ -137,6 +150,35 @@ module Ibex
137
150
  require "digest"
138
151
  "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(grammar))}"
139
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
140
182
  end
141
183
  end
142
184
  end
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "parser_contract"
4
+
3
5
  module Ibex
4
6
  module IR
5
- SCHEMA_VERSION = 2
6
- SUPPORTED_SCHEMA_VERSIONS = [1, 2].freeze #: Array[Integer]
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.
10
+ SCHEMA_VERSION = 1
11
+ SUPPORTED_SCHEMA_VERSIONS = [SCHEMA_VERSION].freeze #: Array[Integer]
7
12
 
8
- # @rbs (untyped value) -> untyped
13
+ # @rbs (Object? value) -> Object?
9
14
  def deep_freeze(value)
10
15
  case value
11
16
  when Array then value.each { |item| deep_freeze(item) }
@@ -52,13 +57,13 @@ module Ibex
52
57
  # @rbs () -> bool
53
58
  def nonterminal? = @kind == :nonterminal
54
59
 
55
- # @rbs (?schema_version: Integer) -> Hash[Symbol, untyped]
56
- def to_h(schema_version: SCHEMA_VERSION)
60
+ # @rbs () -> Hash[Symbol, Object?]
61
+ def to_h
57
62
  value = { id: @id, name: @name, kind: @kind, reserved: @reserved,
58
- prec: @precedence, loc: @location } #: Hash[Symbol, untyped]
63
+ prec: @precedence, loc: @location } #: Hash[Symbol, Object?]
59
64
  value[:display_name] = @display_name if @display_name
60
65
  value[:semantic_type] = @semantic_type if @semantic_type
61
- value[:doc] = @documentation if schema_version >= 2
66
+ value[:doc] = @documentation
62
67
  value
63
68
  end
64
69
  end
@@ -82,11 +87,11 @@ module Ibex
82
87
  freeze
83
88
  end
84
89
 
85
- # @rbs (?schema_version: Integer) -> Hash[Symbol, untyped]
86
- def to_h(schema_version: SCHEMA_VERSION)
90
+ # @rbs () -> Hash[Symbol, Object?]
91
+ def to_h
87
92
  value = { code: @code, loc: @location, named_refs: @named_refs,
88
- context_length: @context_length } #: Hash[Symbol, untyped]
89
- value[:composition] = @composition if schema_version >= 2
93
+ context_length: @context_length } #: Hash[Symbol, Object?]
94
+ value[:composition] = @composition
90
95
  value
91
96
  end
92
97
  end
@@ -98,13 +103,13 @@ module Ibex
98
103
  attr_reader :rhs #: Array[Integer]
99
104
  attr_reader :action #: Action?
100
105
  attr_reader :precedence_override #: Integer?
101
- attr_reader :origin #: Hash[Symbol, untyped]
106
+ attr_reader :origin #: Hash[Symbol, Object?]
102
107
  attr_reader :documentation #: String?
103
108
  attr_reader :expansion #: production_expansion?
104
109
  attr_reader :node #: node_annotation?
105
110
 
106
111
  # @rbs (id: Integer, lhs: Integer, rhs: Array[Integer], action: Action?, precedence_override: Integer?,
107
- # origin: Hash[Symbol, untyped], ?documentation: String?, ?expansion: production_expansion?,
112
+ # origin: Hash[Symbol, Object?], ?documentation: String?, ?expansion: production_expansion?,
108
113
  # ?node: node_annotation?) -> void
109
114
  def initialize(id:, lhs:, rhs:, action:, precedence_override:, origin:, documentation: nil, expansion: nil,
110
115
  node: nil)
@@ -120,15 +125,13 @@ module Ibex
120
125
  freeze
121
126
  end
122
127
 
123
- # @rbs (?schema_version: Integer) -> Hash[Symbol, untyped]
124
- def to_h(schema_version: SCHEMA_VERSION)
125
- value = { id: @id, lhs: @lhs, rhs: @rhs, action: @action&.to_h(schema_version: schema_version),
126
- prec_override: @precedence_override, origin: @origin } #: Hash[Symbol, untyped]
127
- if schema_version >= 2
128
- value[:doc] = @documentation
129
- value[:expansion] = @expansion
130
- value[:node] = @node if @node
131
- end
128
+ # @rbs () -> Hash[Symbol, Object?]
129
+ def to_h
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
132
135
  value
133
136
  end
134
137
  end
@@ -145,7 +148,7 @@ module Ibex
145
148
  freeze
146
149
  end
147
150
 
148
- # @rbs () -> Hash[Symbol, untyped]
151
+ # @rbs () -> Hash[Symbol, Object?]
149
152
  def to_h
150
153
  { code: @code, loc: @location }
151
154
  end
@@ -174,25 +177,47 @@ module Ibex
174
177
  attr_reader :warnings #: Array[grammar_warning]
175
178
  attr_reader :schema_version #: Integer
176
179
  attr_reader :source_provenance #: source_provenance?
177
- attr_reader :migration #: migration_metadata?
180
+ attr_reader :parser_contract #: ParserContract
178
181
 
179
182
  # @rbs (class_name: String, superclass: String?, start: String, expect: Integer, ?expect_rr: Integer?,
180
183
  # options: grammar_options,
181
184
  # symbols: Array[GrammarSymbol], productions: Array[Production], user_code: Hash[String, String],
182
185
  # conversions: Hash[String, String], warnings: Array[grammar_warning], ?user_code_chunks: user_code_chunks?,
183
- # ?schema_version: Integer, ?source_provenance: source_provenance?,
184
- # ?migration: migration_metadata?, ?parser_parameters: Array[parser_parameter],
186
+ # ?parser_parameters: Array[parser_parameter],
185
187
  # ?value_printers: Array[value_printer], ?grammar_tests: Array[grammar_test],
186
188
  # ?recovery: recovery_policy?, ?lexer: Lexer?,
187
- # ?mode: grammar_mode, ?starts: Array[String]?) -> void
189
+ # ?mode: grammar_mode, ?starts: Array[String]?, ?parser_contract: ParserContract,
190
+ # ?source_provenance: source_provenance?) -> void
188
191
  # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
189
- # Immutable versioned IR is constructed from explicit public fields.
192
+ # Immutable current IR is constructed from explicit public fields.
190
193
  def initialize(class_name:, superclass:, start:, expect:, options:, symbols:, productions:, user_code:,
191
- conversions:, warnings:, user_code_chunks: nil, schema_version: SCHEMA_VERSION,
192
- source_provenance: nil, migration: nil, expect_rr: nil, parser_parameters: [], value_printers: [],
193
- grammar_tests: [], recovery: nil, lexer: nil, mode: :default, starts: nil)
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:)
194
218
  validate_mode(mode)
195
219
  normalized_starts = validate_starts(start, starts, mode)
220
+ validate_current_metadata(parser_contract)
196
221
 
197
222
  @class_name = class_name.freeze
198
223
  @superclass = superclass&.freeze
@@ -214,14 +239,15 @@ module Ibex
214
239
  validate_user_code_chunks
215
240
  @conversions = IR.deep_freeze(conversions)
216
241
  @warnings = IR.deep_freeze(warnings)
217
- @schema_version = schema_version
242
+ @schema_version = SCHEMA_VERSION
218
243
  @source_provenance = IR.deep_freeze(source_provenance)
219
- @migration = IR.deep_freeze(migration)
244
+ @parser_contract = IR.deep_freeze(parser_contract)
220
245
  @symbols_by_name = @symbols.to_h { |symbol| [symbol.name, symbol] }.freeze
221
246
  @symbols_by_id = @symbols.to_h { |symbol| [symbol.id, symbol] }.freeze
222
247
  freeze
223
248
  end
224
249
  # rubocop:enable Metrics/AbcSize, Metrics/ParameterLists
250
+ private :initialize_current
225
251
 
226
252
  # @rbs (String name) -> GrammarSymbol?
227
253
  def symbol(name) = @symbols_by_name[name]
@@ -232,14 +258,14 @@ module Ibex
232
258
  # @rbs () -> Array[GrammarSymbol]
233
259
  def nonterminals = @symbols.select(&:nonterminal?)
234
260
 
235
- # @rbs () -> Hash[Symbol, untyped]
261
+ # @rbs () -> Hash[Symbol, Object?]
236
262
  def to_h
237
263
  value = { ibex_ir: "grammar", schema_version: @schema_version, class_name: @class_name, superclass: @superclass,
238
264
  start: @start, expect: @expect, options: @options,
239
- symbols: @symbols.map { |symbol| symbol.to_h(schema_version: @schema_version) },
240
- productions: @productions.map { |production| production.to_h(schema_version: @schema_version) },
265
+ symbols: @symbols.map(&:to_h),
266
+ productions: @productions.map(&:to_h),
241
267
  user_code: @user_code, conversions: @conversions,
242
- warnings: @warnings } #: Hash[Symbol, untyped]
268
+ warnings: @warnings } #: Hash[Symbol, Object?]
243
269
  append_optional_metadata(value)
244
270
  value
245
271
  end
@@ -262,30 +288,28 @@ module Ibex
262
288
  values
263
289
  end
264
290
 
265
- # @rbs (Hash[Symbol, untyped] value) -> void
291
+ # @rbs (Hash[Symbol, Object?] value) -> void
266
292
  def append_optional_metadata(value)
267
293
  append_parser_metadata(value)
268
294
  append_recovery_metadata(value)
269
295
  value[:user_code_chunks] = @user_code_chunks.transform_values { |chunks| chunks.map(&:to_h) } \
270
296
  unless @user_code_chunks.empty?
271
- return unless @schema_version >= 2
272
-
273
297
  value[:source_provenance] = @source_provenance
274
- value[:migration] = @migration
298
+ value[:parser_contract] = @parser_contract.to_h
275
299
  end
276
300
 
277
- # @rbs (Hash[Symbol, untyped] value) -> void
301
+ # @rbs (Hash[Symbol, Object?] value) -> void
278
302
  def append_parser_metadata(value)
279
303
  value[:expect_rr] = @expect_rr unless @expect_rr.nil?
280
304
  value[:mode] = @mode if @mode == :extended
281
305
  value[:starts] = @starts if @starts.length > 1
282
306
  value[:params] = @parser_parameters unless @parser_parameters.empty?
283
307
  value[:printers] = @value_printers unless @value_printers.empty?
284
- value[:tests] = @grammar_tests unless @grammar_tests.empty? || @schema_version < 2
285
- value[:lexer] = @lexer.to_h if @lexer && @schema_version >= 2
308
+ value[:tests] = @grammar_tests unless @grammar_tests.empty?
309
+ value[:lexer] = @lexer.to_h if @lexer
286
310
  end
287
311
 
288
- # @rbs (Hash[Symbol, untyped] value) -> void
312
+ # @rbs (Hash[Symbol, Object?] value) -> void
289
313
  def append_recovery_metadata(value)
290
314
  return if @recovery[:sync_tokens].empty? && @recovery[:on_error_reduce].empty?
291
315
 
@@ -300,6 +324,13 @@ module Ibex
300
324
  raise Ibex::Error, "(ir):1:1: user-code chunks do not match the concatenated #{name} section"
301
325
  end
302
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
303
334
  end
304
335
  end
305
336
  end
@@ -32,7 +32,7 @@ module Ibex
32
32
  freeze
33
33
  end
34
34
 
35
- # @rbs () -> Hash[Symbol, untyped]
35
+ # @rbs () -> Hash[Symbol, lexer_rule_document_value]
36
36
  def to_h
37
37
  {
38
38
  id: @id, state: @state, kind: @kind, token: @token, pattern: @pattern,
@@ -41,15 +41,15 @@ module Ibex
41
41
  end
42
42
  end
43
43
 
44
- # Independently versioned lexer contract embedded by Grammar IR v2.
44
+ # Independently versioned lexer contract embedded by the current Grammar IR.
45
45
  class Lexer
46
46
  attr_reader :states #: Array[String]
47
47
  attr_reader :rules #: Array[LexerRule]
48
- attr_reader :warnings #: Array[Hash[Symbol, untyped]]
48
+ attr_reader :warnings #: Array[lexer_warning]
49
49
  attr_reader :schema_version #: Integer
50
50
  attr_reader :source_provenance #: source_provenance?
51
51
 
52
- # @rbs (states: Array[String], rules: Array[LexerRule], warnings: Array[Hash[Symbol, untyped]],
52
+ # @rbs (states: Array[String], rules: Array[LexerRule], warnings: Array[lexer_warning],
53
53
  # ?schema_version: Integer, ?source_provenance: source_provenance?) -> void
54
54
  def initialize(states:, rules:, warnings:, schema_version: LEXER_SCHEMA_VERSION, source_provenance: nil)
55
55
  raise ArgumentError, "lexer states must start with INITIAL" unless states.first == "INITIAL"
@@ -63,7 +63,7 @@ module Ibex
63
63
  freeze
64
64
  end
65
65
 
66
- # @rbs () -> Hash[Symbol, untyped]
66
+ # @rbs () -> Hash[Symbol, lexer_document_value]
67
67
  def to_h
68
68
  {
69
69
  ibex_ir: "lexer", schema_version: @schema_version, initial_state: "INITIAL",