ibex 0.2.0 → 0.4.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 (461) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -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 +274 -0
  40. data/docs/development.md +196 -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 +1617 -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/impact.md +101 -0
  56. data/docs/ja/bison-import.md +51 -0
  57. data/docs/ja/stable-api.md +53 -0
  58. data/docs/lexer-construction-profile.md +109 -0
  59. data/docs/lexer-migration.md +5 -0
  60. data/docs/maturity.md +129 -0
  61. data/docs/project-site-strategy.md +54 -0
  62. data/docs/racc-migration-evidence.md +45 -0
  63. data/docs/racc-migration.md +11 -0
  64. data/docs/release-readiness.md +126 -68
  65. data/docs/repair-semantics.md +123 -0
  66. data/docs/runtime-abi-evolution.md +310 -0
  67. data/docs/stability.md +145 -34
  68. data/docs/status.md +43 -0
  69. data/docs/syntax-sessions.md +183 -0
  70. data/docs/table-artifact.md +125 -0
  71. data/docs/test-interactions.md +219 -0
  72. data/docs/verification-report.md +137 -0
  73. data/docs/verifier-trust-boundary.md +199 -0
  74. data/docs/workloads.md +132 -0
  75. data/lib/ibex/analysis/digraph.rb +128 -0
  76. data/lib/ibex/analysis/sets.rb +14 -0
  77. data/lib/ibex/analysis.rb +1 -0
  78. data/lib/ibex/bison_import/importer.rb +516 -0
  79. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  80. data/lib/ibex/bison_import.rb +200 -0
  81. data/lib/ibex/bounded_subprocess.rb +171 -0
  82. data/lib/ibex/cli/ambiguity.rb +14 -5
  83. data/lib/ibex/cli/analysis.rb +134 -0
  84. data/lib/ibex/cli/bison_import.rb +121 -0
  85. data/lib/ibex/cli/config.rb +128 -0
  86. data/lib/ibex/cli/diagnostics.rb +31 -11
  87. data/lib/ibex/cli/documentation.rb +11 -8
  88. data/lib/ibex/cli/equiv.rb +169 -0
  89. data/lib/ibex/cli/error_messages.rb +11 -4
  90. data/lib/ibex/cli/explain.rb +14 -5
  91. data/lib/ibex/cli/fix.rb +203 -0
  92. data/lib/ibex/cli/formatting.rb +11 -10
  93. data/lib/ibex/cli/fuzz.rb +200 -0
  94. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  95. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  96. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  97. data/lib/ibex/cli/grammar_tests.rb +32 -14
  98. data/lib/ibex/cli/impact.rb +393 -0
  99. data/lib/ibex/cli/ir_tools.rb +5 -61
  100. data/lib/ibex/cli/outputs.rb +60 -33
  101. data/lib/ibex/cli/reduce.rb +202 -0
  102. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  103. data/lib/ibex/cli/samples.rb +49 -23
  104. data/lib/ibex/cli/verify.rb +116 -0
  105. data/lib/ibex/cli/watch.rb +6 -5
  106. data/lib/ibex/cli.rb +362 -56
  107. data/lib/ibex/codegen/action_locations.rb +1 -1
  108. data/lib/ibex/codegen/action_method_source.rb +5 -6
  109. data/lib/ibex/codegen/action_source.rb +3 -2
  110. data/lib/ibex/codegen/ambiguity.rb +5 -4
  111. data/lib/ibex/codegen/explain.rb +123 -55
  112. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  113. data/lib/ibex/codegen/rbs.rb +15 -7
  114. data/lib/ibex/codegen/report.rb +25 -13
  115. data/lib/ibex/codegen/ruby.rb +28 -64
  116. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  117. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  118. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  119. data/lib/ibex/configuration/explanation.rb +421 -0
  120. data/lib/ibex/configuration/inspector.rb +232 -0
  121. data/lib/ibex/configuration.rb +580 -0
  122. data/lib/ibex/coverage/collector.rb +21 -11
  123. data/lib/ibex/coverage/event_stream.rb +13 -7
  124. data/lib/ibex/coverage/report.rb +26 -18
  125. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  126. data/lib/ibex/delta_reducer.rb +99 -0
  127. data/lib/ibex/diff.rb +141 -0
  128. data/lib/ibex/equiv/machine.rb +135 -0
  129. data/lib/ibex/equiv.rb +373 -0
  130. data/lib/ibex/fix.rb +557 -0
  131. data/lib/ibex/frontend/ast.rb +22 -2
  132. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  133. data/lib/ibex/frontend/diagnostic.rb +1 -1
  134. data/lib/ibex/frontend/formatter.rb +72 -26
  135. data/lib/ibex/frontend/generated_parser.rb +147 -117
  136. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  137. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  138. data/lib/ibex/frontend/generation.rb +1 -1
  139. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  140. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  141. data/lib/ibex/frontend/regenerator.rb +1 -0
  142. data/lib/ibex/frontend/resolution.rb +2 -1
  143. data/lib/ibex/frontend/resolver.rb +1 -1
  144. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  145. data/lib/ibex/frontend/source_cursor.rb +2 -2
  146. data/lib/ibex/frontend/source_span.rb +9 -1
  147. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  148. data/lib/ibex/frontend/token_adapter.rb +10 -0
  149. data/lib/ibex/frontend.rb +1 -0
  150. data/lib/ibex/fuzz.rb +219 -0
  151. data/lib/ibex/generation_input.rb +1 -1
  152. data/lib/ibex/generation_manifest.rb +36 -25
  153. data/lib/ibex/generation_transaction.rb +1 -1
  154. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  155. data/lib/ibex/generation_transaction_validation.rb +2 -2
  156. data/lib/ibex/grammar_tests.rb +1 -1
  157. data/lib/ibex/impact/action_impact.rb +148 -0
  158. data/lib/ibex/impact/automaton_impact.rb +57 -0
  159. data/lib/ibex/impact/coverage_impact.rb +53 -0
  160. data/lib/ibex/impact/graph.rb +132 -0
  161. data/lib/ibex/impact/propagation.rb +147 -0
  162. data/lib/ibex/impact/report.rb +273 -0
  163. data/lib/ibex/impact/seeds.rb +69 -0
  164. data/lib/ibex/impact/severity.rb +139 -0
  165. data/lib/ibex/impact.rb +16 -0
  166. data/lib/ibex/ir/automaton_ir.rb +61 -19
  167. data/lib/ibex/ir/grammar_ir.rb +76 -45
  168. data/lib/ibex/ir/lexer_ir.rb +5 -5
  169. data/lib/ibex/ir/parser_contract.rb +135 -0
  170. data/lib/ibex/ir/serialize.rb +203 -76
  171. data/lib/ibex/ir/validator/automaton.rb +64 -34
  172. data/lib/ibex/ir/validator/base.rb +51 -33
  173. data/lib/ibex/ir/validator/grammar.rb +113 -83
  174. data/lib/ibex/ir/validator/lexer.rb +2 -2
  175. data/lib/ibex/ir/validator.rb +2 -1
  176. data/lib/ibex/ir.rb +16 -3
  177. data/lib/ibex/lalr/build_metrics.rb +50 -2
  178. data/lib/ibex/lalr/builder.rb +191 -56
  179. data/lib/ibex/lalr/conflict_search.rb +15 -4
  180. data/lib/ibex/lalr/counterexample.rb +19 -14
  181. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  182. data/lib/ibex/lalr/goto_follows.rb +229 -0
  183. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  184. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  185. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  186. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  187. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  188. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  189. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  190. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  191. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  192. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  193. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  194. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  195. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  196. data/lib/ibex/lalr.rb +52 -1
  197. data/lib/ibex/location.rb +3 -3
  198. data/lib/ibex/lsp/document_handlers.rb +9 -7
  199. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  200. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  201. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  202. data/lib/ibex/lsp/position_codec.rb +6 -2
  203. data/lib/ibex/lsp/request_handlers.rb +3 -2
  204. data/lib/ibex/lsp/request_support.rb +14 -7
  205. data/lib/ibex/lsp/server.rb +13 -11
  206. data/lib/ibex/lsp/symbol_index.rb +13 -13
  207. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  208. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  209. data/lib/ibex/lsp/transport.rb +13 -3
  210. data/lib/ibex/lsp/workspace.rb +5 -4
  211. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  212. data/lib/ibex/lsp.rb +1 -0
  213. data/lib/ibex/messages/en.yml +28 -0
  214. data/lib/ibex/messages/ja.yml +28 -0
  215. data/lib/ibex/messages.rb +68 -0
  216. data/lib/ibex/metrics.rb +175 -0
  217. data/lib/ibex/normalize/declarations.rb +2 -0
  218. data/lib/ibex/normalize/diagnostics.rb +63 -38
  219. data/lib/ibex/normalize/expander.rb +5 -4
  220. data/lib/ibex/normalize/expression.rb +8 -6
  221. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  222. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  223. data/lib/ibex/normalize/lexer.rb +3 -3
  224. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  225. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  226. data/lib/ibex/normalize/parameters.rb +31 -4
  227. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  228. data/lib/ibex/normalize.rb +10 -20
  229. data/lib/ibex/racc_migration/report.rb +6 -3
  230. data/lib/ibex/rake_task.rb +1 -1
  231. data/lib/ibex/samples.rb +43 -13
  232. data/lib/ibex/table_artifact/builder.rb +266 -0
  233. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  234. data/lib/ibex/table_artifact/document.rb +39 -0
  235. data/lib/ibex/table_artifact/executor.rb +187 -0
  236. data/lib/ibex/table_artifact/serializer.rb +57 -0
  237. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  238. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  239. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  240. data/lib/ibex/table_artifact/validator.rb +253 -0
  241. data/lib/ibex/table_artifact.rb +88 -0
  242. data/lib/ibex/table_simulation/result.rb +6 -2
  243. data/lib/ibex/table_simulation/step.rb +3 -1
  244. data/lib/ibex/tables.rb +14 -0
  245. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  246. data/lib/ibex/verification_report/builder.rb +159 -0
  247. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  248. data/lib/ibex/verification_report/logical_path.rb +68 -0
  249. data/lib/ibex/verification_report/validator.rb +329 -0
  250. data/lib/ibex/verification_report.rb +69 -0
  251. data/lib/ibex/verify/action_correspondence.rb +138 -0
  252. data/lib/ibex/verify/language_witness.rb +295 -0
  253. data/lib/ibex/verify/reference_collection.rb +190 -0
  254. data/lib/ibex/verify/result.rb +73 -0
  255. data/lib/ibex/verify/verifier.rb +598 -0
  256. data/lib/ibex/verify.rb +15 -0
  257. data/lib/ibex/version.rb +1 -1
  258. data/lib/ibex/watch/runner.rb +10 -1
  259. data/lib/ibex/watch/source_snapshot.rb +13 -8
  260. data/lib/ibex.rb +11 -0
  261. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  262. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  263. data/schema/bison-import-v1.schema.json +106 -0
  264. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  265. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  266. data/schema/construction-profile-v1.schema.json +779 -0
  267. data/schema/diff-v1.schema.json +41 -0
  268. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  269. data/schema/equiv-v1.schema.json +64 -0
  270. data/schema/error-ux-review-v1.schema.json +609 -0
  271. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  272. data/schema/error-ux-round2-v1.schema.json +519 -0
  273. data/schema/explain-v1.schema.json +63 -1
  274. data/schema/fix-v1.schema.json +64 -0
  275. data/schema/fix-v2.schema.json +66 -0
  276. data/schema/fix-v3.schema.json +101 -0
  277. data/schema/fuzz-regression-v1.schema.json +79 -0
  278. data/schema/fuzz-v1.schema.json +114 -0
  279. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  280. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  281. data/schema/grammar-ir.schema.json +271 -0
  282. data/schema/ielr-benchmark-v1.schema.json +85 -0
  283. data/schema/impact-v1.schema.json +208 -0
  284. data/schema/lexer-profile-v1.schema.json +491 -0
  285. data/schema/metrics-v1.schema.json +60 -0
  286. data/schema/reduce-v1.schema.json +22 -0
  287. data/schema/reduce-v2.schema.json +63 -0
  288. data/schema/table-artifact-v1.schema.json +377 -0
  289. data/schema/verification-report-v1.schema.json +222 -0
  290. data/schema/verify-v1.schema.json +42 -0
  291. data/sig/ibex/analysis/digraph.rbs +19 -0
  292. data/sig/ibex/analysis/sets.rbs +10 -0
  293. data/sig/ibex/bison_import/importer.rbs +140 -0
  294. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  295. data/sig/ibex/bison_import.rbs +95 -0
  296. data/sig/ibex/bounded_subprocess.rbs +76 -0
  297. data/sig/ibex/cli/ambiguity.rbs +6 -0
  298. data/sig/ibex/cli/analysis.rbs +43 -0
  299. data/sig/ibex/cli/bison_import.rbs +32 -0
  300. data/sig/ibex/cli/config.rbs +35 -0
  301. data/sig/ibex/cli/diagnostics.rbs +16 -3
  302. data/sig/ibex/cli/documentation.rbs +5 -1
  303. data/sig/ibex/cli/equiv.rbs +49 -0
  304. data/sig/ibex/cli/error_messages.rbs +6 -0
  305. data/sig/ibex/cli/explain.rbs +6 -0
  306. data/sig/ibex/cli/fix.rbs +53 -0
  307. data/sig/ibex/cli/formatting.rbs +5 -1
  308. data/sig/ibex/cli/fuzz.rbs +47 -0
  309. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  310. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  311. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  312. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  313. data/sig/ibex/cli/impact.rbs +95 -0
  314. data/sig/ibex/cli/ir_tools.rbs +7 -19
  315. data/sig/ibex/cli/outputs.rbs +9 -3
  316. data/sig/ibex/cli/reduce.rbs +60 -0
  317. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  318. data/sig/ibex/cli/samples.rbs +8 -0
  319. data/sig/ibex/cli/verify.rbs +28 -0
  320. data/sig/ibex/cli/watch.rbs +2 -2
  321. data/sig/ibex/cli.rbs +79 -5
  322. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  323. data/sig/ibex/codegen/explain.rbs +45 -25
  324. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  325. data/sig/ibex/codegen/rbs.rbs +12 -10
  326. data/sig/ibex/codegen/report.rbs +12 -12
  327. data/sig/ibex/codegen/ruby.rbs +18 -15
  328. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  329. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  330. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  331. data/sig/ibex/configuration/explanation.rbs +195 -0
  332. data/sig/ibex/configuration/inspector.rbs +53 -0
  333. data/sig/ibex/configuration.rbs +242 -0
  334. data/sig/ibex/coverage/collector.rbs +16 -14
  335. data/sig/ibex/coverage/event_stream.rbs +12 -10
  336. data/sig/ibex/coverage/report.rbs +20 -18
  337. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  338. data/sig/ibex/delta_reducer.rbs +38 -0
  339. data/sig/ibex/diff.rbs +47 -0
  340. data/sig/ibex/equiv/machine.rbs +54 -0
  341. data/sig/ibex/equiv.rbs +129 -0
  342. data/sig/ibex/fix.rbs +138 -0
  343. data/sig/ibex/frontend/ast.rbs +33 -7
  344. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  345. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  346. data/sig/ibex/frontend/formatter.rbs +38 -30
  347. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  348. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  349. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  350. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  351. data/sig/ibex/frontend/resolution.rbs +3 -2
  352. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  353. data/sig/ibex/frontend/source_span.rbs +5 -2
  354. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  355. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  356. data/sig/ibex/fuzz.rbs +71 -0
  357. data/sig/ibex/generation_input.rbs +2 -2
  358. data/sig/ibex/generation_manifest.rbs +35 -32
  359. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  360. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  361. data/sig/ibex/grammar_tests.rbs +2 -2
  362. data/sig/ibex/impact/action_impact.rbs +58 -0
  363. data/sig/ibex/impact/automaton_impact.rbs +37 -0
  364. data/sig/ibex/impact/coverage_impact.rbs +28 -0
  365. data/sig/ibex/impact/graph.rbs +67 -0
  366. data/sig/ibex/impact/propagation.rbs +56 -0
  367. data/sig/ibex/impact/report.rbs +107 -0
  368. data/sig/ibex/impact/seeds.rbs +36 -0
  369. data/sig/ibex/impact/severity.rbs +55 -0
  370. data/sig/ibex/impact.rbs +6 -0
  371. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  372. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  373. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  374. data/sig/ibex/ir/parser_contract.rbs +69 -0
  375. data/sig/ibex/ir/serialize.rbs +63 -28
  376. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  377. data/sig/ibex/ir/validator/base.rbs +35 -31
  378. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  379. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  380. data/sig/ibex/ir.rbs +9 -3
  381. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  382. data/sig/ibex/lalr/builder.rbs +75 -24
  383. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  384. data/sig/ibex/lalr/counterexample.rbs +17 -14
  385. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  386. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  387. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  388. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  389. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  390. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  391. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  392. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  393. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  394. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  395. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  396. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  397. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  398. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  399. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  400. data/sig/ibex/lalr.rbs +11 -1
  401. data/sig/ibex/location.rbs +6 -6
  402. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  403. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  404. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  405. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  406. data/sig/ibex/lsp/position_codec.rbs +8 -4
  407. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  408. data/sig/ibex/lsp/request_support.rbs +8 -8
  409. data/sig/ibex/lsp/server.rbs +16 -16
  410. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  411. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  412. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  413. data/sig/ibex/lsp/transport.rbs +18 -6
  414. data/sig/ibex/lsp/workspace.rbs +3 -2
  415. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  416. data/sig/ibex/messages.rbs +26 -0
  417. data/sig/ibex/metrics.rbs +50 -0
  418. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  419. data/sig/ibex/normalize/expander.rbs +2 -2
  420. data/sig/ibex/normalize/expression.rbs +10 -8
  421. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  422. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  423. data/sig/ibex/normalize/lexer.rbs +4 -4
  424. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  425. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  426. data/sig/ibex/normalize/parameters.rbs +13 -8
  427. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  428. data/sig/ibex/normalize.rbs +11 -5
  429. data/sig/ibex/racc_migration/report.rbs +8 -6
  430. data/sig/ibex/rake_task.rbs +1 -1
  431. data/sig/ibex/samples.rbs +13 -4
  432. data/sig/ibex/table_artifact/builder.rbs +93 -0
  433. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  434. data/sig/ibex/table_artifact/document.rbs +27 -0
  435. data/sig/ibex/table_artifact/executor.rbs +66 -0
  436. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  437. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  438. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  439. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  440. data/sig/ibex/table_artifact/validator.rbs +66 -0
  441. data/sig/ibex/table_artifact.rbs +32 -0
  442. data/sig/ibex/table_simulation/result.rbs +8 -4
  443. data/sig/ibex/table_simulation/step.rbs +4 -2
  444. data/sig/ibex/tables.rbs +10 -9
  445. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  446. data/sig/ibex/verification_report/builder.rbs +53 -0
  447. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  448. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  449. data/sig/ibex/verification_report/validator.rbs +114 -0
  450. data/sig/ibex/verification_report.rbs +44 -0
  451. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  452. data/sig/ibex/verify/language_witness.rbs +112 -0
  453. data/sig/ibex/verify/reference_collection.rbs +63 -0
  454. data/sig/ibex/verify/result.rbs +52 -0
  455. data/sig/ibex/verify/verifier.rbs +170 -0
  456. data/sig/ibex/verify.rbs +6 -0
  457. data/sig/ibex/watch/runner.rbs +12 -2
  458. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  459. metadata +254 -12
  460. data/lib/ibex/ir/migration.rb +0 -120
  461. data/sig/ibex/ir/migration.rbs +0 -34
