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,634 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module IR
5
+ module Validator
6
+ # Structural and referential validation for a current Grammar IR JSON object.
7
+ # rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.
8
+ class GrammarDocument < Base
9
+ ROOT_REQUIRED = %w[
10
+ ibex_ir schema_version class_name superclass start expect options symbols productions user_code
11
+ conversions warnings source_provenance parser_contract
12
+ ].freeze #: Array[String]
13
+ ROOT_OPTIONAL = %w[
14
+ user_code_chunks expect_rr params printers tests recovery lexer mode starts
15
+ ].freeze #: Array[String]
16
+ SYMBOL_REQUIRED = %w[id name kind reserved prec loc].freeze #: Array[String]
17
+ SYMBOL_OPTIONAL = %w[display_name semantic_type].freeze #: Array[String]
18
+ SYMBOL_METADATA_REQUIRED = %w[doc].freeze #: Array[String]
19
+ PRODUCTION_REQUIRED = %w[id lhs rhs action prec_override origin].freeze #: Array[String]
20
+ PRODUCTION_METADATA_REQUIRED = %w[doc expansion].freeze #: Array[String]
21
+ ACTION_COMPOSITION_REQUIRED = %w[composition].freeze #: Array[String]
22
+ ORIGIN_KINDS = %w[
23
+ optional_expansion star_expansion plus_expansion separated_list_expansion group_expansion
24
+ ].freeze #: Array[String]
25
+ RUBY_KEYWORDS = %w[
26
+ __ENCODING__ __FILE__ __LINE__ alias and begin break case class def defined do else elsif end ensure false for
27
+ if in module next nil not or redo rescue retry return self super then true undef unless until when while yield
28
+ ].freeze #: Array[String]
29
+
30
+ attr_reader :symbols_by_id #: Hash[Integer, json_object]
31
+ attr_reader :symbols_by_name #: Hash[String, json_object]
32
+ attr_reader :productions_by_id #: Hash[Integer, json_object]
33
+
34
+ # @rbs @data: json_object
35
+ # @rbs @path: String
36
+ # @rbs @version: Integer
37
+
38
+ # @rbs (json_object data, ?path: String, ?version: Integer) -> void
39
+ def initialize(data, path: "$", version: data.fetch("schema_version"))
40
+ super()
41
+ @data = data
42
+ @path = path
43
+ @version = version
44
+ @symbols_by_id = {}
45
+ @symbols_by_name = {}
46
+ @productions_by_id = {}
47
+ end
48
+
49
+ # @rbs () -> self
50
+ def validate
51
+ validate_root_record
52
+ validate_envelope
53
+ validate_header
54
+ validate_options
55
+ validate_parser_parameters_if_present
56
+ validate_symbols
57
+ validate_optional_parser_metadata
58
+ validate_reserved_symbols
59
+ validate_start
60
+ validate_productions
61
+ validate_string_map(@data["user_code"], "#{@path}.user_code")
62
+ validate_string_map(@data["conversions"], "#{@path}.conversions")
63
+ validate_warnings
64
+ validate_optional_source_metadata
65
+ self
66
+ end
67
+
68
+ private
69
+
70
+ # @rbs () -> void
71
+ def validate_root_record
72
+ record(@data, @path, ROOT_REQUIRED, ROOT_OPTIONAL)
73
+ end
74
+
75
+ # @rbs () -> void
76
+ def validate_optional_parser_metadata
77
+ validate_value_printers if @data.key?("printers")
78
+ validate_grammar_tests if @data.key?("tests")
79
+ validate_lexer if @data.key?("lexer")
80
+ validate_recovery if @data.key?("recovery")
81
+ validate_parser_contract
82
+ end
83
+
84
+ # @rbs () -> void
85
+ def validate_parser_parameters_if_present
86
+ validate_parser_parameters if @data.key?("params")
87
+ end
88
+
89
+ # @rbs () -> void
90
+ def validate_optional_source_metadata
91
+ validate_user_code_chunks if @data.key?("user_code_chunks")
92
+ validate_source_provenance(@data["source_provenance"], "#{@path}.source_provenance")
93
+ end
94
+
95
+ # @rbs () -> void
96
+ def validate_envelope
97
+ literal(@data["ibex_ir"], "#{@path}.ibex_ir", "grammar")
98
+ literal(@data["schema_version"], "#{@path}.schema_version", @version)
99
+ end
100
+
101
+ # @rbs () -> void
102
+ def validate_header
103
+ nonempty_string(@data["class_name"], "#{@path}.class_name")
104
+ nullable_string(@data["superclass"], "#{@path}.superclass")
105
+ nonempty_string(@data["start"], "#{@path}.start")
106
+ enum(@data["mode"], "#{@path}.mode", %w[default extended]) if @data.key?("mode")
107
+ nonnegative_integer(@data["expect"], "#{@path}.expect")
108
+ nonnegative_integer(@data["expect_rr"], "#{@path}.expect_rr") if @data.key?("expect_rr")
109
+ end
110
+
111
+ # @rbs () -> void
112
+ def validate_options
113
+ path = "#{@path}.options"
114
+ options = record(@data["options"], path, %w[result_var omit_action_call], %w[cst])
115
+ boolean(options["result_var"], "#{path}.result_var")
116
+ boolean(options["omit_action_call"], "#{path}.omit_action_call")
117
+ return unless options.key?("cst")
118
+
119
+ literal(options["cst"], "#{path}.cst", true)
120
+ invalid("#{@path}.mode", "must be extended when CST construction is enabled") unless
121
+ @data["mode"] == "extended"
122
+ end
123
+
124
+ # @rbs () -> void
125
+ def validate_parser_parameters
126
+ names = {} #: Hash[String, bool]
127
+ array(@data["params"], "#{@path}.params").each_with_index do |value, index|
128
+ path = "#{@path}.params[#{index}]"
129
+ parameter = record(value, path, %w[name semantic_type])
130
+ name = nonempty_string(parameter["name"], "#{path}.name")
131
+ invalid("#{path}.name", "must be a Ruby local identifier") unless name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
132
+ invalid("#{path}.name", "must not be a Ruby keyword") if RUBY_KEYWORDS.include?(name)
133
+ invalid("#{path}.name", "duplicates parameter #{name.inspect}") if names.key?(name)
134
+ names[name] = true
135
+ metadata(parameter["semantic_type"], "#{path}.semantic_type")
136
+ end
137
+ end
138
+
139
+ # @rbs () -> void
140
+ def validate_value_printers
141
+ names = {} #: Hash[String, bool]
142
+ array(@data["printers"], "#{@path}.printers").each_with_index do |value, index|
143
+ path = "#{@path}.printers[#{index}]"
144
+ printer = record(value, path, %w[symbol code loc])
145
+ name = nonempty_string(printer["symbol"], "#{path}.symbol")
146
+ invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
147
+ invalid("#{path}.symbol", "duplicates printer for #{name.inspect}") if names.key?(name)
148
+ names[name] = true
149
+ string(printer["code"], "#{path}.code")
150
+ location(printer["loc"], "#{path}.loc", nullable: false)
151
+ end
152
+ end
153
+
154
+ # @rbs () -> void
155
+ def validate_grammar_tests
156
+ invalid("#{@path}.mode", "must be extended for grammar tests") unless @data["mode"] == "extended"
157
+ seen = {} #: Hash[[String, String], bool]
158
+ array(@data["tests"], "#{@path}.tests").each_with_index do |value, index|
159
+ path = "#{@path}.tests[#{index}]"
160
+ test = record(value, path, %w[expectation source loc])
161
+ expectation = enum(test["expectation"], "#{path}.expectation", %w[accept reject])
162
+ source = string(test["source"], "#{path}.source")
163
+ key = [expectation, source] #: [String, String]
164
+ invalid(path, "duplicates grammar test") if seen[key]
165
+ seen[key] = true
166
+ location(test["loc"], "#{path}.loc", nullable: false)
167
+ end
168
+ end
169
+
170
+ # @rbs () -> void
171
+ def validate_symbols
172
+ array(@data["symbols"], "#{@path}.symbols").each_with_index do |value, index|
173
+ validate_symbol(value, index)
174
+ end
175
+ end
176
+
177
+ # @rbs (json_value value, Integer index) -> void
178
+ def validate_symbol(value, index)
179
+ path = "#{@path}.symbols[#{index}]"
180
+ required = SYMBOL_REQUIRED + SYMBOL_METADATA_REQUIRED
181
+ symbol = record(value, path, required, SYMBOL_OPTIONAL)
182
+ id = nonnegative_integer(symbol["id"], "#{path}.id")
183
+ invalid("#{path}.id", "must equal its array index #{index}") unless id == index
184
+ name = nonempty_string(symbol["name"], "#{path}.name")
185
+ invalid("#{path}.name", "duplicates symbol #{name.inspect}") if @symbols_by_name.key?(name)
186
+ @symbols_by_id[id] = symbol
187
+ @symbols_by_name[name] = symbol
188
+ enum(symbol["kind"], "#{path}.kind", %w[terminal nonterminal])
189
+ boolean(symbol["reserved"], "#{path}.reserved")
190
+ validate_precedence(symbol["prec"], "#{path}.prec")
191
+ location(symbol["loc"], "#{path}.loc")
192
+ SYMBOL_OPTIONAL.each { |key| metadata(symbol[key], "#{path}.#{key}") if symbol.key?(key) }
193
+ nullable_string(symbol["doc"], "#{path}.doc")
194
+ end
195
+
196
+ # @rbs (json_value value, String path) -> void
197
+ def validate_precedence(value, path)
198
+ return if value.nil?
199
+
200
+ precedence = record(value, path, %w[associativity level])
201
+ enum(precedence["associativity"], "#{path}.associativity", %w[left right nonassoc precedence])
202
+ positive_integer(precedence["level"], "#{path}.level")
203
+ end
204
+
205
+ # @rbs () -> void
206
+ def validate_reserved_symbols
207
+ validate_reserved_symbol(0, "$eof")
208
+ validate_reserved_symbol(1, "error")
209
+ end
210
+
211
+ # @rbs (Integer id, String name) -> void
212
+ def validate_reserved_symbol(id, name)
213
+ symbol = @symbols_by_id[id]
214
+ invalid("#{@path}.symbols", "must contain reserved symbol #{name.inspect} at id #{id}") unless symbol
215
+ invalid("#{@path}.symbols[#{id}]", "must be reserved terminal #{name.inspect}") unless
216
+ symbol["name"] == name && symbol["kind"] == "terminal" && symbol["reserved"]
217
+ end
218
+
219
+ # @rbs () -> void
220
+ def validate_start
221
+ start = nonempty_string(@data["start"], "#{@path}.start")
222
+ symbol = @symbols_by_name[start]
223
+ invalid("#{@path}.start", "references missing symbol #{start.inspect}") unless symbol
224
+ invalid("#{@path}.start", "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
225
+ return unless @data.key?("starts")
226
+
227
+ validate_multiple_starts(start)
228
+ end
229
+
230
+ # @rbs (String start) -> void
231
+ def validate_multiple_starts(start)
232
+ starts = array(@data["starts"], "#{@path}.starts")
233
+ invalid("#{@path}.starts", "must not be empty") if starts.empty?
234
+ invalid("#{@path}.starts[0]", "must equal start #{start.inspect}") unless starts.first == start
235
+ invalid("#{@path}.mode", "must be extended for multiple start symbols") unless @data["mode"] == "extended"
236
+ seen = {} #: Hash[String, bool]
237
+ starts.each_with_index do |name, index|
238
+ name = nonempty_string(name, "#{@path}.starts[#{index}]")
239
+ invalid("#{@path}.starts[#{index}]", "duplicates start symbol #{name.inspect}") if seen[name]
240
+ seen[name] = true
241
+ definition = @symbols_by_name[name]
242
+ invalid("#{@path}.starts[#{index}]", "references missing symbol #{name.inspect}") unless definition
243
+ invalid("#{@path}.starts[#{index}]", "must reference a nonterminal") unless
244
+ definition["kind"] == "nonterminal"
245
+ end
246
+ end
247
+
248
+ # @rbs () -> void
249
+ def validate_recovery
250
+ path = "#{@path}.recovery"
251
+ recovery = record(@data["recovery"], path, %w[sync_tokens on_error_reduce])
252
+ invalid("#{@path}.mode", "must be extended for recovery declarations") unless @data["mode"] == "extended"
253
+ validate_recovery_symbols(recovery["sync_tokens"], "#{path}.sync_tokens", kind: "terminal")
254
+ groups = array(recovery["on_error_reduce"], "#{path}.on_error_reduce")
255
+ seen = {} #: Hash[String, bool]
256
+ groups.each_with_index do |group, index|
257
+ group_path = "#{path}.on_error_reduce[#{index}]"
258
+ names = array(group, group_path)
259
+ invalid(group_path, "must not be empty") if names.empty?
260
+ names.each_with_index do |name, name_index|
261
+ validate_recovery_symbol(
262
+ name, "#{group_path}[#{name_index}]", kind: "nonterminal", seen: seen
263
+ )
264
+ end
265
+ end
266
+ return unless array(recovery["sync_tokens"], "#{path}.sync_tokens").empty? && groups.empty?
267
+
268
+ invalid(path, "must declare at least one recovery policy")
269
+ end
270
+
271
+ # @rbs (json_value values, String path, kind: String) -> void
272
+ def validate_recovery_symbols(values, path, kind:)
273
+ seen = {} #: Hash[String, bool]
274
+ array(values, path).each_with_index do |name, index|
275
+ validate_recovery_symbol(name, "#{path}[#{index}]", kind: kind, seen: seen)
276
+ end
277
+ end
278
+
279
+ # @rbs (json_value value, String path, kind: String, seen: Hash[String, bool]) -> void
280
+ def validate_recovery_symbol(value, path, kind:, seen:)
281
+ name = nonempty_string(value, path)
282
+ invalid(path, "duplicates symbol #{name.inspect}") if seen[name]
283
+ seen[name] = true
284
+ symbol = @symbols_by_name[name]
285
+ invalid(path, "references missing symbol #{name.inspect}") unless symbol
286
+ invalid(path, "must reference a #{kind}") unless symbol["kind"] == kind
287
+ invalid(path, "must not reference the synthetic error token") if name == "error"
288
+ end
289
+
290
+ # @rbs () -> void
291
+ def validate_productions
292
+ array(@data["productions"], "#{@path}.productions").each_with_index do |value, index|
293
+ validate_production(value, index)
294
+ end
295
+ end
296
+
297
+ # @rbs (json_value value, Integer index) -> void
298
+ def validate_production(value, index)
299
+ path = "#{@path}.productions[#{index}]"
300
+ required = PRODUCTION_REQUIRED + PRODUCTION_METADATA_REQUIRED
301
+ optional = %w[node] #: Array[String]
302
+ production = record(value, path, required, optional)
303
+ id = nonnegative_integer(production["id"], "#{path}.id")
304
+ invalid("#{path}.id", "must equal its array index #{index}") unless id == index
305
+ @productions_by_id[id] = production
306
+ validate_lhs(production["lhs"], "#{path}.lhs")
307
+ rhs = array(production["rhs"], "#{path}.rhs")
308
+ validate_rhs(rhs, "#{path}.rhs")
309
+ validate_action(production["action"], "#{path}.action", rhs_length: rhs.length)
310
+ validate_precedence_override(production["prec_override"], "#{path}.prec_override")
311
+ validate_origin(production["origin"], "#{path}.origin")
312
+ nullable_string(production["doc"], "#{path}.doc")
313
+ validate_expansion(production["expansion"], "#{path}.expansion")
314
+ validate_node_annotation(production["node"], "#{path}.node", rhs_length: rhs.length)
315
+ end
316
+
317
+ # @rbs (json_value value, String path, rhs_length: Integer) -> void
318
+ def validate_node_annotation(value, path, rhs_length:)
319
+ return if value.nil?
320
+
321
+ node = record(value, path, %w[name fields loc])
322
+ name = nonempty_string(node["name"], "#{path}.name")
323
+ invalid("#{path}.name", "must be a Ruby constant identifier") unless name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
324
+ fields = array(node["fields"], "#{path}.fields")
325
+ invalid("#{path}.fields", "must contain #{rhs_length} entries") unless fields.length == rhs_length
326
+ seen = {} #: Hash[String, bool]
327
+ fields.each_with_index do |value, index|
328
+ field = nonempty_string(value, "#{path}.fields[#{index}]")
329
+ invalid("#{path}.fields[#{index}]", "must be a Ruby local identifier") unless
330
+ field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) && !RUBY_KEYWORDS.include?(field)
331
+ invalid("#{path}.fields[#{index}]", "duplicates field #{field.inspect}") if seen[field]
332
+ seen[field] = true
333
+ end
334
+ location(node["loc"], "#{path}.loc", nullable: false)
335
+ end
336
+
337
+ # @rbs (json_value value, String path) -> void
338
+ def validate_lhs(value, path)
339
+ id = nonnegative_integer(value, path)
340
+ symbol = @symbols_by_id[id]
341
+ invalid(path, "references missing symbol id #{id}") unless symbol
342
+ invalid(path, "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
343
+ end
344
+
345
+ # @rbs (json_value value, String path) -> void
346
+ def validate_rhs(value, path)
347
+ array(value, path).each_with_index do |id, index|
348
+ id = nonnegative_integer(id, "#{path}[#{index}]")
349
+ invalid("#{path}[#{index}]", "references missing symbol id #{id}") unless @symbols_by_id.key?(id)
350
+ end
351
+ end
352
+
353
+ # @rbs (json_value value, String path, rhs_length: Integer) -> void
354
+ def validate_action(value, path, rhs_length:)
355
+ return if value.nil?
356
+
357
+ required = %w[code loc named_refs context_length] + ACTION_COMPOSITION_REQUIRED
358
+ action = record(value, path, required)
359
+ string(action["code"], "#{path}.code")
360
+ location(action["loc"], "#{path}.loc", nullable: false)
361
+ context_length = nonnegative_integer(action["context_length"], "#{path}.context_length")
362
+ validate_named_refs(action["named_refs"], "#{path}.named_refs", limit: [rhs_length, context_length].max)
363
+ validate_action_composition(
364
+ action["composition"], "#{path}.composition", rhs_length: rhs_length
365
+ )
366
+ end
367
+
368
+ # @rbs (json_value value, String path, limit: Integer) -> void
369
+ def validate_named_refs(value, path, limit:)
370
+ names = {} #: Hash[String, bool]
371
+ array(value, path).each_with_index do |entry, index|
372
+ entry_path = "#{path}[#{index}]"
373
+ reference = record(entry, entry_path, %w[name index])
374
+ name = nonempty_string(reference["name"], "#{entry_path}.name")
375
+ invalid("#{entry_path}.name", "duplicates named reference #{name.inspect}") if names.key?(name)
376
+ names[name] = true
377
+ reference_index = nonnegative_integer(reference["index"], "#{entry_path}.index")
378
+ if reference_index >= limit
379
+ invalid("#{entry_path}.index", "must be less than the action context length #{limit}")
380
+ end
381
+ end
382
+ end
383
+
384
+ # @rbs (json_value value, String path) -> void
385
+ def validate_precedence_override(value, path)
386
+ return if value.nil?
387
+
388
+ id = nonnegative_integer(value, path)
389
+ symbol = @symbols_by_id[id]
390
+ invalid(path, "references missing symbol id #{id}") unless symbol
391
+ invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
392
+ end
393
+
394
+ # @rbs (json_value value, String path) -> void
395
+ def validate_origin(value, path)
396
+ origin = object(value, path)
397
+ kind = string(field(origin, "kind", path), "#{path}.kind")
398
+ optional = ORIGIN_KINDS.include?(kind) ? %w[expression] : [] # @type var optional: Array[String]
399
+ record(origin, path, %w[kind loc], optional)
400
+ enum(kind, "#{path}.kind", %w[user inline_action] + ORIGIN_KINDS)
401
+ string(origin["expression"], "#{path}.expression") if origin.key?("expression")
402
+ location(origin["loc"], "#{path}.loc", nullable: false)
403
+ end
404
+
405
+ # @rbs (json_value value, String path) -> void
406
+ def validate_string_map(value, path)
407
+ object(value, path).each do |key, item|
408
+ string(key, path)
409
+ string(item, child_path(path, key))
410
+ end
411
+ end
412
+
413
+ # @rbs () -> void
414
+ def validate_warnings
415
+ array(@data["warnings"], "#{@path}.warnings").each_with_index do |value, index|
416
+ validate_warning(value, "#{@path}.warnings[#{index}]")
417
+ end
418
+ end
419
+
420
+ # @rbs (json_value value, String path) -> void
421
+ def validate_warning(value, path)
422
+ warning = record(value, path, %w[type loc], %w[symbol production original])
423
+ nonempty_string(warning["type"], "#{path}.type")
424
+ location(warning["loc"], "#{path}.loc")
425
+ validate_warning_symbol(warning, path) if warning.key?("symbol")
426
+ validate_warning_production(warning, "production", path) if warning.key?("production")
427
+ validate_warning_production(warning, "original", path) if warning.key?("original")
428
+ end
429
+
430
+ # @rbs (json_object warning, String path) -> void
431
+ def validate_warning_symbol(warning, path)
432
+ name = nonempty_string(warning["symbol"], "#{path}.symbol")
433
+ invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
434
+ end
435
+
436
+ # @rbs (json_object warning, String field_name, String path) -> void
437
+ def validate_warning_production(warning, field_name, path)
438
+ id = nonnegative_integer(warning[field_name], "#{path}.#{field_name}")
439
+ invalid("#{path}.#{field_name}", "references missing production id #{id}") unless @productions_by_id.key?(id)
440
+ end
441
+
442
+ # @rbs () -> void
443
+ def validate_lexer
444
+ value = @data.fetch("lexer")
445
+ lexer = value #: json_object
446
+ LexerDocument.new(lexer, path: "#{@path}.lexer").validate
447
+ rules = array(lexer.fetch("rules"), "#{@path}.lexer.rules")
448
+ rules.each_with_index do |rule, index|
449
+ rule_record = rule #: json_object
450
+ next unless rule_record["kind"] == "token"
451
+
452
+ name = nonempty_string(rule_record["token"], "#{@path}.lexer.rules[#{index}].token")
453
+ symbol = @symbols_by_name[name]
454
+ invalid("#{@path}.lexer.rules[#{index}].token", "references missing terminal #{name.inspect}") unless
455
+ symbol&.fetch("kind") == "terminal"
456
+ end
457
+ end
458
+
459
+ # @rbs () -> void
460
+ def validate_user_code_chunks
461
+ path = "#{@path}.user_code_chunks"
462
+ object(@data["user_code_chunks"], path).each do |key, chunks|
463
+ string(key, path)
464
+ array(chunks, child_path(path, key)).each_with_index do |chunk, index|
465
+ chunk_path = "#{child_path(path, key)}[#{index}]"
466
+ chunk = record(chunk, chunk_path, %w[code loc])
467
+ string(chunk["code"], "#{chunk_path}.code")
468
+ location(chunk["loc"], "#{chunk_path}.loc", nullable: false)
469
+ end
470
+ end
471
+ end
472
+
473
+ # @rbs (json_value value, String path, ?nullable: bool) -> void
474
+ def validate_source_provenance(value, path, nullable: true)
475
+ return if nullable && value.nil?
476
+
477
+ source = record(value, path, %w[file root byte_span])
478
+ nullable_string(source["file"], "#{path}.file")
479
+ nullable_string(source["root"], "#{path}.root")
480
+ validate_byte_span(source["byte_span"], "#{path}.byte_span")
481
+ end
482
+
483
+ # @rbs (json_value value, String path) -> void
484
+ def validate_byte_span(value, path)
485
+ return if value.nil?
486
+
487
+ span = record(value, path, %w[start end])
488
+ start_byte = nonnegative_integer(span["start"], "#{path}.start")
489
+ end_byte = nonnegative_integer(span["end"], "#{path}.end")
490
+ invalid("#{path}.end", "must be greater than or equal to start") if end_byte < start_byte
491
+ end
492
+
493
+ # @rbs () -> void
494
+ def validate_parser_contract
495
+ path = "#{@path}.parser_contract"
496
+ contract = record(@data["parser_contract"], path, %w[algorithm entries cst_trivia])
497
+ validate_contract_entry(contract["algorithm"], "#{path}.algorithm", %w[slr lalr ielr lr1])
498
+ validate_contract_entry(contract["entries"], "#{path}.entries", %w[shared isolated])
499
+ cst = validate_contract_entry(
500
+ contract["cst_trivia"], "#{path}.cst_trivia", %w[leading balanced drop]
501
+ )
502
+ return unless cst["explicit"]
503
+
504
+ invalid("#{path}.cst_trivia", "requires options.cst") unless @data.dig("options", "cst") == true
505
+ end
506
+
507
+ # @rbs (untyped value, String, Array[String]) -> Hash[String, untyped]
508
+ def validate_contract_entry(value, path, allowed)
509
+ entry = record(value, path, %w[value explicit loc])
510
+ explicit = boolean(entry["explicit"], "#{path}.explicit")
511
+ unless explicit
512
+ literal(entry["value"], "#{path}.value", nil)
513
+ literal(entry["loc"], "#{path}.loc", nil)
514
+ return entry
515
+ end
516
+
517
+ enum(entry["value"], "#{path}.value", allowed)
518
+ location(entry["loc"], "#{path}.loc", nullable: false)
519
+ entry
520
+ end
521
+
522
+ # @rbs (json_value value, String path) -> void
523
+ def validate_expansion(value, path)
524
+ return if value.nil?
525
+
526
+ expansion = record(value, path, %w[parameter inline include_chain])
527
+ validate_parameter_expansion(expansion["parameter"], "#{path}.parameter")
528
+ validate_inline_expansion(expansion["inline"], "#{path}.inline")
529
+ array(expansion["include_chain"], "#{path}.include_chain").each_with_index do |source, index|
530
+ validate_source_provenance(source, "#{path}.include_chain[#{index}]", nullable: false)
531
+ end
532
+ end
533
+
534
+ # @rbs (json_value value, String path) -> void
535
+ def validate_parameter_expansion(value, path)
536
+ return if value.nil?
537
+
538
+ parameter = record(value, path, %w[rule arguments])
539
+ nonempty_string(parameter["rule"], "#{path}.rule")
540
+ array(parameter["arguments"], "#{path}.arguments").each_with_index do |argument, index|
541
+ nonempty_string(argument, "#{path}.arguments[#{index}]")
542
+ end
543
+ end
544
+
545
+ # @rbs (json_value value, String path) -> void
546
+ def validate_inline_expansion(value, path)
547
+ return if value.nil?
548
+
549
+ inline = record(value, path, %w[rule])
550
+ nonempty_string(inline["rule"], "#{path}.rule")
551
+ end
552
+
553
+ # @rbs (json_value value, String path, rhs_length: Integer) -> void
554
+ def validate_action_composition(value, path, rhs_length:)
555
+ return if value.nil?
556
+
557
+ composition = record(value, path, %w[strategy fragments], %w[plan])
558
+ literal(composition["strategy"], "#{path}.strategy", "sequence")
559
+ fragments = array(composition["fragments"], "#{path}.fragments")
560
+ invalid("#{path}.fragments", "must not be empty") if fragments.empty?
561
+ fragments.each_with_index do |value, index|
562
+ fragment_path = "#{path}.fragments[#{index}]"
563
+ fragment = record(value, fragment_path, %w[kind source])
564
+ enum(fragment["kind"], "#{fragment_path}.kind", %w[rule inline])
565
+ validate_source_provenance(fragment["source"], "#{fragment_path}.source")
566
+ end
567
+ validate_action_composition_plan(composition["plan"], "#{path}.plan", rhs_length, fragments) if
568
+ composition.key?("plan")
569
+ end
570
+
571
+ # @rbs (json_value value, String path, Integer rhs_length, Array[json_value] fragments) -> void
572
+ def validate_action_composition_plan(value, path, rhs_length, fragments)
573
+ plan = record(value, path, %w[version physical steps])
574
+ literal(plan["version"], "#{path}.version", 1)
575
+ physical = nonnegative_integer(plan["physical"], "#{path}.physical")
576
+ invalid("#{path}.physical", "must equal production RHS length #{rhs_length}") unless physical == rhs_length
577
+ steps = array(plan["steps"], "#{path}.steps")
578
+ invalid("#{path}.steps", "must not be empty") if steps.empty?
579
+ invalid("#{path}.steps", "must align one-to-one with fragments") unless steps.length == fragments.length
580
+ steps.each_with_index do |step, index|
581
+ validate_action_composition_step(step, "#{path}.steps[#{index}]", physical, index)
582
+ step_record = step #: json_object
583
+ fragment_record = fragments.fetch(index) #: json_object
584
+ next if step_record["kind"] == fragment_record["kind"]
585
+
586
+ invalid("#{path}.steps[#{index}].kind", "must match the corresponding fragment")
587
+ end
588
+ end
589
+
590
+ # @rbs (json_value value, String path, Integer physical, Integer step_index) -> void
591
+ def validate_action_composition_step(value, path, physical, step_index)
592
+ step = record(
593
+ value, path,
594
+ %w[kind rule code loc named_refs context_length inputs stack_inputs lookahead result_var],
595
+ %w[result_type]
596
+ )
597
+ kind = enum(step["kind"], "#{path}.kind", %w[rule inline])
598
+ rule = nullable_string(step["rule"], "#{path}.rule")
599
+ invalid("#{path}.rule", "must name an inline rule") if kind == "inline" && (!rule || rule.empty?)
600
+ nullable_string(step["code"], "#{path}.code")
601
+ location(step["loc"], "#{path}.loc", nullable: false)
602
+ context = nonnegative_integer(step["context_length"], "#{path}.context_length")
603
+ inputs = array(step["inputs"], "#{path}.inputs")
604
+ limit = physical + step_index
605
+ validate_action_slots(inputs, "#{path}.inputs", limit)
606
+ stack_inputs = array(step["stack_inputs"], "#{path}.stack_inputs")
607
+ validate_action_slots(stack_inputs, "#{path}.stack_inputs", limit)
608
+ validate_named_refs(step["named_refs"], "#{path}.named_refs", limit: [inputs.length, context].max)
609
+ validate_action_lookahead(step["lookahead"], "#{path}.lookahead", physical)
610
+ boolean(step["result_var"], "#{path}.result_var")
611
+ nullable_string(step["result_type"], "#{path}.result_type")
612
+ end
613
+
614
+ # @rbs (Array[json_value] inputs, String path, Integer limit) -> void
615
+ def validate_action_slots(inputs, path, limit)
616
+ inputs.each_with_index do |input, index|
617
+ slot_path = "#{path}[#{index}]"
618
+ slot = nonnegative_integer(input, slot_path)
619
+ invalid(slot_path, "must reference an available slot below #{limit}") if slot >= limit
620
+ end
621
+ end
622
+
623
+ # @rbs (json_value value, String path, Integer physical) -> void
624
+ def validate_action_lookahead(value, path, physical)
625
+ return if value.nil?
626
+
627
+ boundary = nonnegative_integer(value, path)
628
+ invalid(path, "must reference a physical slot below #{physical}") if boundary >= physical
629
+ end
630
+ end
631
+ # rubocop:enable Metrics/ClassLength
632
+ end
633
+ end
634
+ end