ibex 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +95 -272
  3. data/docs/architecture.md +187 -35
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +3 -4
  12. data/docs/cst.md +24 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +179 -2
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +2 -1
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +54 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +184 -13
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +5 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +11 -0
  63. data/docs/release-readiness.md +126 -68
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +144 -34
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/lib/ibex/analysis/digraph.rb +128 -0
  75. data/lib/ibex/analysis.rb +1 -0
  76. data/lib/ibex/bison_import/importer.rb +516 -0
  77. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  78. data/lib/ibex/bison_import.rb +200 -0
  79. data/lib/ibex/bounded_subprocess.rb +171 -0
  80. data/lib/ibex/cli/ambiguity.rb +14 -5
  81. data/lib/ibex/cli/analysis.rb +134 -0
  82. data/lib/ibex/cli/bison_import.rb +121 -0
  83. data/lib/ibex/cli/config.rb +128 -0
  84. data/lib/ibex/cli/diagnostics.rb +31 -11
  85. data/lib/ibex/cli/documentation.rb +11 -8
  86. data/lib/ibex/cli/equiv.rb +169 -0
  87. data/lib/ibex/cli/error_messages.rb +11 -4
  88. data/lib/ibex/cli/explain.rb +14 -5
  89. data/lib/ibex/cli/fix.rb +203 -0
  90. data/lib/ibex/cli/formatting.rb +11 -10
  91. data/lib/ibex/cli/fuzz.rb +200 -0
  92. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  93. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  94. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  95. data/lib/ibex/cli/grammar_tests.rb +32 -14
  96. data/lib/ibex/cli/ir_tools.rb +5 -61
  97. data/lib/ibex/cli/outputs.rb +60 -33
  98. data/lib/ibex/cli/reduce.rb +202 -0
  99. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  100. data/lib/ibex/cli/samples.rb +49 -23
  101. data/lib/ibex/cli/verify.rb +116 -0
  102. data/lib/ibex/cli/watch.rb +6 -5
  103. data/lib/ibex/cli.rb +357 -56
  104. data/lib/ibex/codegen/action_locations.rb +1 -1
  105. data/lib/ibex/codegen/action_method_source.rb +5 -6
  106. data/lib/ibex/codegen/action_source.rb +3 -2
  107. data/lib/ibex/codegen/ambiguity.rb +5 -4
  108. data/lib/ibex/codegen/explain.rb +123 -55
  109. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  110. data/lib/ibex/codegen/rbs.rb +15 -7
  111. data/lib/ibex/codegen/report.rb +25 -13
  112. data/lib/ibex/codegen/ruby.rb +28 -64
  113. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  114. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  115. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  116. data/lib/ibex/configuration/explanation.rb +421 -0
  117. data/lib/ibex/configuration/inspector.rb +232 -0
  118. data/lib/ibex/configuration.rb +580 -0
  119. data/lib/ibex/coverage/collector.rb +21 -11
  120. data/lib/ibex/coverage/event_stream.rb +13 -7
  121. data/lib/ibex/coverage/report.rb +26 -18
  122. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  123. data/lib/ibex/delta_reducer.rb +99 -0
  124. data/lib/ibex/diff.rb +140 -0
  125. data/lib/ibex/equiv/machine.rb +135 -0
  126. data/lib/ibex/equiv.rb +373 -0
  127. data/lib/ibex/fix.rb +557 -0
  128. data/lib/ibex/frontend/ast.rb +22 -2
  129. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  130. data/lib/ibex/frontend/diagnostic.rb +1 -1
  131. data/lib/ibex/frontend/formatter.rb +72 -26
  132. data/lib/ibex/frontend/generated_parser.rb +147 -117
  133. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  134. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  135. data/lib/ibex/frontend/generation.rb +1 -1
  136. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  137. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  138. data/lib/ibex/frontend/regenerator.rb +1 -0
  139. data/lib/ibex/frontend/resolution.rb +2 -1
  140. data/lib/ibex/frontend/resolver.rb +1 -1
  141. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  142. data/lib/ibex/frontend/source_cursor.rb +2 -2
  143. data/lib/ibex/frontend/source_span.rb +9 -1
  144. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  145. data/lib/ibex/frontend/token_adapter.rb +10 -0
  146. data/lib/ibex/frontend.rb +1 -0
  147. data/lib/ibex/fuzz.rb +219 -0
  148. data/lib/ibex/generation_input.rb +1 -1
  149. data/lib/ibex/generation_manifest.rb +36 -25
  150. data/lib/ibex/generation_transaction.rb +1 -1
  151. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  152. data/lib/ibex/generation_transaction_validation.rb +2 -2
  153. data/lib/ibex/grammar_tests.rb +1 -1
  154. data/lib/ibex/ir/automaton_ir.rb +61 -19
  155. data/lib/ibex/ir/grammar_ir.rb +76 -45
  156. data/lib/ibex/ir/lexer_ir.rb +5 -5
  157. data/lib/ibex/ir/parser_contract.rb +135 -0
  158. data/lib/ibex/ir/serialize.rb +203 -76
  159. data/lib/ibex/ir/validator/automaton.rb +64 -34
  160. data/lib/ibex/ir/validator/base.rb +51 -33
  161. data/lib/ibex/ir/validator/grammar.rb +113 -83
  162. data/lib/ibex/ir/validator/lexer.rb +2 -2
  163. data/lib/ibex/ir/validator.rb +2 -1
  164. data/lib/ibex/ir.rb +16 -3
  165. data/lib/ibex/lalr/build_metrics.rb +50 -2
  166. data/lib/ibex/lalr/builder.rb +191 -56
  167. data/lib/ibex/lalr/conflict_search.rb +15 -4
  168. data/lib/ibex/lalr/counterexample.rb +19 -14
  169. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  170. data/lib/ibex/lalr/goto_follows.rb +229 -0
  171. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  172. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  173. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  174. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  175. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  176. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  177. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  178. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  179. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  180. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  181. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  182. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  183. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  184. data/lib/ibex/lalr.rb +52 -1
  185. data/lib/ibex/location.rb +3 -3
  186. data/lib/ibex/lsp/document_handlers.rb +9 -7
  187. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  188. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  189. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  190. data/lib/ibex/lsp/position_codec.rb +6 -2
  191. data/lib/ibex/lsp/request_handlers.rb +3 -2
  192. data/lib/ibex/lsp/request_support.rb +14 -7
  193. data/lib/ibex/lsp/server.rb +13 -11
  194. data/lib/ibex/lsp/symbol_index.rb +13 -13
  195. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  196. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  197. data/lib/ibex/lsp/transport.rb +13 -3
  198. data/lib/ibex/lsp/workspace.rb +5 -4
  199. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  200. data/lib/ibex/lsp.rb +1 -0
  201. data/lib/ibex/messages/en.yml +28 -0
  202. data/lib/ibex/messages/ja.yml +28 -0
  203. data/lib/ibex/messages.rb +68 -0
  204. data/lib/ibex/metrics.rb +175 -0
  205. data/lib/ibex/normalize/declarations.rb +2 -0
  206. data/lib/ibex/normalize/diagnostics.rb +63 -38
  207. data/lib/ibex/normalize/expander.rb +5 -4
  208. data/lib/ibex/normalize/expression.rb +8 -6
  209. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  210. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  211. data/lib/ibex/normalize/lexer.rb +3 -3
  212. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  213. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  214. data/lib/ibex/normalize/parameters.rb +31 -4
  215. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  216. data/lib/ibex/normalize.rb +10 -20
  217. data/lib/ibex/racc_migration/report.rb +6 -3
  218. data/lib/ibex/rake_task.rb +1 -1
  219. data/lib/ibex/samples.rb +43 -13
  220. data/lib/ibex/table_artifact/builder.rb +266 -0
  221. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  222. data/lib/ibex/table_artifact/document.rb +39 -0
  223. data/lib/ibex/table_artifact/executor.rb +187 -0
  224. data/lib/ibex/table_artifact/serializer.rb +57 -0
  225. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  226. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  227. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  228. data/lib/ibex/table_artifact/validator.rb +253 -0
  229. data/lib/ibex/table_artifact.rb +88 -0
  230. data/lib/ibex/table_simulation/result.rb +6 -2
  231. data/lib/ibex/table_simulation/step.rb +3 -1
  232. data/lib/ibex/tables.rb +14 -0
  233. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  234. data/lib/ibex/verification_report/builder.rb +159 -0
  235. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  236. data/lib/ibex/verification_report/logical_path.rb +68 -0
  237. data/lib/ibex/verification_report/validator.rb +329 -0
  238. data/lib/ibex/verification_report.rb +69 -0
  239. data/lib/ibex/verify/action_correspondence.rb +138 -0
  240. data/lib/ibex/verify/language_witness.rb +295 -0
  241. data/lib/ibex/verify/reference_collection.rb +190 -0
  242. data/lib/ibex/verify/result.rb +73 -0
  243. data/lib/ibex/verify/verifier.rb +598 -0
  244. data/lib/ibex/verify.rb +15 -0
  245. data/lib/ibex/version.rb +1 -1
  246. data/lib/ibex/watch/runner.rb +10 -1
  247. data/lib/ibex/watch/source_snapshot.rb +13 -8
  248. data/lib/ibex.rb +10 -0
  249. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  250. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  251. data/schema/bison-import-v1.schema.json +106 -0
  252. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  253. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  254. data/schema/construction-profile-v1.schema.json +779 -0
  255. data/schema/diff-v1.schema.json +41 -0
  256. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  257. data/schema/equiv-v1.schema.json +64 -0
  258. data/schema/error-ux-review-v1.schema.json +609 -0
  259. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  260. data/schema/error-ux-round2-v1.schema.json +519 -0
  261. data/schema/explain-v1.schema.json +63 -1
  262. data/schema/fix-v1.schema.json +64 -0
  263. data/schema/fix-v2.schema.json +66 -0
  264. data/schema/fix-v3.schema.json +101 -0
  265. data/schema/fuzz-regression-v1.schema.json +79 -0
  266. data/schema/fuzz-v1.schema.json +114 -0
  267. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  268. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  269. data/schema/grammar-ir.schema.json +271 -0
  270. data/schema/ielr-benchmark-v1.schema.json +85 -0
  271. data/schema/lexer-profile-v1.schema.json +491 -0
  272. data/schema/metrics-v1.schema.json +60 -0
  273. data/schema/reduce-v1.schema.json +22 -0
  274. data/schema/reduce-v2.schema.json +63 -0
  275. data/schema/table-artifact-v1.schema.json +377 -0
  276. data/schema/verification-report-v1.schema.json +222 -0
  277. data/schema/verify-v1.schema.json +42 -0
  278. data/sig/ibex/analysis/digraph.rbs +19 -0
  279. data/sig/ibex/bison_import/importer.rbs +140 -0
  280. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  281. data/sig/ibex/bison_import.rbs +95 -0
  282. data/sig/ibex/bounded_subprocess.rbs +76 -0
  283. data/sig/ibex/cli/ambiguity.rbs +6 -0
  284. data/sig/ibex/cli/analysis.rbs +43 -0
  285. data/sig/ibex/cli/bison_import.rbs +32 -0
  286. data/sig/ibex/cli/config.rbs +35 -0
  287. data/sig/ibex/cli/diagnostics.rbs +16 -3
  288. data/sig/ibex/cli/documentation.rbs +5 -1
  289. data/sig/ibex/cli/equiv.rbs +49 -0
  290. data/sig/ibex/cli/error_messages.rbs +6 -0
  291. data/sig/ibex/cli/explain.rbs +6 -0
  292. data/sig/ibex/cli/fix.rbs +53 -0
  293. data/sig/ibex/cli/formatting.rbs +5 -1
  294. data/sig/ibex/cli/fuzz.rbs +47 -0
  295. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  296. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  297. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  298. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  299. data/sig/ibex/cli/ir_tools.rbs +7 -19
  300. data/sig/ibex/cli/outputs.rbs +9 -3
  301. data/sig/ibex/cli/reduce.rbs +60 -0
  302. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  303. data/sig/ibex/cli/samples.rbs +8 -0
  304. data/sig/ibex/cli/verify.rbs +28 -0
  305. data/sig/ibex/cli/watch.rbs +2 -2
  306. data/sig/ibex/cli.rbs +79 -5
  307. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  308. data/sig/ibex/codegen/explain.rbs +45 -25
  309. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  310. data/sig/ibex/codegen/rbs.rbs +12 -10
  311. data/sig/ibex/codegen/report.rbs +12 -12
  312. data/sig/ibex/codegen/ruby.rbs +18 -15
  313. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  314. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  315. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  316. data/sig/ibex/configuration/explanation.rbs +195 -0
  317. data/sig/ibex/configuration/inspector.rbs +53 -0
  318. data/sig/ibex/configuration.rbs +242 -0
  319. data/sig/ibex/coverage/collector.rbs +16 -14
  320. data/sig/ibex/coverage/event_stream.rbs +12 -10
  321. data/sig/ibex/coverage/report.rbs +20 -18
  322. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  323. data/sig/ibex/delta_reducer.rbs +38 -0
  324. data/sig/ibex/diff.rbs +47 -0
  325. data/sig/ibex/equiv/machine.rbs +54 -0
  326. data/sig/ibex/equiv.rbs +129 -0
  327. data/sig/ibex/fix.rbs +138 -0
  328. data/sig/ibex/frontend/ast.rbs +33 -7
  329. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  330. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  331. data/sig/ibex/frontend/formatter.rbs +38 -30
  332. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  333. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  334. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  335. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  336. data/sig/ibex/frontend/resolution.rbs +3 -2
  337. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  338. data/sig/ibex/frontend/source_span.rbs +5 -2
  339. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  340. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  341. data/sig/ibex/fuzz.rbs +71 -0
  342. data/sig/ibex/generation_input.rbs +2 -2
  343. data/sig/ibex/generation_manifest.rbs +35 -32
  344. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  345. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  346. data/sig/ibex/grammar_tests.rbs +2 -2
  347. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  348. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  349. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  350. data/sig/ibex/ir/parser_contract.rbs +69 -0
  351. data/sig/ibex/ir/serialize.rbs +63 -28
  352. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  353. data/sig/ibex/ir/validator/base.rbs +35 -31
  354. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  355. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  356. data/sig/ibex/ir.rbs +9 -3
  357. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  358. data/sig/ibex/lalr/builder.rbs +75 -24
  359. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  360. data/sig/ibex/lalr/counterexample.rbs +17 -14
  361. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  362. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  363. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  364. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  365. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  366. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  367. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  368. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  369. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  370. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  371. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  372. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  373. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  374. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  375. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  376. data/sig/ibex/lalr.rbs +11 -1
  377. data/sig/ibex/location.rbs +6 -6
  378. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  379. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  380. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  381. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  382. data/sig/ibex/lsp/position_codec.rbs +8 -4
  383. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  384. data/sig/ibex/lsp/request_support.rbs +8 -8
  385. data/sig/ibex/lsp/server.rbs +16 -16
  386. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  387. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  388. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  389. data/sig/ibex/lsp/transport.rbs +18 -6
  390. data/sig/ibex/lsp/workspace.rbs +3 -2
  391. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  392. data/sig/ibex/messages.rbs +26 -0
  393. data/sig/ibex/metrics.rbs +50 -0
  394. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  395. data/sig/ibex/normalize/expander.rbs +2 -2
  396. data/sig/ibex/normalize/expression.rbs +10 -8
  397. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  398. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  399. data/sig/ibex/normalize/lexer.rbs +4 -4
  400. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  401. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  402. data/sig/ibex/normalize/parameters.rbs +13 -8
  403. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  404. data/sig/ibex/normalize.rbs +11 -5
  405. data/sig/ibex/racc_migration/report.rbs +8 -6
  406. data/sig/ibex/rake_task.rbs +1 -1
  407. data/sig/ibex/samples.rbs +13 -4
  408. data/sig/ibex/table_artifact/builder.rbs +93 -0
  409. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  410. data/sig/ibex/table_artifact/document.rbs +27 -0
  411. data/sig/ibex/table_artifact/executor.rbs +66 -0
  412. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  413. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  414. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  415. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  416. data/sig/ibex/table_artifact/validator.rbs +66 -0
  417. data/sig/ibex/table_artifact.rbs +32 -0
  418. data/sig/ibex/table_simulation/result.rbs +8 -4
  419. data/sig/ibex/table_simulation/step.rbs +4 -2
  420. data/sig/ibex/tables.rbs +10 -9
  421. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  422. data/sig/ibex/verification_report/builder.rbs +53 -0
  423. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  424. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  425. data/sig/ibex/verification_report/validator.rbs +114 -0
  426. data/sig/ibex/verification_report.rbs +44 -0
  427. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  428. data/sig/ibex/verify/language_witness.rbs +112 -0
  429. data/sig/ibex/verify/reference_collection.rbs +63 -0
  430. data/sig/ibex/verify/result.rbs +52 -0
  431. data/sig/ibex/verify/verifier.rbs +170 -0
  432. data/sig/ibex/verify.rbs +6 -0
  433. data/sig/ibex/watch/runner.rbs +12 -2
  434. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  435. metadata +232 -12
  436. data/lib/ibex/ir/migration.rb +0 -120
  437. data/sig/ibex/ir/migration.rbs +0 -34