@@ -0,0 +1,421 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Configuration
6
+ # One observed configuration source and how it participated in selection.
7
+ class Evidence
8
+ STATUSES = %i[accepted ignored duplicate conflicting].freeze #: Array[Symbol]
9
+ SOURCES = %i[grammar project cli].freeze #: Array[Symbol]
10
+
11
+ attr_reader :key #: Key
12
+ attr_reader :source #: Symbol
13
+ attr_reader :value #: config_value
14
+ attr_reader :status #: Symbol
15
+ attr_reader :location #: Location?
16
+ attr_reader :reason #: String
17
+
18
+ # @rbs (Key key, source: Symbol, value: config_value, status: Symbol, reason: String,
19
+ # ?location: Location?) -> void
20
+ def initialize(key, source:, value:, status:, reason:, location: nil)
21
+ raise ArgumentError, "configuration evidence key must be a Configuration::Key" unless key.is_a?(Key)
22
+ raise ArgumentError, "unknown configuration evidence source: #{source.inspect}" unless SOURCES.include?(source)
23
+ raise ArgumentError, "unknown configuration evidence status: #{status.inspect}" unless STATUSES.include?(status)
24
+ unless location.nil? || location.is_a?(Location)
25
+ raise ArgumentError, "configuration evidence location must be an Ibex::Location"
26
+ end
27
+ raise ArgumentError, "configuration evidence reason must not be empty" if reason.empty?
28
+
29
+ @key = key
30
+ @source = source
31
+ @value = key.validate(value)
32
+ @status = status
33
+ @location = location
34
+ @reason = reason.dup.freeze
35
+ freeze
36
+ end
37
+
38
+ # @rbs () -> Hash[String, json_value]
39
+ def to_h
40
+ result = {
41
+ "source" => @source.to_s,
42
+ "value" => json_value(@value),
43
+ "status" => @status.to_s,
44
+ "reason" => @reason
45
+ } #: Hash[String, json_value]
46
+ location = @location
47
+ result["location"] = location.to_h.transform_keys(&:to_s) if location
48
+ result
49
+ end
50
+
51
+ private
52
+
53
+ # @rbs (config_value value) -> (String | Integer | bool | nil)
54
+ def json_value(value)
55
+ value.is_a?(Symbol) ? value.to_s : value
56
+ end
57
+ end
58
+
59
+ # Whether an input can prove the historical source of a selected setting.
60
+ class Recording
61
+ STATES = %i[source recorded unspecified unavailable not_applicable].freeze #: Array[Symbol]
62
+
63
+ attr_reader :state #: Symbol
64
+ attr_reader :reason #: String
65
+
66
+ # @rbs (Symbol state, String reason) -> void
67
+ def initialize(state, reason)
68
+ raise ArgumentError, "unknown configuration recording state: #{state.inspect}" unless STATES.include?(state)
69
+ raise ArgumentError, "configuration recording reason must not be empty" if reason.empty?
70
+
71
+ @state = state
72
+ @reason = reason.dup.freeze
73
+ freeze
74
+ end
75
+
76
+ # @rbs () -> Hash[String, String]
77
+ def to_h
78
+ { "state" => @state.to_s.tr("_", "-"), "reason" => @reason }
79
+ end
80
+ end
81
+
82
+ # Canonical, static input facts used to build a configuration report.
83
+ class Input
84
+ KINDS = %i[grammar_source grammar_ir].freeze #: Array[Symbol]
85
+
86
+ attr_reader :kind #: Symbol
87
+ attr_reader :path #: String
88
+ attr_reader :files #: Array[String]
89
+ attr_reader :schema_version #: Integer?
90
+ attr_reader :grammar_values #: Hash[String, config_value]
91
+ attr_reader :grammar_locations #: Hash[String, Location]
92
+ attr_reader :evidence #: Hash[String, Array[Evidence]]
93
+ attr_reader :recordings #: Hash[String, Recording]
94
+
95
+ # @rbs (kind: Symbol, path: String, ?files: Array[String], ?schema_version: Integer?,
96
+ # ?grammar_values: Hash[String, config_value], ?grammar_locations: Hash[String, Location],
97
+ # ?evidence: Hash[String, Array[Evidence]], ?recordings: Hash[String, Recording]) -> void
98
+ def initialize(kind:, path:, files: [path], schema_version: nil, grammar_values: {}, grammar_locations: {},
99
+ evidence: {}, recordings: {})
100
+ raise ArgumentError, "unknown configuration input kind: #{kind.inspect}" unless KINDS.include?(kind)
101
+ unless path.is_a?(String) && !path.empty?
102
+ raise ArgumentError, "configuration input path must be a non-empty String"
103
+ end
104
+
105
+ validate_files!(files, path)
106
+ validate_schema_version!(kind, schema_version)
107
+ validate_entries!(grammar_values, grammar_locations, evidence, recordings)
108
+ assign_input(
109
+ kind, path, files, schema_version, grammar_values, grammar_locations, evidence, recordings
110
+ )
111
+ freeze
112
+ end
113
+
114
+ # @rbs () -> Hash[String, json_value]
115
+ def to_h
116
+ result = {
117
+ "kind" => @kind.to_s.tr("_", "-"),
118
+ "path" => @path,
119
+ "files" => @files
120
+ } #: Hash[String, json_value]
121
+ result["schema_version"] = @schema_version if @schema_version
122
+ result
123
+ end
124
+
125
+ private
126
+
127
+ # @rbs (Symbol kind, String path, Array[String] files, Integer? schema_version,
128
+ # Hash[String, config_value] grammar_values, Hash[String, Location] grammar_locations,
129
+ # Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
130
+ def assign_input(kind, path, files, schema_version, grammar_values, grammar_locations, evidence, recordings)
131
+ @kind = kind
132
+ @path = path.dup.freeze
133
+ @files = files.map { |file| file.dup.freeze }.freeze
134
+ @schema_version = schema_version
135
+ @grammar_values = immutable_values(grammar_values)
136
+ @grammar_locations = grammar_locations.to_h { |key, location| [key.dup.freeze, location] }.freeze
137
+ @evidence = evidence.to_h { |key, entries| [key.dup.freeze, entries.dup.freeze] }.freeze
138
+ @recordings = recordings.to_h { |key, recording| [key.dup.freeze, recording] }.freeze
139
+ end
140
+
141
+ # @rbs (Array[String] files, String path) -> void
142
+ def validate_files!(files, path)
143
+ unless files.is_a?(Array) && !files.empty? && files.all? { |file| file.is_a?(String) && !file.empty? }
144
+ raise ArgumentError, "configuration input files must be non-empty Strings"
145
+ end
146
+ raise ArgumentError, "configuration input files must be unique" unless files.uniq.length == files.length
147
+ raise ArgumentError, "configuration input path must be the first file" unless files.first == path
148
+ end
149
+
150
+ # @rbs (Symbol kind, Integer? schema_version) -> void
151
+ def validate_schema_version!(kind, schema_version)
152
+ if kind == :grammar_source
153
+ raise ArgumentError, "grammar source input cannot have a schema version" unless schema_version.nil?
154
+ elsif !schema_version.is_a?(Integer) || !schema_version.positive?
155
+ raise ArgumentError, "Grammar IR input requires a positive schema version"
156
+ end
157
+ end
158
+
159
+ # @rbs (Hash[String, config_value] values, Hash[String, Location] locations,
160
+ # Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
161
+ def validate_entries!(values, locations, evidence, recordings)
162
+ validate_fact_hashes!(values, locations, evidence, recordings)
163
+ names = values.keys | locations.keys | evidence.keys | recordings.keys
164
+ names.each do |name|
165
+ raise ArgumentError, "configuration input keys must be Strings" unless name.is_a?(String)
166
+
167
+ Registry.fetch(name)
168
+ end
169
+ validate_locations!(values, locations)
170
+ validate_evidence!(evidence)
171
+ validate_recordings!(recordings)
172
+ end
173
+
174
+ # @rbs (*Hash[Object?, Object?] values) -> void
175
+ def validate_fact_hashes!(*values)
176
+ return if values.all?(Hash)
177
+
178
+ raise ArgumentError, "configuration input facts must be Hash values"
179
+ end
180
+
181
+ # @rbs (Hash[String, config_value] values, Hash[String, Location] locations) -> void
182
+ def validate_locations!(values, locations)
183
+ locations.each do |name, location|
184
+ raise ArgumentError, "configuration location for #{name} has no grammar value" unless values.key?(name)
185
+ raise ArgumentError, "configuration location must be an Ibex::Location" unless location.is_a?(Location)
186
+ end
187
+ end
188
+
189
+ # @rbs (Hash[String, Array[Evidence]] evidence) -> void
190
+ def validate_evidence!(evidence)
191
+ evidence.each do |name, entries|
192
+ key = Registry.fetch(name)
193
+ unless entries.is_a?(Array) && entries.all? { |entry| entry.is_a?(Evidence) && entry.key.equal?(key) }
194
+ raise ArgumentError, "configuration evidence for #{name} has the wrong key"
195
+ end
196
+ end
197
+ end
198
+
199
+ # @rbs (Hash[String, Recording] recordings) -> void
200
+ def validate_recordings!(recordings)
201
+ recordings.each do |name, recording|
202
+ next if recording.is_a?(Recording)
203
+
204
+ raise ArgumentError, "configuration recording for #{name} must be a Recording"
205
+ end
206
+ end
207
+
208
+ # @rbs (Hash[String, config_value] values) -> Hash[String, config_value]
209
+ def immutable_values(values)
210
+ values.to_h do |name, value|
211
+ [name.dup.freeze, Registry.fetch(name).validate(value)]
212
+ end.freeze
213
+ end
214
+ end
215
+
216
+ # A fixed-contract mismatch retained as structured report data.
217
+ class ConflictRecord
218
+ attr_reader :key #: Key
219
+ attr_reader :declared #: Value
220
+ attr_reader :requested #: Value
221
+
222
+ # @rbs (Conflict conflict) -> void
223
+ def initialize(conflict)
224
+ raise ArgumentError, "conflict record requires a Configuration::Conflict" unless conflict.is_a?(Conflict)
225
+
226
+ @key = conflict.key
227
+ @declared = conflict.declared
228
+ @requested = conflict.requested
229
+ freeze
230
+ end
231
+
232
+ # @rbs () -> Hash[String, json_value]
233
+ def to_h
234
+ {
235
+ "key" => @key.name,
236
+ "policy" => @key.policy.to_s,
237
+ "declared" => selection(@declared),
238
+ "requested" => selection(@requested)
239
+ }
240
+ end
241
+
242
+ # @rbs () -> String
243
+ def message
244
+ location = @declared.origin.location
245
+ prefix = location ? "#{location.file || '(source)'}:#{location.line}:#{location.column}: " : "(config):1:1: "
246
+ "#{prefix}configuration conflict for #{@key.name}: " \
247
+ "#{@declared.origin.label} selected #{@declared.value.inspect}, " \
248
+ "#{@requested.origin.label} requested #{@requested.value.inspect}"
249
+ end
250
+
251
+ private
252
+
253
+ # @rbs (Value value) -> Hash[String, json_value]
254
+ def selection(value)
255
+ serialized = value.to_h
256
+ { "value" => serialized.fetch("value"), "origin" => serialized.fetch("origin") }
257
+ end
258
+ end
259
+
260
+ # The effective value and all accepted or rejected evidence for one key.
261
+ class Explanation
262
+ attr_reader :value #: Value
263
+ attr_reader :evidence #: Array[Evidence]
264
+ attr_reader :recording #: Recording
265
+
266
+ # @rbs (Value value, evidence: Array[Evidence], recording: Recording) -> void
267
+ def initialize(value, evidence:, recording:)
268
+ raise ArgumentError, "explanation value must be a Configuration::Value" unless value.is_a?(Value)
269
+ unless recording.is_a?(Recording)
270
+ raise ArgumentError,
271
+ "explanation recording must be a Configuration::Recording"
272
+ end
273
+ unless evidence.all? { |entry| entry.is_a?(Evidence) && entry.key.equal?(value.key) }
274
+ raise ArgumentError, "explanation evidence has the wrong key"
275
+ end
276
+
277
+ @value = value
278
+ @evidence = evidence.dup.freeze
279
+ @recording = recording
280
+ freeze
281
+ end
282
+
283
+ # @rbs () -> Hash[String, json_value]
284
+ def to_h
285
+ @value.to_h.merge(
286
+ "selection" => @value.explicit ? "explicit" : "implicit",
287
+ "conformance" => @value.canonical ? "canonical" : "noncanonical",
288
+ "recording" => @recording.to_h,
289
+ "evidence" => @evidence.map(&:to_h)
290
+ )
291
+ end
292
+ end
293
+
294
+ # Deterministic, static-no-user-code explanation of every registered setting.
295
+ class Report
296
+ attr_reader :input #: Input
297
+ attr_reader :explanations #: Array[Explanation]
298
+ attr_reader :conflicts #: Array[ConflictRecord]
299
+
300
+ # @rbs (Input input, ?cli: Hash[String, config_value]) -> void
301
+ def initialize(input, cli: {})
302
+ raise ArgumentError, "configuration report input must be a Configuration::Input" unless input.is_a?(Input)
303
+
304
+ cli.each_key { |name| Registry.fetch(name) }
305
+
306
+ @input = input
307
+ conflicts = [] #: Array[ConflictRecord]
308
+ @explanations = Registry.keys.sort_by(&:name).map do |key|
309
+ value = resolve_key(key, cli)
310
+ explanation(value, cli)
311
+ rescue Conflict => e
312
+ record = ConflictRecord.new(e)
313
+ conflicts << record
314
+ explanation(e.declared, cli, conflict: record)
315
+ end.freeze
316
+ @conflicts = conflicts.freeze
317
+ freeze
318
+ end
319
+
320
+ # @rbs () -> bool
321
+ def success?
322
+ @conflicts.empty?
323
+ end
324
+
325
+ # @rbs () -> Hash[String, json_value]
326
+ def to_h
327
+ {
328
+ "ibex_report" => "configuration",
329
+ "schema_version" => 1,
330
+ "trust" => "static-no-user-code",
331
+ "status" => success? ? "ok" : "conflict",
332
+ "input" => @input.to_h,
333
+ "configuration" => @explanations.map(&:to_h),
334
+ "conflicts" => @conflicts.map(&:to_h)
335
+ }
336
+ end
337
+
338
+ # Deterministic JSON independent of caller hash insertion order.
339
+ # @rbs () -> String
340
+ def dump
341
+ "#{JSON.generate(to_h)}\n"
342
+ end
343
+
344
+ # @rbs () -> String
345
+ def render_text
346
+ lines = ["configuration status=#{success? ? 'ok' : 'conflict'} (static-no-user-code): #{@input.path}"]
347
+ @explanations.each { |entry| lines.concat(render_explanation(entry)) }
348
+ @conflicts.each { |conflict| lines << "conflict: #{conflict.message}" }
349
+ "#{lines.join("\n")}\n"
350
+ end
351
+
352
+ private
353
+
354
+ # @rbs (Key key, Hash[String, config_value] cli) -> Value
355
+ def resolve_key(key, cli)
356
+ name = key.name
357
+ grammar = {} #: Hash[String, config_value]
358
+ grammar[name] = @input.grammar_values.fetch(name) if @input.grammar_values.key?(name)
359
+ request = {} #: Hash[String, config_value]
360
+ request[name] = cli.fetch(name) if cli.key?(name)
361
+ locations = {} #: Hash[Symbol, Hash[String, Location]]
362
+ locations[:grammar] = { name => @input.grammar_locations.fetch(name) } \
363
+ if @input.grammar_locations.key?(name)
364
+ Resolver.new(keys: [key], grammar: grammar, cli: request, locations: locations).values.fetch(0)
365
+ end
366
+
367
+ # @rbs (Value value, Hash[String, config_value] cli, ?conflict: ConflictRecord?) -> Explanation
368
+ def explanation(value, cli, conflict: nil)
369
+ key = value.key
370
+ evidence = @input.evidence.fetch(key.name, []).dup
371
+ evidence << cli_evidence(key, cli.fetch(key.name), conflicting: !conflict.nil?) if cli.key?(key.name)
372
+ recording = @input.recordings.fetch(
373
+ key.name,
374
+ Recording.new(:not_applicable, "this setting has no historical source contract in this input")
375
+ )
376
+ Explanation.new(value, evidence: evidence, recording: recording)
377
+ end
378
+
379
+ # @rbs (Key key, config_value raw, conflicting: bool) -> Evidence
380
+ def cli_evidence(key, raw, conflicting:)
381
+ value = key.validate(raw)
382
+ grammar = @input.grammar_values
383
+ status = conflicting ? :conflicting : :accepted
384
+ reason = if conflicting
385
+ "conflicts with the fixed grammar contract and was not selected"
386
+ elsif grammar.key?(key.name) && grammar.fetch(key.name) == value
387
+ "matches the fixed grammar contract"
388
+ else
389
+ "selected canonical command-line request"
390
+ end
391
+ Evidence.new(key, source: :cli, value: value, status: status, reason: reason)
392
+ end
393
+
394
+ # @rbs (config_value value) -> String
395
+ def display(value)
396
+ value.nil? ? "null" : value.to_s
397
+ end
398
+
399
+ # @rbs (Location location) -> String
400
+ def location_label(location)
401
+ "#{location.file || '(source)'}:#{location.line}:#{location.column}"
402
+ end
403
+
404
+ # @rbs (Explanation entry) -> Array[String]
405
+ def render_explanation(entry)
406
+ value = entry.value
407
+ lines = [
408
+ "#{value.key.name}=#{display(value.value)} owner=#{value.key.owner_name} " \
409
+ "origin=#{value.origin.label} policy=#{value.key.policy} " \
410
+ "#{value.explicit ? 'explicit' : 'implicit'} #{value.canonical ? 'canonical' : 'noncanonical'}",
411
+ " recording=#{entry.recording.state}: #{entry.recording.reason}"
412
+ ]
413
+ entry.evidence.each do |evidence|
414
+ location = evidence.location ? " at #{location_label(evidence.location)}" : ""
415
+ lines << " #{evidence.status} #{evidence.source}=#{display(evidence.value)}#{location}: #{evidence.reason}"
416
+ end
417
+ lines
418
+ end
419
+ end
420
+ end
421
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "../frontend/ast"
5
+ require_relative "../frontend/resolution"
6
+ require_relative "../ir/grammar_ir"
7
+ require_relative "explanation"
8
+
9
+ module Ibex
10
+ module Configuration
11
+ # Converts parsed source or validated Grammar IR into static configuration facts.
12
+ # rubocop:disable Metrics/ModuleLength -- source and IR adapters share one closed evidence vocabulary.
13
+ module Inspector
14
+ # @rbs (Frontend::Resolution resolution) -> Input
15
+ def from_source(resolution)
16
+ unless resolution.is_a?(Frontend::Resolution)
17
+ raise ArgumentError, "source configuration inspection requires a Frontend::Resolution"
18
+ end
19
+
20
+ values = {} #: Hash[String, config_value]
21
+ locations = {} #: Hash[String, Location]
22
+ evidence = {} #: Hash[String, Array[Evidence]]
23
+ root = resolution.root
24
+ extended_loc = root.extended_loc
25
+ add_source_fact(values, locations, evidence, "grammar.mode", :extended, extended_loc) if extended_loc
26
+ add_source_fact(values, locations, evidence, "parser.superclass", root.superclass, root.loc) if root.superclass
27
+ add_source_options(root, values, locations, evidence)
28
+ add_source_parser_contract(root, values, locations, evidence)
29
+ recordings = Registry.keys.to_h do |key|
30
+ [key.name,
31
+ Recording.new(:source, "the grammar source and contained import closure were inspected statically")]
32
+ end
33
+ Input.new(
34
+ kind: :grammar_source, path: resolution.root_path, files: resolution.files,
35
+ grammar_values: values, grammar_locations: locations, evidence: evidence, recordings: recordings
36
+ )
37
+ end
38
+ module_function :from_source
39
+
40
+ # @rbs (IR::Grammar grammar, path: String) -> Input
41
+ def from_grammar_ir(grammar, path:)
42
+ raise ArgumentError, "configuration inspection requires Grammar IR" unless grammar.is_a?(IR::Grammar)
43
+
44
+ values = {} #: Hash[String, config_value]
45
+ locations = {} #: Hash[String, Location]
46
+ evidence = {} #: Hash[String, Array[Evidence]]
47
+ recordings = Registry.keys.to_h do |key|
48
+ reason = "Grammar IR does not persist #{key.name}; " \
49
+ "current builtin or CLI selection is not historical evidence"
50
+ [key.name, Recording.new(:unavailable, reason)]
51
+ end
52
+ add_ir_fact(values, evidence, recordings, "grammar.mode", grammar.mode) if grammar.mode != :default
53
+ add_ir_fact(values, evidence, recordings, "parser.superclass", grammar.superclass) if grammar.superclass
54
+ add_ir_fact(values, evidence, recordings, "actions.omit_calls", grammar.options.fetch(:omit_action_call))
55
+ add_parser_contract(grammar, values, locations, evidence, recordings)
56
+ Input.new(
57
+ kind: :grammar_ir, path: path, schema_version: grammar.schema_version,
58
+ grammar_values: values, grammar_locations: locations, evidence: evidence, recordings: recordings
59
+ )
60
+ end
61
+ module_function :from_grammar_ir
62
+
63
+ # @rbs (Hash[String, config_value] values, Hash[String, Location] locations,
64
+ # Hash[String, Array[Evidence]] evidence, String name, config_value value,
65
+ # Frontend::Location | Location location) -> void
66
+ def add_source_fact(values, locations, evidence, name, value, location)
67
+ key = Registry.fetch(name)
68
+ location = configuration_location(location)
69
+ values[name] = value
70
+ locations[name] = location
71
+ evidence[name] = [
72
+ Evidence.new(
73
+ key, source: :grammar, value: value, status: :accepted, location: location,
74
+ reason: "selected by the grammar source"
75
+ )
76
+ ]
77
+ end
78
+ module_function :add_source_fact
79
+ private_class_method :add_source_fact
80
+
81
+ # @rbs (Frontend::AST::Root root, Hash[String, config_value] values, Hash[String, Location] locations,
82
+ # Hash[String, Array[Evidence]] evidence) -> void
83
+ def add_source_options(root, values, locations, evidence)
84
+ occurrences = root.declarations.filter_map do |declaration|
85
+ next unless declaration.is_a?(Frontend::AST::Options)
86
+
87
+ declaration.names.filter_map do |name|
88
+ value = option_value(name)
89
+ [value, configuration_location(declaration.loc)] unless value.nil?
90
+ end
91
+ end.flatten(1)
92
+ return if occurrences.empty?
93
+
94
+ name = "actions.omit_calls"
95
+ key = Registry.fetch(name)
96
+ selected_value, selected_location = occurrences.last
97
+ values[name] = selected_value
98
+ locations[name] = selected_location
99
+ evidence[name] = option_evidence(key, occurrences, selected_value)
100
+ end
101
+ module_function :add_source_options
102
+ private_class_method :add_source_options
103
+
104
+ # @rbs (Frontend::AST::Root root, Hash[String, config_value] values, Hash[String, Location] locations,
105
+ # Hash[String, Array[Evidence]] evidence) -> void
106
+ def add_source_parser_contract(root, values, locations, evidence)
107
+ declaration = root.declarations.grep(Frontend::AST::ParserConfiguration).first
108
+ return unless declaration.is_a?(Frontend::AST::ParserConfiguration)
109
+
110
+ declaration.settings.each do |setting|
111
+ key = setting.key == :cst_trivia ? "cst.trivia" : "parser.#{setting.key}"
112
+ add_source_fact(
113
+ values, locations, evidence, key, setting.value, setting.loc
114
+ )
115
+ end
116
+ end
117
+ module_function :add_source_parser_contract
118
+ private_class_method :add_source_parser_contract
119
+
120
+ # @rbs (String name) -> bool?
121
+ def option_value(name)
122
+ return true if name == "omit_action_call"
123
+ return false if name == "no_omit_action_call"
124
+
125
+ nil
126
+ end
127
+ module_function :option_value
128
+ private_class_method :option_value
129
+
130
+ # @rbs (Frontend::Location | Location location) -> Location
131
+ def configuration_location(location)
132
+ return location if location.is_a?(Ibex::Location)
133
+
134
+ Ibex::Location.new(file: location.file, line: location.line, column: location.column)
135
+ end
136
+ module_function :configuration_location
137
+ private_class_method :configuration_location
138
+
139
+ # @rbs (Key key, Array[[bool, Location]] occurrences, bool selected_value) -> Array[Evidence]
140
+ def option_evidence(key, occurrences, selected_value)
141
+ occurrences.each_with_index.map do |(value, location), index|
142
+ last = index == occurrences.length - 1
143
+ status = if last
144
+ :accepted
145
+ else
146
+ (value == selected_value ? :duplicate : :conflicting)
147
+ end
148
+ reason = if last
149
+ "last compatible options declaration selects the effective value"
150
+ elsif value == selected_value
151
+ "later identical source evidence selects the same value"
152
+ else
153
+ "later compatible source evidence supersedes this value"
154
+ end
155
+ Evidence.new(key, source: :grammar, value: value, status: status, location: location, reason: reason)
156
+ end
157
+ end
158
+ module_function :option_evidence
159
+ private_class_method :option_evidence
160
+
161
+ # @rbs (Hash[String, config_value] values, Hash[String, Array[Evidence]] evidence,
162
+ # Hash[String, Recording] recordings, String name, config_value value) -> void
163
+ def add_ir_fact(values, evidence, recordings, name, value)
164
+ key = Registry.fetch(name)
165
+ values[name] = value
166
+ evidence[name] = [
167
+ Evidence.new(
168
+ key, source: :grammar, value: value, status: :accepted,
169
+ reason: "selected by persisted Grammar IR configuration; original declaration location is unavailable"
170
+ )
171
+ ]
172
+ recordings[name] = Recording.new(
173
+ :recorded, "Grammar IR records the effective value but not the original declaration location"
174
+ )
175
+ end
176
+ module_function :add_ir_fact
177
+ private_class_method :add_ir_fact
178
+
179
+ # @rbs (IR::Grammar grammar, Hash[String, config_value] values, Hash[String, Location] locations,
180
+ # Hash[String, Array[Evidence]] evidence, Hash[String, Recording] recordings) -> void
181
+ def add_parser_contract(grammar, values, locations, evidence, recordings)
182
+ contract = grammar.parser_contract
183
+ contract_entries(contract).each do |name, entry|
184
+ add_contract_entry(name, entry, values, locations, evidence, recordings)
185
+ end
186
+ end
187
+ module_function :add_parser_contract
188
+ private_class_method :add_parser_contract
189
+
190
+ # @rbs (String name, IR::ParserContract::Entry entry, Hash[String, config_value] values,
191
+ # Hash[String, Location] locations, Hash[String, Array[Evidence]] evidence,
192
+ # Hash[String, Recording] recordings) -> void
193
+ def add_contract_entry(name, entry, values, locations, evidence, recordings)
194
+ unless entry.explicit
195
+ recordings[name] = Recording.new(
196
+ :unspecified,
197
+ "The current Grammar IR marks this parser contract field unspecified; " \
198
+ "the current builtin or CLI value is not a historical fact"
199
+ )
200
+ return
201
+ end
202
+
203
+ value = entry.value || raise("explicit parser contract entry has no value")
204
+ location = entry.location || raise("explicit parser contract entry has no location")
205
+ key = Registry.fetch(name)
206
+ values[name] = value
207
+ locations[name] = location
208
+ evidence[name] = [
209
+ Evidence.new(
210
+ key, source: :grammar, value: value, status: :accepted, location: location,
211
+ reason: "selected by the current Grammar IR parser contract"
212
+ )
213
+ ]
214
+ recordings[name] = Recording.new(:recorded, "The current Grammar IR records an explicit parser contract")
215
+ end
216
+ module_function :add_contract_entry
217
+ private_class_method :add_contract_entry
218
+
219
+ # @rbs (IR::ParserContract contract) -> Hash[String, IR::ParserContract::Entry]
220
+ def contract_entries(contract)
221
+ {
222
+ "parser.algorithm" => contract.algorithm,
223
+ "parser.entries" => contract.entries,
224
+ "cst.trivia" => contract.cst_trivia
225
+ }
226
+ end
227
+ module_function :contract_entries
228
+ private_class_method :contract_entries
229
+ end
230
+ # rubocop:enable Metrics/ModuleLength
231
+ end
232
+ end