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,375 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module IR
5
+ module Validator
6
+ # Structural and referential validation for a current Automaton IR JSON object.
7
+ # rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.
8
+ class AutomatonDocument < Base
9
+ ROOT_REQUIRED = %w[
10
+ ibex_ir schema_version algorithm entry_construction grammar_digest grammar states conflict_summary
11
+ ].freeze #: Array[String]
12
+ ROOT_OPTIONAL = %w[entry_states].freeze #: Array[String]
13
+ STATE_REQUIRED = %w[id items transitions actions gotos default_action conflicts].freeze #: Array[String]
14
+ ACTION_TYPES = %w[shift reduce accept error].freeze #: Array[String]
15
+ RESOLUTION_KINDS = %w[definition_order default_shift precedence associativity].freeze #: Array[String]
16
+
17
+ # @rbs @data: json_object
18
+ # @rbs @states_by_id: Hash[Integer, json_object]
19
+ # @rbs @grammar: GrammarDocument
20
+ # @rbs @version: Integer
21
+
22
+ # @rbs (json_object data, ?version: Integer) -> void
23
+ def initialize(data, version: data.fetch("schema_version"))
24
+ super()
25
+ @data = data
26
+ @version = version
27
+ @states_by_id = {} #: Hash[Integer, json_object]
28
+ end
29
+
30
+ # @rbs () -> self
31
+ def validate
32
+ record(@data, "$", ROOT_REQUIRED, ROOT_OPTIONAL)
33
+ literal(@data["ibex_ir"], "$.ibex_ir", "automaton")
34
+ literal(@data["schema_version"], "$.schema_version", @version)
35
+ enum(@data["algorithm"], "$.algorithm", %w[slr lalr1 ielr1 lr1])
36
+ validate_digest
37
+ grammar = object(@data["grammar"], "$.grammar")
38
+ literal(grammar["schema_version"], "$.grammar.schema_version", @version)
39
+ @grammar = GrammarDocument.new(grammar, path: "$.grammar", version: @version).validate
40
+ validate_construction_contract(grammar)
41
+ validate_state_records
42
+ validate_entry_states
43
+ validate_state_contents
44
+ validate_conflict_summary
45
+ self
46
+ end
47
+
48
+ private
49
+
50
+ # @rbs () -> void
51
+ def validate_entry_states
52
+ value = @data["entry_states"]
53
+ if value.nil?
54
+ invalid("$.entry_states", "is required for multiple start symbols") if @data.dig("grammar", "starts")
55
+ return
56
+ end
57
+
58
+ entries = object(value, "$.entry_states")
59
+ expected = @data.dig("grammar", "starts") || [@data.dig("grammar", "start")]
60
+ invalid("$.entry_states", "keys must equal grammar starts in order") unless entries.keys == expected
61
+ entries.each do |name, state|
62
+ state_id = nonnegative_integer(state, "$.entry_states.#{name}")
63
+ invalid("$.entry_states.#{name}", "references missing state #{state_id}") unless @states_by_id[state_id]
64
+ end
65
+ end
66
+
67
+ # @rbs (Hash[String, untyped] grammar) -> void
68
+ def validate_construction_contract(grammar)
69
+ entry_construction = enum(
70
+ @data["entry_construction"], "$.entry_construction", %w[shared isolated]
71
+ )
72
+ contract = grammar.fetch("parser_contract")
73
+ algorithm = contract.fetch("algorithm")
74
+ expected_algorithm = { "lalr" => "lalr1", "ielr" => "ielr1" }.fetch(
75
+ algorithm["value"], algorithm["value"]
76
+ )
77
+ if algorithm["explicit"] && @data["algorithm"] != expected_algorithm
78
+ invalid("$.algorithm", "must match the embedded parser contract")
79
+ end
80
+
81
+ entries = contract.fetch("entries")
82
+ return unless entries["explicit"] && entry_construction != entries["value"]
83
+
84
+ invalid("$.entry_construction", "must match the embedded parser contract")
85
+ end
86
+
87
+ # @rbs () -> void
88
+ def validate_digest
89
+ digest = string(@data["grammar_digest"], "$.grammar_digest")
90
+ invalid("$.grammar_digest", "must be a sha256 digest") unless digest.match?(/\Asha256:[0-9a-f]{64}\z/)
91
+ end
92
+
93
+ # @rbs () -> void
94
+ def validate_state_records
95
+ states = array(@data["states"], "$.states")
96
+ invalid("$.states", "must contain at least one state") if states.empty?
97
+ states.each_with_index do |value, index|
98
+ path = "$.states[#{index}]"
99
+ state = record(value, path, STATE_REQUIRED)
100
+ id = nonnegative_integer(state["id"], "#{path}.id")
101
+ invalid("#{path}.id", "must equal its array index #{index}") unless id == index
102
+ @states_by_id[id] = state
103
+ end
104
+ end
105
+
106
+ # @rbs () -> void
107
+ def validate_state_contents
108
+ @states_by_id.each do |id, state|
109
+ path = "$.states[#{id}]"
110
+ validate_items(state["items"], "#{path}.items")
111
+ validate_transitions(state["transitions"], "#{path}.transitions")
112
+ validate_actions(state["actions"], "#{path}.actions")
113
+ validate_gotos(state["gotos"], "#{path}.gotos")
114
+ validate_parser_action(state["default_action"], "#{path}.default_action", nullable: true)
115
+ validate_conflicts(state["conflicts"], "#{path}.conflicts")
116
+ end
117
+ end
118
+
119
+ # @rbs (json_value value, String path) -> void
120
+ def validate_items(value, path)
121
+ array(value, path).each_with_index do |item, index|
122
+ item_path = "#{path}[#{index}]"
123
+ item = record(item, item_path, %w[production dot lookaheads])
124
+ production = integer(item["production"], "#{item_path}.production")
125
+ validate_item_production(production, item["dot"], item_path)
126
+ validate_lookaheads(item["lookaheads"], "#{item_path}.lookaheads")
127
+ end
128
+ end
129
+
130
+ # @rbs (Integer production_id, json_value dot_value, String path) -> void
131
+ def validate_item_production(production_id, dot_value, path)
132
+ dot = nonnegative_integer(dot_value, "#{path}.dot")
133
+ if production_id == -1
134
+ invalid("#{path}.dot", "must not exceed 1 for the augmented production") if dot > 1
135
+ return
136
+ end
137
+ production = @grammar.productions_by_id[production_id]
138
+ invalid("#{path}.production", "references missing production id #{production_id}") unless production
139
+ rhs = array(production["rhs"], "#{path}.production.rhs")
140
+ invalid("#{path}.dot", "exceeds production #{production_id} length") if dot > rhs.length
141
+ end
142
+
143
+ # @rbs (json_value value, String path) -> void
144
+ def validate_lookaheads(value, path)
145
+ array(value, path).each_with_index do |name, index|
146
+ name = string(name, "#{path}[#{index}]")
147
+ symbol = @grammar.symbols_by_name[name]
148
+ invalid("#{path}[#{index}]", "references missing symbol #{name.inspect}") unless symbol
149
+ invalid("#{path}[#{index}]", "must reference a terminal") unless symbol["kind"] == "terminal"
150
+ end
151
+ end
152
+
153
+ # @rbs (json_value value, String path) -> void
154
+ def validate_transitions(value, path)
155
+ symbol_map(value, path) do |target, target_path, _symbol|
156
+ validate_state_reference(target, target_path)
157
+ end
158
+ end
159
+
160
+ # @rbs (json_value value, String path) -> void
161
+ def validate_actions(value, path)
162
+ symbol_map(value, path, kind: "terminal") do |action, action_path, _symbol|
163
+ validate_parser_action(action, action_path)
164
+ end
165
+ end
166
+
167
+ # @rbs (json_value value, String path) -> void
168
+ def validate_gotos(value, path)
169
+ symbol_map(value, path, kind: "nonterminal") do |target, target_path, _symbol|
170
+ validate_state_reference(target, target_path)
171
+ end
172
+ end
173
+
174
+ # @rbs (json_value value, String path, ?kind: String?) { (json_value, String, json_object) -> void } -> void
175
+ def symbol_map(value, path, kind: nil, &block)
176
+ object(value, path).each do |name, item|
177
+ item_path = child_path(path, name)
178
+ symbol = @grammar.symbols_by_name[name]
179
+ invalid(item_path, "references missing symbol #{name.inspect}") unless symbol
180
+ invalid(item_path, "must reference a #{kind}") if kind && symbol["kind"] != kind
181
+ block.call(item, item_path, symbol)
182
+ end
183
+ end
184
+
185
+ # @rbs (json_value value, String path) -> void
186
+ def validate_state_reference(value, path)
187
+ id = nonnegative_integer(value, path)
188
+ invalid(path, "references missing state id #{id}") unless @states_by_id.key?(id)
189
+ end
190
+
191
+ # @rbs (json_value value, String path, ?nullable: bool) -> void
192
+ def validate_parser_action(value, path, nullable: false)
193
+ return if nullable && value.nil?
194
+
195
+ action = object(value, path)
196
+ type = enum(field(action, "type", path), "#{path}.type", ACTION_TYPES)
197
+ required = case type
198
+ when "shift" then %w[type state]
199
+ when "reduce" then %w[type production]
200
+ else %w[type]
201
+ end
202
+ record(action, path, required)
203
+ validate_state_reference(action["state"], "#{path}.state") if type == "shift"
204
+ validate_production_reference(action["production"], "#{path}.production") if type == "reduce"
205
+ end
206
+
207
+ # @rbs (json_value value, String path) -> void
208
+ def validate_production_reference(value, path)
209
+ id = nonnegative_integer(value, path)
210
+ invalid(path, "references missing production id #{id}") unless @grammar.productions_by_id.key?(id)
211
+ end
212
+
213
+ # @rbs (json_value value, String path) -> void
214
+ def validate_conflicts(value, path)
215
+ array(value, path).each_with_index do |conflict, index|
216
+ conflict_path = "#{path}[#{index}]"
217
+ conflict = object(conflict, conflict_path)
218
+ type = enum(field(conflict, "type", conflict_path), "#{conflict_path}.type",
219
+ %w[shift_reduce reduce_reduce])
220
+ if type == "shift_reduce"
221
+ validate_shift_reduce(conflict, conflict_path)
222
+ else
223
+ validate_reduce_reduce(conflict, conflict_path)
224
+ end
225
+ end
226
+ end
227
+
228
+ # @rbs (json_object conflict, String path) -> void
229
+ def validate_shift_reduce(conflict, path)
230
+ record(conflict, path, %w[type symbol shift_to reduce resolution], %w[midrule_origins entries composite])
231
+ validate_conflict_symbol(conflict["symbol"], "#{path}.symbol")
232
+ validate_state_reference(conflict["shift_to"], "#{path}.shift_to")
233
+ validate_production_reference(conflict["reduce"], "#{path}.reduce")
234
+ validate_resolution(conflict["resolution"], "#{path}.resolution")
235
+ validate_midrule_origins(conflict["midrule_origins"], "#{path}.midrule_origins") if
236
+ conflict.key?("midrule_origins")
237
+ validate_conflict_entries(conflict, path)
238
+ end
239
+
240
+ # @rbs (json_object conflict, String path) -> void
241
+ def validate_reduce_reduce(conflict, path)
242
+ record(conflict, path, %w[type symbol reductions resolution], %w[midrule_origins entries composite])
243
+ validate_conflict_symbol(conflict["symbol"], "#{path}.symbol")
244
+ reductions = array(conflict["reductions"], "#{path}.reductions").map.with_index do |id, index|
245
+ nonnegative_integer(id, "#{path}.reductions[#{index}]")
246
+ end
247
+ invalid("#{path}.reductions", "must contain at least two productions") if reductions.length < 2
248
+ reductions.each_with_index do |id, index|
249
+ validate_production_reference(id, "#{path}.reductions[#{index}]")
250
+ end
251
+ if reductions.uniq.length != reductions.length
252
+ invalid("#{path}.reductions", "must contain unique production ids")
253
+ end
254
+ validate_resolution(conflict["resolution"], "#{path}.resolution", reductions: reductions)
255
+ validate_midrule_origins(conflict["midrule_origins"], "#{path}.midrule_origins") if
256
+ conflict.key?("midrule_origins")
257
+ validate_conflict_entries(conflict, path)
258
+ end
259
+
260
+ # @rbs (json_object conflict, String path) -> void
261
+ def validate_conflict_entries(conflict, path)
262
+ if conflict.key?("entries")
263
+ starts = @data.dig("grammar", "starts") || [@data.dig("grammar", "start")]
264
+ entries = array(conflict["entries"], "#{path}.entries")
265
+ invalid("#{path}.entries", "must not be empty") if entries.empty?
266
+ entries.each_with_index do |name, index|
267
+ name = nonempty_string(name, "#{path}.entries[#{index}]")
268
+ invalid("#{path}.entries[#{index}]", "is not a grammar start symbol") unless starts.include?(name)
269
+ end
270
+ invalid("#{path}.entries", "must be unique") unless entries.uniq.length == entries.length
271
+ end
272
+ boolean(conflict["composite"], "#{path}.composite") if conflict.key?("composite")
273
+ end
274
+
275
+ # @rbs (json_value value, String path) -> void
276
+ def validate_midrule_origins(value, path)
277
+ origins = array(value, path)
278
+ invalid(path, "must not be empty") if origins.empty?
279
+ origins.each_with_index { |origin, index| location(origin, "#{path}[#{index}]") }
280
+ end
281
+
282
+ # @rbs (json_value value, String path) -> void
283
+ def validate_conflict_symbol(value, path)
284
+ name = string(value, path)
285
+ symbol = @grammar.symbols_by_name[name]
286
+ invalid(path, "references missing symbol #{name.inspect}") unless symbol
287
+ invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
288
+ end
289
+
290
+ # @rbs (json_value value, String path, ?reductions: Array[Integer]?) -> void
291
+ def validate_resolution(value, path, reductions: nil)
292
+ resolution = record(value, path, %w[by chose], %w[associativity])
293
+ enum(resolution["by"], "#{path}.by", RESOLUTION_KINDS)
294
+ if reductions
295
+ chosen = resolution["chose"]
296
+ validate_production_reference(chosen, "#{path}.chose")
297
+ invalid("#{path}.chose", "must be one of the reductions") unless reductions.include?(chosen)
298
+ else
299
+ enum(resolution["chose"], "#{path}.chose", %w[shift reduce error])
300
+ end
301
+ return unless resolution.key?("associativity")
302
+
303
+ enum(resolution["associativity"], "#{path}.associativity", %w[left right nonassoc])
304
+ end
305
+
306
+ # @rbs () -> void
307
+ def validate_conflict_summary
308
+ path = "$.conflict_summary"
309
+ summary = record(
310
+ @data["conflict_summary"], path, %w[sr resolved_sr rr expected_sr expectation_met],
311
+ %w[expected_rr rr_expectation_met]
312
+ )
313
+ %w[sr resolved_sr rr expected_sr].each do |key|
314
+ nonnegative_integer(summary[key], "#{path}.#{key}")
315
+ end
316
+ boolean(summary["expectation_met"], "#{path}.expectation_met")
317
+ if summary.key?("expected_rr") || summary.key?("rr_expectation_met")
318
+ nonnegative_integer(summary["expected_rr"], "#{path}.expected_rr")
319
+ boolean(summary["rr_expectation_met"], "#{path}.rr_expectation_met")
320
+ end
321
+ validate_conflict_counts(summary, path)
322
+ validate_expectation(summary, path)
323
+ end
324
+
325
+ # @rbs (json_object summary, String path) -> void
326
+ def validate_conflict_counts(summary, path)
327
+ conflicts = @states_by_id.values.flat_map do |state|
328
+ array(state["conflicts"], "$.states.conflicts").map do |conflict|
329
+ record = conflict #: json_object
330
+ record
331
+ end
332
+ end
333
+ shift_reduce = conflicts.select { |conflict| conflict["type"] == "shift_reduce" }
334
+ counts = {
335
+ "sr" => shift_reduce.count { |conflict| conflict.dig("resolution", "by") == "default_shift" },
336
+ "resolved_sr" => shift_reduce.count { |conflict| conflict.dig("resolution", "by") != "default_shift" },
337
+ "rr" => conflicts.count { |conflict| conflict["type"] == "reduce_reduce" }
338
+ }
339
+ counts.each do |key, actual|
340
+ next if summary[key] == actual
341
+
342
+ label = key == "rr" ? "reduce/reduce" : "shift/reduce"
343
+ invalid("#{path}.#{key}", "must equal the #{actual} recorded #{label} conflicts")
344
+ end
345
+ end
346
+
347
+ # @rbs (json_object summary, String path) -> void
348
+ def validate_expectation(summary, path)
349
+ grammar = @data.fetch("grammar") #: json_object
350
+ grammar_expect = grammar.fetch("expect")
351
+ unless summary["expected_sr"] == grammar_expect
352
+ invalid("#{path}.expected_sr", "must equal embedded grammar expect #{grammar_expect}")
353
+ end
354
+ expected_met = summary["sr"] == summary["expected_sr"]
355
+ unless summary["expectation_met"] == expected_met
356
+ invalid("#{path}.expectation_met", "must be #{expected_met} for the recorded shift/reduce count")
357
+ end
358
+
359
+ grammar_expect_rr = grammar["expect_rr"]
360
+ return if grammar_expect_rr.nil?
361
+
362
+ unless summary["expected_rr"] == grammar_expect_rr
363
+ invalid("#{path}.expected_rr", "must equal embedded grammar expect_rr #{grammar_expect_rr}")
364
+ end
365
+ rr_expected_met = summary["rr"] == summary["expected_rr"]
366
+ return if summary["rr_expectation_met"] == rr_expected_met
367
+
368
+ invalid("#{path}.rr_expectation_met",
369
+ "must be #{rr_expected_met} for the recorded reduce/reduce count")
370
+ end
371
+ end
372
+ # rubocop:enable Metrics/ClassLength
373
+ end
374
+ end
375
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module IR
5
+ module Validator
6
+ # @rbs!
7
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
8
+ # type json_object = Hash[String, json_value]
9
+
10
+ # Shared JSON-shape checks for current IR documents.
11
+ class Base
12
+ POSITION = "(ir):1:1"
13
+
14
+ private
15
+
16
+ # @rbs (json_value value, String path, Array[String] required, ?Array[String] optional) -> json_object
17
+ def record(value, path, required, optional = [])
18
+ value = object(value, path)
19
+ missing = required.reject { |key| value.key?(key) }
20
+ invalid(path, "is missing required field #{missing.first.inspect}") unless missing.empty?
21
+ unknown = value.keys - required - optional
22
+ invalid(path, "has unsupported field #{unknown.first.inspect}") unless unknown.empty?
23
+ value
24
+ end
25
+
26
+ # @rbs (json_value value, String path) -> json_object
27
+ def object(value, path)
28
+ if value.is_a?(Hash)
29
+ object = value #: json_object
30
+ return object
31
+ end
32
+
33
+ invalid(path, "must be an object")
34
+ end
35
+
36
+ # @rbs (json_value value, String path) -> Array[json_value]
37
+ def array(value, path)
38
+ if value.is_a?(Array)
39
+ array = value #: Array[json_value]
40
+ return array
41
+ end
42
+
43
+ invalid(path, "must be an array")
44
+ end
45
+
46
+ # @rbs (json_value value, String path) -> String
47
+ def string(value, path)
48
+ return value if value.is_a?(String)
49
+
50
+ invalid(path, "must be a string")
51
+ end
52
+
53
+ # @rbs (json_value value, String path) -> String
54
+ def nonempty_string(value, path)
55
+ value = string(value, path)
56
+ invalid(path, "must not be empty") if value.empty?
57
+ value
58
+ end
59
+
60
+ # @rbs (json_value value, String path) -> Integer
61
+ def integer(value, path)
62
+ return value if value.is_a?(Integer)
63
+
64
+ invalid(path, "must be an integer")
65
+ end
66
+
67
+ # @rbs (json_value value, String path) -> Integer
68
+ def nonnegative_integer(value, path)
69
+ value = integer(value, path)
70
+ invalid(path, "must be greater than or equal to 0") if value.negative?
71
+ value
72
+ end
73
+
74
+ # @rbs (json_value value, String path) -> bool
75
+ def boolean(value, path)
76
+ if [true, false].include?(value)
77
+ result = value #: bool
78
+ return result
79
+ end
80
+
81
+ invalid(path, "must be a boolean")
82
+ end
83
+
84
+ # @rbs (json_value value, String path, json_value expected) -> void
85
+ def literal(value, path, expected)
86
+ invalid(path, "must be #{expected.inspect}") unless value == expected
87
+ end
88
+
89
+ # @rbs (json_value value, String path, Array[String] values) -> String
90
+ def enum(value, path, values)
91
+ value = string(value, path)
92
+ invalid(path, "must be one of #{values.join(', ')}") unless values.include?(value)
93
+ value
94
+ end
95
+
96
+ # @rbs (json_value value, String path, ?nullable: bool) -> void
97
+ def location(value, path, nullable: true)
98
+ return if nullable && value.nil?
99
+
100
+ value = record(value, path, %w[file line column])
101
+ string(value["file"], "#{path}.file")
102
+ positive_integer(value["line"], "#{path}.line")
103
+ positive_integer(value["column"], "#{path}.column")
104
+ end
105
+
106
+ # @rbs (json_value value, String path) -> Integer
107
+ def positive_integer(value, path)
108
+ value = integer(value, path)
109
+ invalid(path, "must be greater than or equal to 1") unless value.positive?
110
+ value
111
+ end
112
+
113
+ # @rbs (json_value value, String path) -> String?
114
+ def nullable_string(value, path)
115
+ return nil if value.nil?
116
+
117
+ string(value, path)
118
+ end
119
+
120
+ # @rbs (json_value value, String path) -> void
121
+ def metadata(value, path)
122
+ return if value.nil?
123
+
124
+ value = string(value, path)
125
+ invalid(path, "must not be empty") if value.strip.empty?
126
+ invalid(path, "must be a single line") if value.match?(/[\r\n]/)
127
+ invalid(path, "must not contain control characters") if value.match?(/[[:cntrl:]]/)
128
+ end
129
+
130
+ # @rbs (json_object value, String key, String path) -> json_value
131
+ def field(value, key, path)
132
+ value.fetch(key) { invalid(path, "is missing required field #{key.inspect}") }
133
+ end
134
+
135
+ # @rbs (String path, String key) -> String
136
+ def child_path(path, key)
137
+ key.match?(/\A[$A-Za-z_][$A-Za-z0-9_]*\z/) ? "#{path}.#{key}" : "#{path}[#{key.inspect}]"
138
+ end
139
+
140
+ # @rbs (String path, String message) -> bot
141
+ def invalid(path, message)
142
+ raise Ibex::Error, "#{POSITION}: #{path} #{message}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end