@@ -0,0 +1,580 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require_relative "error"
6
+ require_relative "location"
7
+
8
+ module Ibex
9
+ # Typed, provenance-preserving effective configuration.
10
+ module Configuration
11
+ # @rbs!
12
+ # type config_value = Symbol | String | Integer | bool | nil
13
+ # type json_value = String | Integer | bool | nil | Array[json_value] | Hash[String, json_value]
14
+
15
+ OWNER_NAMES = {
16
+ grammar_contract: "grammar-contract",
17
+ grammar_minimum: "grammar-minimum",
18
+ project_build: "project-build",
19
+ invocation: "invocation"
20
+ }.freeze #: Hash[Symbol, String]
21
+ POLICIES = %i[fixed minimum build invocation].freeze #: Array[Symbol]
22
+ ORIGIN_KINDS = %i[builtin grammar project cli analysis_override].freeze #: Array[Symbol]
23
+ BOOLEAN_VALUES = [true, false].freeze #: Array[bool]
24
+ DECLARED_VALUE_UNSET = Object.new.freeze #: Object
25
+ OWNER_POLICIES = {
26
+ grammar_contract: :fixed, grammar_minimum: :minimum,
27
+ project_build: :build, invocation: :invocation
28
+ }.freeze #: Hash[Symbol, Symbol]
29
+
30
+ # One closed definition of a configuration concept and its value domain.
31
+ class Key
32
+ attr_reader :name #: String
33
+ attr_reader :type #: Symbol
34
+ attr_reader :default #: config_value
35
+ attr_reader :owner #: Symbol
36
+ attr_reader :policy #: Symbol
37
+ attr_reader :allowed_values #: Array[config_value]?
38
+ attr_reader :cli_option #: Symbol?
39
+
40
+ # @rbs (String name, type: Symbol, default: config_value, owner: Symbol, policy: Symbol,
41
+ # ?allowed_values: Array[config_value]?, ?cli_option: Symbol?) -> void
42
+ def initialize(name, type:, default:, owner:, policy:, allowed_values: nil, cli_option: nil)
43
+ validate_identity!(name, owner, policy)
44
+ validate_shape!(type, policy, cli_option)
45
+
46
+ @name = name.dup.freeze
47
+ @type = type
48
+ @owner = owner
49
+ @policy = policy
50
+ @allowed_values = allowed_values&.map { |value| immutable(value) }&.freeze
51
+ @cli_option = cli_option
52
+ @default = validate(default)
53
+ freeze
54
+ end
55
+
56
+ # Validate and defensively freeze a value from an adapter or declaration.
57
+ # @rbs (config_value value) -> config_value
58
+ def validate(value)
59
+ raise ArgumentError, "#{@name} expects #{@type}, got #{value.inspect}" unless valid_type?(value)
60
+
61
+ allowed_values = @allowed_values
62
+ if allowed_values && !allowed_values.include?(value)
63
+ raise ArgumentError, "#{@name} must be one of #{allowed_values.map(&:inspect).join(', ')}"
64
+ end
65
+
66
+ immutable(value)
67
+ end
68
+
69
+ # @rbs () -> String
70
+ def owner_name
71
+ OWNER_NAMES.fetch(@owner)
72
+ end
73
+
74
+ private
75
+
76
+ # @rbs (String name, Symbol owner, Symbol policy) -> void
77
+ def validate_identity!(name, owner, policy)
78
+ valid_name = name.is_a?(String) && name.match?(/\A[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+\z/)
79
+ raise ArgumentError, "invalid canonical configuration key: #{name.inspect}" unless valid_name
80
+ raise ArgumentError, "unknown configuration owner: #{owner.inspect}" unless OWNER_NAMES.key?(owner)
81
+ raise ArgumentError, "unknown configuration policy: #{policy.inspect}" unless POLICIES.include?(policy)
82
+ return if OWNER_POLICIES.fetch(owner) == policy
83
+
84
+ raise ArgumentError, "#{owner} configuration must use #{OWNER_POLICIES.fetch(owner)} policy"
85
+ end
86
+
87
+ # @rbs (Symbol type, Symbol policy, Symbol? cli_option) -> void
88
+ def validate_shape!(type, policy, cli_option)
89
+ raise ArgumentError, "cli_option must be a Symbol or nil" unless cli_option.nil? || cli_option.is_a?(Symbol)
90
+ return unless policy == :minimum && type != :integer
91
+
92
+ raise ArgumentError, "minimum configuration keys must have Integer values"
93
+ end
94
+
95
+ # @rbs (config_value value) -> config_value
96
+ def immutable(value)
97
+ value.is_a?(String) ? value.dup.freeze : value
98
+ end
99
+
100
+ # @rbs (config_value value) -> bool
101
+ def valid_type?(value)
102
+ case @type
103
+ when :symbol then value.is_a?(Symbol)
104
+ when :boolean then BOOLEAN_VALUES.include?(value)
105
+ when :integer then value.is_a?(Integer)
106
+ when :optional_boolean then value.nil? || BOOLEAN_VALUES.include?(value)
107
+ when :optional_string then value.nil? || value.is_a?(String)
108
+ else raise ArgumentError, "unknown configuration value type: #{@type.inspect}"
109
+ end
110
+ end
111
+ end
112
+
113
+ # Where a selected configuration value came from.
114
+ class Origin
115
+ attr_reader :kind #: Symbol
116
+ attr_reader :location #: Location?
117
+
118
+ # @rbs (Symbol kind, ?location: Location?) -> void
119
+ def initialize(kind, location: nil)
120
+ raise ArgumentError, "unknown configuration origin: #{kind.inspect}" unless ORIGIN_KINDS.include?(kind)
121
+ unless location.nil? || location.is_a?(Location)
122
+ raise ArgumentError, "configuration location must be an Ibex::Location"
123
+ end
124
+ if location && !%i[grammar project].include?(kind)
125
+ raise ArgumentError, "#{kind} configuration cannot have a source location"
126
+ end
127
+
128
+ @kind = kind
129
+ @location = location
130
+ freeze
131
+ end
132
+
133
+ # @rbs () -> Hash[String, json_value]
134
+ def to_h
135
+ result = { "kind" => @kind.to_s } #: Hash[String, json_value]
136
+ location = @location
137
+ result["location"] = stringify_location(location) if location
138
+ result
139
+ end
140
+
141
+ # @rbs () -> String
142
+ def label
143
+ location = @location
144
+ return @kind.to_s unless location
145
+
146
+ file = location.file || "(source)"
147
+ "#{@kind} #{file}:#{location.line}:#{location.column}"
148
+ end
149
+
150
+ private
151
+
152
+ # @rbs (Location location) -> Hash[String, json_value]
153
+ def stringify_location(location)
154
+ location.to_h.transform_keys(&:to_s)
155
+ end
156
+ end
157
+
158
+ # A typed effective value plus the evidence needed to explain it.
159
+ class Value
160
+ attr_reader :key #: Key
161
+ attr_reader :value #: config_value
162
+ attr_reader :origin #: Origin
163
+ attr_reader :explicit #: bool
164
+ attr_reader :canonical #: bool
165
+ attr_reader :declared_value #: config_value
166
+
167
+ # @rbs (Key key, config_value value, origin: Origin, explicit: bool, canonical: bool,
168
+ # ?declared_value: config_value | Object) -> void
169
+ def initialize(key, value, origin:, explicit:, canonical:, declared_value: DECLARED_VALUE_UNSET)
170
+ raise ArgumentError, "configuration value key must be a Configuration::Key" unless key.is_a?(Key)
171
+ raise ArgumentError, "configuration value origin must be a Configuration::Origin" unless origin.is_a?(Origin)
172
+ raise ArgumentError, "explicit must be boolean" unless BOOLEAN_VALUES.include?(explicit)
173
+ raise ArgumentError, "canonical must be boolean" unless BOOLEAN_VALUES.include?(canonical)
174
+
175
+ validate_provenance!(key, origin, explicit, canonical, declared_value)
176
+
177
+ @key = key
178
+ @value = key.validate(value)
179
+ @origin = origin
180
+ @explicit = explicit
181
+ @canonical = canonical
182
+ declared = declared_value #: config_value
183
+ @declared_value = declared.equal?(DECLARED_VALUE_UNSET) ? nil : key.validate(declared)
184
+
185
+ freeze
186
+ end
187
+
188
+ # @rbs () -> Hash[String, json_value]
189
+ def to_h
190
+ result = {
191
+ "key" => @key.name,
192
+ "value" => json_value(@value),
193
+ "owner" => @key.owner_name,
194
+ "policy" => @key.policy.to_s,
195
+ "origin" => @origin.to_h,
196
+ "explicit" => @explicit,
197
+ "canonical" => @canonical
198
+ } #: Hash[String, json_value]
199
+ unless @canonical
200
+ result["analysis"] = {
201
+ "declared" => json_value(@declared_value),
202
+ "selected" => json_value(@value),
203
+ "override" => true,
204
+ "canonical_generation" => false
205
+ }
206
+ end
207
+ result
208
+ end
209
+
210
+ private
211
+
212
+ # @rbs (Key key, Origin origin, bool explicit, bool canonical, config_value | Object declared_value) -> void
213
+ def validate_provenance!(key, origin, explicit, canonical, declared_value)
214
+ validate_canonical_origin!(key, origin, canonical)
215
+ validate_owner_source!(key, origin)
216
+ validate_explicitness!(origin, explicit)
217
+ validate_declared_evidence!(canonical, declared_value)
218
+ end
219
+
220
+ # @rbs (Key key, Origin origin, bool canonical) -> void
221
+ def validate_canonical_origin!(key, origin, canonical)
222
+ analysis_override = origin.kind == :analysis_override
223
+ unless analysis_override == !canonical
224
+ raise ArgumentError, "analysis_override origin must identify a noncanonical selection"
225
+ end
226
+ return if canonical || key.policy == :fixed
227
+
228
+ raise ArgumentError, "noncanonical selections are only valid for fixed configuration"
229
+ end
230
+
231
+ # @rbs (Key key, Origin origin) -> void
232
+ def validate_owner_source!(key, origin)
233
+ if origin.kind == :grammar && !%i[fixed minimum].include?(key.policy)
234
+ raise ArgumentError, "#{key.name} is #{key.policy} configuration and cannot be grammar-owned"
235
+ end
236
+ return unless origin.kind == :project && key.policy == :invocation
237
+
238
+ raise ArgumentError, "#{key.name} is invocation configuration and cannot be project-owned"
239
+ end
240
+
241
+ # @rbs (Origin origin, bool explicit) -> void
242
+ def validate_explicitness!(origin, explicit)
243
+ raise ArgumentError, "builtin configuration cannot be explicit" if origin.kind == :builtin && explicit
244
+ raise ArgumentError, "non-builtin configuration must be explicit" if origin.kind != :builtin && !explicit
245
+ end
246
+
247
+ # @rbs (bool canonical, config_value | Object declared_value) -> void
248
+ def validate_declared_evidence!(canonical, declared_value)
249
+ declared = !declared_value.equal?(DECLARED_VALUE_UNSET)
250
+ raise ArgumentError, "canonical selections cannot carry declared evidence" if canonical && declared
251
+ return if canonical || declared
252
+
253
+ raise ArgumentError, "noncanonical selections require declared evidence"
254
+ end
255
+
256
+ # @rbs (config_value value) -> (String | Integer | bool | nil)
257
+ def json_value(value)
258
+ value.is_a?(Symbol) ? value.to_s : value
259
+ end
260
+ end
261
+
262
+ # A fixed/minimum request that contradicts the declared contract.
263
+ class Conflict < Ibex::Error
264
+ attr_reader :key #: Key
265
+ attr_reader :declared #: Value
266
+ attr_reader :requested #: Value
267
+
268
+ # @rbs (Key key, Value declared, Value requested) -> void
269
+ def initialize(key, declared, requested)
270
+ @key = key
271
+ @declared = declared
272
+ @requested = requested
273
+ super(
274
+ "configuration conflict for #{key.name}: " \
275
+ "#{declared.origin.label} selected #{declared.value.inspect}, " \
276
+ "#{requested.origin.label} requested #{requested.value.inspect}"
277
+ )
278
+ end
279
+ end
280
+
281
+ # Closed definitions shared by generation, grammar tests, and analyses.
282
+ module Registry
283
+ DEFINITIONS = [
284
+ Key.new("grammar.mode", type: :symbol, default: :default, owner: :grammar_contract, policy: :fixed,
285
+ allowed_values: %i[default extended], cli_option: :mode),
286
+ Key.new("parser.algorithm", type: :symbol, default: :lalr, owner: :grammar_contract, policy: :fixed,
287
+ allowed_values: %i[slr lalr ielr lr1], cli_option: :algorithm),
288
+ Key.new("parser.entries", type: :symbol, default: :shared, owner: :grammar_contract, policy: :fixed,
289
+ allowed_values: %i[shared isolated], cli_option: :entry_isolation),
290
+ Key.new("cst.trivia", type: :symbol, default: :leading, owner: :grammar_contract, policy: :fixed,
291
+ allowed_values: %i[leading balanced drop], cli_option: :cst_trivia),
292
+ Key.new("parser.superclass", type: :optional_string, default: nil, owner: :grammar_contract, policy: :fixed,
293
+ cli_option: :superclass),
294
+ Key.new("actions.omit_calls", type: :optional_boolean, default: nil, owner: :grammar_contract,
295
+ policy: :fixed, cli_option: :omit_actions),
296
+ Key.new("table.representation", type: :symbol, default: :compact, owner: :project_build, policy: :build,
297
+ allowed_values: %i[plain compact], cli_option: :table),
298
+ Key.new("runtime.embedded", type: :boolean, default: false, owner: :project_build, policy: :build,
299
+ cli_option: :embedded),
300
+ Key.new("build.debug", type: :boolean, default: false, owner: :project_build, policy: :build,
301
+ cli_option: :debug),
302
+ Key.new("source.line_mapping", type: :symbol, default: :actions, owner: :project_build, policy: :build,
303
+ allowed_values: %i[actions all none], cli_option: :line_convert),
304
+ Key.new("build.frozen_strings", type: :boolean, default: false, owner: :project_build, policy: :build,
305
+ cli_option: :frozen),
306
+ Key.new("build.executable", type: :optional_string, default: nil, owner: :project_build, policy: :build,
307
+ cli_option: :executable)
308
+ ].freeze #: Array[Key]
309
+ BY_NAME = DEFINITIONS.to_h { |key| [key.name, key] }.freeze #: Hash[String, Key]
310
+
311
+ class << self
312
+ # @rbs (String name) -> Key
313
+ def fetch(name)
314
+ BY_NAME.fetch(name) { raise ArgumentError, "unknown configuration key: #{name.inspect}" }
315
+ end
316
+
317
+ # @rbs () -> Array[Key]
318
+ def keys
319
+ DEFINITIONS
320
+ end
321
+ end
322
+ end
323
+
324
+ # Applies fixed/minimum override algebra and exposes deterministic evidence.
325
+ class Resolver
326
+ attr_reader :values #: Array[Value]
327
+
328
+ # @rbs (?keys: Array[Key], ?grammar: Hash[String, config_value], ?project: Hash[String, config_value],
329
+ # ?cli: Hash[String, config_value], ?analysis_overrides: Hash[String, config_value],
330
+ # ?locations: Hash[Symbol, Hash[String, Location]]) -> void
331
+ def initialize(keys: Registry.keys, grammar: {}, project: {}, cli: {}, analysis_overrides: {}, locations: {})
332
+ @keys = keys.sort_by(&:name).freeze
333
+ @key_index = @keys.to_h { |key| [key.name, key] }.freeze
334
+ raise ArgumentError, "duplicate configuration key definition" unless @key_index.length == @keys.length
335
+
336
+ sources = { grammar: grammar, project: project, cli: cli, analysis_override: analysis_overrides }
337
+ sources.each { |kind, entries| validate_source!(kind, entries) }
338
+ validate_locations!(locations, sources)
339
+ @values = @keys.map do |key|
340
+ resolve_key(key, sources, locations)
341
+ end.freeze
342
+ @value_index = @values.to_h { |entry| [entry.key.name, entry] }.freeze
343
+ freeze
344
+ end
345
+
346
+ # @rbs (String name) -> Value
347
+ def fetch(name)
348
+ @value_index.fetch(name) { raise ArgumentError, "unknown configuration key: #{name.inspect}" }
349
+ end
350
+
351
+ # @rbs (String name) -> config_value
352
+ def value(name)
353
+ fetch(name).value
354
+ end
355
+
356
+ # @rbs () -> Hash[String, Array[Hash[String, json_value]]]
357
+ def to_h
358
+ { "configuration" => @values.map(&:to_h) }
359
+ end
360
+
361
+ # Deterministic JSON independent of caller hash insertion order.
362
+ # @rbs () -> String
363
+ def dump
364
+ "#{JSON.generate(to_h)}\n"
365
+ end
366
+
367
+ private
368
+
369
+ # @rbs (Symbol kind, Hash[String, config_value] entries) -> void
370
+ def validate_source!(kind, entries)
371
+ entries.each_key do |name|
372
+ raise ArgumentError, "unknown #{kind} configuration key: #{name.inspect}" unless @key_index.key?(name)
373
+ end
374
+ end
375
+
376
+ # @rbs (Hash[Symbol, Hash[String, Location]] locations,
377
+ # Hash[Symbol, Hash[String, config_value]] sources) -> void
378
+ def validate_locations!(locations, sources)
379
+ locations.each do |kind, entries|
380
+ unless %i[grammar project].include?(kind)
381
+ raise ArgumentError, "#{kind} configuration cannot have source locations"
382
+ end
383
+
384
+ entries.each do |name, location|
385
+ raise ArgumentError, "unknown #{kind} configuration location: #{name.inspect}" unless @key_index.key?(name)
386
+ raise ArgumentError, "configuration location must be an Ibex::Location" unless location.is_a?(Location)
387
+ next if sources.fetch(kind).key?(name)
388
+
389
+ raise ArgumentError, "configuration location for #{name} has no #{kind} value"
390
+ end
391
+ end
392
+ end
393
+
394
+ # @rbs (Key key, Hash[Symbol, Hash[String, config_value]] sources,
395
+ # Hash[Symbol, Hash[String, Location]] locations) -> Value
396
+ def resolve_key(key, sources, locations)
397
+ builtin = value_from(key, key.default, :builtin, locations, explicit: false)
398
+ selected = case key.policy
399
+ when :fixed then resolve_fixed(key, builtin, sources, locations)
400
+ when :minimum then resolve_minimum(key, builtin, sources, locations)
401
+ when :build, :invocation then resolve_override(key, builtin, sources, locations)
402
+ else raise ArgumentError, "unsupported configuration policy: #{key.policy.inspect}"
403
+ end
404
+ override = sources.fetch(:analysis_override)
405
+ return selected unless override.key?(key.name)
406
+ raise ArgumentError, "analysis overrides are only valid for fixed configuration" unless key.policy == :fixed
407
+ if key.validate(override.fetch(key.name)) == selected.value
408
+ raise ArgumentError, "analysis override for #{key.name} must differ from the canonical value"
409
+ end
410
+
411
+ selected_origin = origin(:analysis_override, key.name, locations)
412
+ Value.new(
413
+ key, override.fetch(key.name), origin: selected_origin,
414
+ explicit: true, canonical: false, declared_value: selected.value
415
+ )
416
+ end
417
+
418
+ # @rbs (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources,
419
+ # Hash[Symbol, Hash[String, Location]] locations) -> Value
420
+ def resolve_fixed(key, builtin, sources, locations)
421
+ grammar = source_value(key, :grammar, sources, locations)
422
+ declared = grammar || builtin
423
+ requested = %i[project cli].filter_map { |kind| source_value(key, kind, sources, locations) }
424
+ reject_fixed_conflicts!(key, grammar, declared, requested)
425
+
426
+ grammar || requested.last || builtin
427
+ end
428
+
429
+ # @rbs (Key key, Value? grammar, Value declared, Array[Value] requested) -> void
430
+ def reject_fixed_conflicts!(key, grammar, declared, requested)
431
+ if grammar
432
+ conflict = requested.find { |selection| selection.value != declared.value }
433
+ raise Conflict.new(key, declared, conflict) if conflict
434
+ elsif requested.map(&:value).uniq.length > 1
435
+ raise Conflict.new(key, requested.fetch(0), requested.fetch(1))
436
+ end
437
+ end
438
+
439
+ # @rbs (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources,
440
+ # Hash[Symbol, Hash[String, Location]] locations) -> Value
441
+ def resolve_minimum(key, builtin, sources, locations)
442
+ grammar = source_value(key, :grammar, sources, locations)
443
+ floor = grammar || builtin
444
+ selected = floor
445
+ %i[project cli].each do |kind|
446
+ request = source_value(key, kind, sources, locations)
447
+ next unless request
448
+
449
+ request_value = request.value #: Integer
450
+ floor_value = floor.value #: Integer
451
+ raise Conflict.new(key, floor, request) if request_value < floor_value
452
+
453
+ selected_value = selected.value #: Integer
454
+ selected = request if request_value > selected_value
455
+ end
456
+ selected
457
+ end
458
+
459
+ # @rbs (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources,
460
+ # Hash[Symbol, Hash[String, Location]] locations) -> Value
461
+ def resolve_override(key, builtin, sources, locations)
462
+ if sources.fetch(:grammar).key?(key.name)
463
+ raise ArgumentError, "#{key.name} is #{key.policy} configuration and cannot be grammar-owned"
464
+ end
465
+ if key.policy == :invocation && sources.fetch(:project).key?(key.name)
466
+ raise ArgumentError, "#{key.name} is invocation configuration and cannot be project-owned"
467
+ end
468
+
469
+ source_value(key, :cli, sources, locations) ||
470
+ source_value(key, :project, sources, locations) || builtin
471
+ end
472
+
473
+ # @rbs (Key key, Symbol kind, Hash[Symbol, Hash[String, config_value]] sources,
474
+ # Hash[Symbol, Hash[String, Location]] locations) -> Value?
475
+ def source_value(key, kind, sources, locations)
476
+ entries = sources.fetch(kind)
477
+ return unless entries.key?(key.name)
478
+
479
+ value_from(key, entries.fetch(key.name), kind, locations, explicit: true)
480
+ end
481
+
482
+ # @rbs (Key key, config_value raw, Symbol kind, Hash[Symbol, Hash[String, Location]] locations,
483
+ # explicit: bool) -> Value
484
+ def value_from(key, raw, kind, locations, explicit:)
485
+ Value.new(
486
+ key, raw, origin: origin(kind, key.name, locations), explicit: explicit, canonical: true
487
+ )
488
+ end
489
+
490
+ # @rbs (Symbol kind, String name, Hash[Symbol, Hash[String, Location]] locations) -> Origin
491
+ def origin(kind, name, locations)
492
+ Origin.new(kind, location: locations.dig(kind, name))
493
+ end
494
+ end
495
+
496
+ # Converts the existing internal CLI option hash into canonical concepts.
497
+ class CLIAdapter
498
+ # The adapter receives the legacy CLI option map, which also contains
499
+ # operation flags and list values outside the closed configuration domain.
500
+ # It validates and converts only declared configuration options.
501
+ # @rbs (Hash[Symbol, untyped] options, ?explicit_keys: Array[Symbol]) -> void
502
+ def initialize(options, explicit_keys: options.keys)
503
+ @options = options.to_h do |name, value|
504
+ [name, value.is_a?(String) ? value.dup.freeze : value]
505
+ end.freeze
506
+ @explicit_keys = explicit_keys.dup.freeze
507
+ end
508
+
509
+ # @rbs (?grammar: Hash[String, config_value], ?locations: Hash[String, Location]) -> Resolver
510
+ def resolve(grammar: {}, locations: {})
511
+ source_locations = {} #: Hash[Symbol, Hash[String, Location]]
512
+ source_locations[:grammar] = locations unless locations.empty?
513
+ Resolver.new(grammar: grammar, cli: configuration_values, locations: source_locations)
514
+ end
515
+
516
+ # Canonical configuration values selected by explicit legacy CLI options.
517
+ # Raw option spellings stop at this adapter boundary.
518
+ # @rbs () -> Hash[String, config_value]
519
+ def configuration_values
520
+ options = {} #: Hash[String, config_value]
521
+ Registry::DEFINITIONS.each do |key|
522
+ raw_name = key.cli_option
523
+ next unless raw_name && @explicit_keys.include?(raw_name) && @options.key?(raw_name)
524
+
525
+ options[key.name] = if raw_name == :line_convert
526
+ convert_line_mapping
527
+ else
528
+ convert(raw_name, @options.fetch(raw_name))
529
+ end
530
+ end
531
+ line_mapping = Registry.fetch("source.line_mapping")
532
+ if !options.key?(line_mapping.name) && @explicit_keys.include?(:line_convert_all) &&
533
+ @options.key?(:line_convert_all)
534
+ options[line_mapping.name] = convert_line_mapping
535
+ end
536
+ options
537
+ end
538
+
539
+ private
540
+
541
+ # @rbs (Symbol name, untyped value) -> config_value
542
+ def convert(name, value)
543
+ case name
544
+ when :entry_isolation then convert_entry_isolation(value)
545
+ when :cst_trivia then value == :attach ? :leading : value
546
+ else value
547
+ end
548
+ end
549
+
550
+ # @rbs (untyped value) -> config_value
551
+ def convert_entry_isolation(value)
552
+ return :isolated if value == true
553
+ return :shared if value == false
554
+
555
+ value
556
+ end
557
+
558
+ # @rbs () -> Symbol
559
+ def convert_line_mapping
560
+ line_convert = @options.fetch(:line_convert, true)
561
+ line_convert_all = @options.fetch(:line_convert_all, false)
562
+ unless BOOLEAN_VALUES.include?(line_convert)
563
+ raise ArgumentError, "line_convert expects boolean, got #{line_convert.inspect}"
564
+ end
565
+ unless BOOLEAN_VALUES.include?(line_convert_all)
566
+ raise ArgumentError, "line_convert_all expects boolean, got #{line_convert_all.inspect}"
567
+ end
568
+ if line_convert_all && !line_convert
569
+ raise ArgumentError, "line_convert_all=true conflicts with line_convert=false"
570
+ end
571
+ return :all if line_convert_all
572
+ return :none unless line_convert
573
+
574
+ :actions
575
+ end
576
+ end
577
+ end
578
+ end
579
+
580
+ require_relative "configuration/explanation"
@@ -5,6 +5,9 @@ module Ibex
5
5
  module Coverage
6
6
  # Strictly turns one or more complete parse sessions into a coverage report.
7
7
  class Collector
8
+ # @rbs!
9
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
10
+
8
11
  # @rbs @grammar_digest: String?
9
12
  # @rbs @table_format_version: Integer?
10
13
  # @rbs @state_count: Integer?
@@ -39,14 +42,19 @@ module Ibex
39
42
  collector.finish(source: path)
40
43
  end
41
44
 
42
- # @rbs (Hash[String, untyped] document, source: String, line: Integer) -> void
45
+ # @rbs (Hash[String, json_value] document, source: String, line: Integer) -> void
43
46
  def consume(document, source:, line:)
44
47
  event = document.fetch("event")
45
48
  sequence = document.fetch("sequence")
49
+ data = document.fetch("data")
50
+ unless event.is_a?(String) && data.is_a?(Hash) && data.keys.all?(String)
51
+ invalid(source, line, "event document contains invalid session data")
52
+ end
53
+ event_data = data #: Hash[String, json_value]
46
54
  if event == "start"
47
- start_session(document.fetch("data"), sequence, source, line)
55
+ start_session(event_data, sequence, source, line)
48
56
  else
49
- consume_session_event(event, document.fetch("data"), sequence, source, line)
57
+ consume_session_event(event, event_data, sequence, source, line)
50
58
  end
51
59
  @event_count += 1
52
60
  end
@@ -71,7 +79,7 @@ module Ibex
71
79
 
72
80
  private
73
81
 
74
- # @rbs (Hash[untyped, untyped] data, untyped sequence, String source, Integer line) -> void
82
+ # @rbs (Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void
75
83
  def start_session(data, sequence, source, line)
76
84
  invalid(source, line, "new start event before prior session ended") if @in_session
77
85
  invalid(source, line, "start event sequence must be 1") unless sequence == 1
@@ -85,7 +93,7 @@ module Ibex
85
93
  @expected_sequence = 2
86
94
  end
87
95
 
88
- # @rbs (String event, Hash[untyped, untyped] data, untyped sequence, String source, Integer line) -> void
96
+ # @rbs (String event, Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void
89
97
  def consume_session_event(event, data, sequence, source, line)
90
98
  invalid(source, line, "event appears outside a parse session") unless @in_session
91
99
  unless sequence == @expected_sequence
@@ -106,7 +114,7 @@ module Ibex
106
114
  end
107
115
  end
108
116
 
109
- # @rbs (Hash[untyped, untyped] data, String source, Integer line)
117
+ # @rbs (Hash[String, json_value] data, String source, Integer line)
110
118
  # -> [[String, Integer, Integer, Integer], Integer]
111
119
  def session_metadata(data, source, line)
112
120
  digest = data["grammar_digest"]
@@ -148,21 +156,23 @@ module Ibex
148
156
  invalid(source, line, e.message)
149
157
  end
150
158
 
151
- # @rbs (untyped id, String source, Integer line) -> void
159
+ # @rbs (json_value id, String source, Integer line) -> void
152
160
  def hit_state(id, source, line)
153
161
  total = required_metadata(@state_count)
154
162
  invalid(source, line, "state id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
155
- increment(@state_hits, id, "state", source, line)
163
+ state_id = id #: Integer
164
+ increment(@state_hits, state_id, "state", source, line)
156
165
  end
157
166
 
158
- # @rbs (untyped id, String source, Integer line) -> void
167
+ # @rbs (json_value id, String source, Integer line) -> void
159
168
  def hit_production(id, source, line)
160
169
  total = required_metadata(@production_count)
161
170
  invalid(source, line, "production id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
162
- increment(@production_hits, id, "production", source, line)
171
+ production_id = id #: Integer
172
+ increment(@production_hits, production_id, "production", source, line)
163
173
  end
164
174
 
165
- # @rbs (untyped id, Integer total) -> bool
175
+ # @rbs (json_value id, Integer total) -> bool
166
176
  def valid_id?(id, total)
167
177
  id.is_a?(Integer) && id >= 0 && id < total
168
178
  end