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
@@ -10,6 +10,9 @@ module Ibex
10
10
  module Coverage
11
11
  # Bounded reader for the public runtime-event JSON Lines protocol.
12
12
  module EventStream
13
+ # @rbs!
14
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
15
+
13
16
  # @rbs!
14
17
  # interface _CoverageEventInput
15
18
  # def gets: (Integer) -> String?
@@ -21,13 +24,13 @@ module Ibex
21
24
  EVENT_TYPES = %w[start shift reduce error recover discard accept reject].freeze #: Array[String]
22
25
 
23
26
  class << self
24
- # @rbs (String path) { (Hash[String, untyped], Integer) -> void } -> void
27
+ # @rbs (String path) { (Hash[String, json_value], Integer) -> void } -> void
25
28
  def each_file(path, &block)
26
29
  File.open(path, "rb") { |input| each_io(input, source: path, &block) }
27
30
  end
28
31
 
29
32
  # @rbs (_CoverageEventInput input, source: String)
30
- # { (Hash[String, untyped], Integer) -> void } -> void
33
+ # { (Hash[String, json_value], Integer) -> void } -> void
31
34
  def each_io(input, source:, &block)
32
35
  line_number = 0
33
36
  while (line = input.gets(MAX_LINE_BYTES + 1))
@@ -49,20 +52,23 @@ module Ibex
49
52
  raise_invalid(source, line_number, "event line exceeds #{MAX_LINE_BYTES} bytes")
50
53
  end
51
54
 
52
- # @rbs (String line, String source, Integer line_number) -> Hash[String, untyped]
55
+ # @rbs (String line, String source, Integer line_number) -> Hash[String, json_value]
53
56
  def parse_line(line, source, line_number)
54
57
  text = line.dup.force_encoding(Encoding::UTF_8)
55
58
  raise_invalid(source, line_number, "event line is not valid UTF-8") unless text.valid_encoding?
56
59
 
57
- value = JSON.parse(text, max_nesting: MAX_NESTING, allow_nan: false)
58
- return value if value.is_a?(Hash) && value.keys.all?(String)
60
+ value = JSON.parse(text, max_nesting: MAX_NESTING, allow_nan: false) #: json_value
61
+ if value.is_a?(Hash) && value.keys.all?(String)
62
+ document = value #: Hash[String, json_value]
63
+ return document
64
+ end
59
65
 
60
66
  raise_invalid(source, line_number, "event line must be a JSON object")
61
67
  rescue JSON::ParserError => e
62
68
  raise_invalid(source, line_number, "invalid event JSON: #{e.message}")
63
69
  end
64
70
 
65
- # @rbs (Hash[String, untyped] document, String source, Integer line_number) -> void
71
+ # @rbs (Hash[String, json_value] document, String source, Integer line_number) -> void
66
72
  def validate_envelope(document, source, line_number)
67
73
  unless document.keys.sort == ROOT_KEYS
68
74
  raise_invalid(source, line_number, "event object has unknown or missing fields")
@@ -82,7 +88,7 @@ module Ibex
82
88
  raise_invalid(source, line_number, "event data does not match the runtime-event v1 schema")
83
89
  end
84
90
 
85
- # @rbs (untyped value) -> bool
91
+ # @rbs (json_value value) -> bool
86
92
  def positive_integer?(value)
87
93
  value.is_a?(Integer) && value.positive?
88
94
  end
@@ -8,6 +8,9 @@ module Ibex
8
8
  module Coverage
9
9
  # Versioned, mergeable runtime coverage result.
10
10
  class Report
11
+ # @rbs!
12
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
13
+
11
14
  IDENTIFIER = "runtime-coverage" #: String
12
15
  SCHEMA_VERSION = 1 #: Integer
13
16
  MAX_DOCUMENT_BYTES = 16_777_216 #: Integer
@@ -46,7 +49,7 @@ module Ibex
46
49
  end
47
50
  # rubocop:enable Layout/LineLength
48
51
 
49
- # @rbs () -> Hash[String, untyped]
52
+ # @rbs () -> Hash[String, json_value]
50
53
  def to_h
51
54
  {
52
55
  "ibex_coverage" => IDENTIFIER,
@@ -61,7 +64,7 @@ module Ibex
61
64
  }
62
65
  end
63
66
 
64
- # @rbs (*untyped) -> String
67
+ # @rbs (*Object) -> String
65
68
  def to_json(*)
66
69
  "#{JSON.pretty_generate(to_h)}\n"
67
70
  end
@@ -87,7 +90,7 @@ module Ibex
87
90
  raise Ibex::Error, "#{path}:1:1: invalid coverage JSON: #{e.message}"
88
91
  end
89
92
 
90
- # @rbs (untyped value, source: String) -> Report
93
+ # @rbs (json_value value, source: String) -> Report
91
94
  def self.from_h(value, source:)
92
95
  document = string_hash(value, source, "coverage document")
93
96
  invalid(source, "coverage object has unknown or missing fields") unless document.keys.sort == ROOT_KEYS
@@ -97,15 +100,19 @@ module Ibex
97
100
  totals = string_hash(document["totals"], source, "coverage totals")
98
101
  invalid(source, "coverage totals have unknown or missing fields") unless totals.keys.sort == TOTAL_KEYS
99
102
 
100
- state_count = totals["states"]
101
- production_count = totals["productions"]
103
+ grammar_digest = document["grammar_digest"] #: String
104
+ table_format_version = document["table_format_version"] #: Integer
105
+ state_count = totals["states"] #: Integer
106
+ production_count = totals["productions"] #: Integer
107
+ sessions = document["sessions"] #: Integer
108
+ event_count = document["events"] #: Integer
102
109
  new(
103
- grammar_digest: document["grammar_digest"],
104
- table_format_version: document["table_format_version"],
110
+ grammar_digest: grammar_digest,
111
+ table_format_version: table_format_version,
105
112
  state_count: state_count,
106
113
  production_count: production_count,
107
- sessions: document["sessions"],
108
- event_count: document["events"],
114
+ sessions: sessions,
115
+ event_count: event_count,
109
116
  state_hits: parse_hits(document["state_hits"], state_count, source, "state"),
110
117
  production_hits: parse_hits(document["production_hits"], production_count, source, "production")
111
118
  )
@@ -133,22 +140,21 @@ module Ibex
133
140
 
134
141
  private
135
142
 
136
- # @rbs (untyped input) -> String
143
+ # @rbs (json_value input) -> String
137
144
  def validate_digest(input)
138
- valid = input.is_a?(String) && input.match?(/\Asha256:[0-9a-f]{64}\z/)
139
- return input.dup.freeze if valid
145
+ return input.dup.freeze if input.is_a?(String) && input.match?(/\Asha256:[0-9a-f]{64}\z/)
140
146
 
141
147
  raise ArgumentError, "grammar_digest must be a full lowercase SHA-256 digest"
142
148
  end
143
149
 
144
- # @rbs (untyped input, String name) -> Integer
150
+ # @rbs (json_value input, String name) -> Integer
145
151
  def positive_integer(input, name)
146
152
  return input if input.is_a?(Integer) && input.positive? && input <= MAX_COUNT
147
153
 
148
154
  raise ArgumentError, "#{name} must be a bounded positive integer"
149
155
  end
150
156
 
151
- # @rbs (untyped input, String name, minimum: Integer) -> Integer
157
+ # @rbs (json_value input, String name, minimum: Integer) -> Integer
152
158
  def total_integer(input, name, minimum:)
153
159
  return input if input.is_a?(Integer) && input >= minimum && input <= MAX_TOTAL
154
160
 
@@ -182,14 +188,16 @@ module Ibex
182
188
  class << self
183
189
  private
184
190
 
185
- # @rbs (untyped value, String source, String name) -> Hash[String, untyped]
191
+ # @rbs (json_value value, String source, String name) -> Hash[String, json_value]
186
192
  def string_hash(value, source, name)
187
- return value if value.is_a?(Hash) && value.keys.all?(String)
193
+ if value.is_a?(Hash) && value.keys.all?(String)
194
+ return value #: Hash[String, json_value]
195
+ end
188
196
 
189
197
  invalid(source, "#{name} must be an object")
190
198
  end
191
199
 
192
- # @rbs (untyped value, untyped total, String source, String kind) -> Hash[Integer, Integer]
200
+ # @rbs (json_value value, json_value total, String source, String kind) -> Hash[Integer, Integer]
193
201
  def parse_hits(value, total, source, kind)
194
202
  invalid(source, "#{kind}_hits must be an array") unless value.is_a?(Array)
195
203
  hits = {} #: Hash[Integer, Integer]
@@ -202,7 +210,7 @@ module Ibex
202
210
  hits
203
211
  end
204
212
 
205
- # @rbs (untyped entry, untyped total, Integer previous, String source, String kind) -> [Integer, Integer]
213
+ # @rbs (json_value entry, json_value total, Integer previous, String source, String kind) -> [Integer, Integer]
206
214
  def parse_hit(entry, total, previous, source, kind)
207
215
  hit = string_hash(entry, source, "#{kind} hit")
208
216
  invalid(source, "#{kind} hit has unknown or missing fields") unless hit.keys.sort == HIT_KEYS
@@ -5,6 +5,9 @@ module Ibex
5
5
  module Coverage
6
6
  # Dependency-free structural validation for runtime-event schema version 1.
7
7
  module RuntimeEventValidator
8
+ # @rbs!
9
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
10
+
8
11
  TOKEN_KEYS = %w[location state token token_id value].freeze #: Array[String]
9
12
  LOCATION_KEYS = %w[column end_column end_line file line].freeze #: Array[String]
10
13
  VALIDATORS = {
@@ -19,18 +22,23 @@ module Ibex
19
22
  }.freeze #: Hash[String, Symbol]
20
23
 
21
24
  class << self
22
- # @rbs (Hash[String, untyped] document) -> bool
25
+ # @rbs (Hash[String, json_value] document) -> bool
23
26
  def valid?(document)
24
27
  data = document["data"]
25
28
  return false unless data.is_a?(Hash) && data.keys.all?(String)
26
29
 
27
- validator = VALIDATORS[document["event"]]
28
- validator ? send(validator, data) : false
30
+ event_data = data #: Hash[String, json_value]
31
+
32
+ event = document["event"]
33
+ return false unless event.is_a?(String)
34
+
35
+ validator = VALIDATORS[event]
36
+ validator ? send(validator, event_data) : false
29
37
  end
30
38
 
31
39
  private
32
40
 
33
- # @rbs (Hash[String, untyped] data) -> bool
41
+ # @rbs (Hash[String, json_value] data) -> bool
34
42
  def valid_start?(data)
35
43
  return false unless exact_keys?(
36
44
  data,
@@ -45,45 +53,46 @@ module Ibex
45
53
  optional_string?(data["grammar_digest"])
46
54
  end
47
55
 
48
- # @rbs (Hash[String, untyped] data) -> bool
56
+ # @rbs (Hash[String, json_value] data) -> bool
49
57
  def valid_shift?(data)
50
58
  exact_keys?(data, TOKEN_KEYS + %w[from_state]) &&
51
59
  valid_token_fields?(data) &&
52
60
  integer?(data["from_state"])
53
61
  end
54
62
 
55
- # @rbs (Hash[String, untyped] data) -> bool
63
+ # @rbs (Hash[String, json_value] data) -> bool
56
64
  def valid_reduce?(data)
57
65
  keys = %w[goto_state lhs location post_state pre_state production_id result rhs_length]
66
+ rhs_length = data["rhs_length"]
58
67
  exact_keys?(data, keys) &&
59
68
  %w[goto_state lhs post_state pre_state production_id].all? { |key| integer?(data[key]) } &&
60
- integer?(data["rhs_length"]) && data["rhs_length"] >= 0 &&
69
+ rhs_length.is_a?(Integer) && rhs_length >= 0 &&
61
70
  summary?(data["result"]) && location?(data["location"])
62
71
  end
63
72
 
64
- # @rbs (Hash[String, untyped] data, reasons: Array[String]) -> bool
73
+ # @rbs (Hash[String, json_value] data, reasons: Array[String]) -> bool
65
74
  def valid_token_event?(data, reasons:)
66
75
  exact_keys?(data, TOKEN_KEYS + %w[reason]) &&
67
76
  valid_token_fields?(data) &&
68
77
  reasons.include?(data["reason"])
69
78
  end
70
79
 
71
- # @rbs (Hash[String, untyped] data) -> bool
80
+ # @rbs (Hash[String, json_value] data) -> bool
72
81
  def valid_error?(data)
73
82
  valid_token_event?(data, reasons: %w[syntax semantic])
74
83
  end
75
84
 
76
- # @rbs (Hash[String, untyped] data) -> bool
85
+ # @rbs (Hash[String, json_value] data) -> bool
77
86
  def valid_discard?(data)
78
87
  valid_token_event?(data, reasons: %w[recovery])
79
88
  end
80
89
 
81
- # @rbs (Hash[String, untyped] data) -> bool
90
+ # @rbs (Hash[String, json_value] data) -> bool
82
91
  def valid_reject?(data)
83
92
  valid_token_event?(data, reasons: %w[eof_during_recovery no_recovery_state])
84
93
  end
85
94
 
86
- # @rbs (Hash[String, untyped] data) -> bool
95
+ # @rbs (Hash[String, json_value] data) -> bool
87
96
  def valid_recover?(data)
88
97
  exact_keys?(data, TOKEN_KEYS + %w[from_state reason]) &&
89
98
  valid_token_fields?(data) &&
@@ -91,7 +100,7 @@ module Ibex
91
100
  %w[syntax semantic].include?(data["reason"])
92
101
  end
93
102
 
94
- # @rbs (Hash[String, untyped] data) -> bool
103
+ # @rbs (Hash[String, json_value] data) -> bool
95
104
  def valid_accept?(data)
96
105
  exact_keys?(data, %w[reason result state]) &&
97
106
  integer?(data["state"]) &&
@@ -99,7 +108,7 @@ module Ibex
99
108
  %w[table semantic].include?(data["reason"])
100
109
  end
101
110
 
102
- # @rbs (Hash[String, untyped] data) -> bool
111
+ # @rbs (Hash[String, json_value] data) -> bool
103
112
  def valid_token_fields?(data)
104
113
  integer?(data["state"]) &&
105
114
  optional_integer?(data["token_id"]) &&
@@ -108,7 +117,7 @@ module Ibex
108
117
  location?(data["location"])
109
118
  end
110
119
 
111
- # @rbs (untyped value) -> bool
120
+ # @rbs (json_value value) -> bool
112
121
  def summary?(value)
113
122
  case value
114
123
  when nil, true, false, Integer then true
@@ -118,7 +127,7 @@ module Ibex
118
127
  end
119
128
  end
120
129
 
121
- # @rbs (untyped value) -> bool
130
+ # @rbs (json_value value) -> bool
122
131
  def composite_summary?(value)
123
132
  return value.length <= 17 && value.all? { |entry| summary?(entry) } if value.is_a?(Array)
124
133
  return false unless value.is_a?(Hash)
@@ -126,7 +135,7 @@ module Ibex
126
135
  value.keys.all?(String) && value.length <= 17 && value.values.all? { |entry| summary?(entry) }
127
136
  end
128
137
 
129
- # @rbs (untyped value) -> bool
138
+ # @rbs (json_value value) -> bool
130
139
  def location?(value)
131
140
  return true if value.nil?
132
141
  return false unless value.is_a?(Hash) && value.keys.all?(String)
@@ -135,22 +144,22 @@ module Ibex
135
144
  (value.keys - LOCATION_KEYS).empty? && value.values.all? { |entry| summary?(entry) }
136
145
  end
137
146
 
138
- # @rbs (Hash[String, untyped] data, Array[String] keys) -> bool
147
+ # @rbs (Hash[String, json_value] data, Array[String] keys) -> bool
139
148
  def exact_keys?(data, keys)
140
149
  data.keys.sort == keys.sort
141
150
  end
142
151
 
143
- # @rbs (untyped value) -> bool
152
+ # @rbs (json_value value) -> bool
144
153
  def integer?(value)
145
154
  value.is_a?(Integer)
146
155
  end
147
156
 
148
- # @rbs (untyped value) -> bool
157
+ # @rbs (json_value value) -> bool
149
158
  def optional_integer?(value)
150
159
  value.nil? || integer?(value)
151
160
  end
152
161
 
153
- # @rbs (untyped value) -> bool
162
+ # @rbs (json_value value) -> bool
154
163
  def optional_string?(value)
155
164
  value.nil? || value.is_a?(String)
156
165
  end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ # Deterministic, trial-bounded delta debugging over an ordered sequence.
6
+ class DeltaReducer
7
+ # Immutable outcome of a bounded reduction.
8
+ class Result
9
+ attr_reader :items #: Array[untyped]
10
+ attr_reader :trials #: Integer
11
+ attr_reader :complete #: bool
12
+ attr_reader :original_size #: Integer
13
+
14
+ # @rbs (items: Array[untyped], trials: Integer, complete: bool, original_size: Integer) -> void
15
+ def initialize(items:, trials:, complete:, original_size:)
16
+ @items = items.freeze
17
+ @trials = trials
18
+ @complete = complete
19
+ @original_size = original_size
20
+ freeze
21
+ end
22
+ end
23
+
24
+ # @rbs (?max_trials: Integer) -> void
25
+ def initialize(max_trials: 1_000)
26
+ raise ArgumentError, "max_trials must be positive" unless max_trials.positive?
27
+
28
+ @max_trials = max_trials
29
+ end
30
+
31
+ # `failure` must return true while the failure of interest is preserved.
32
+ # @rbs [T] (Array[T] items) { (Array[T]) -> bool } -> Result
33
+ def minimize(items, &failure)
34
+ raise ArgumentError, "a failure predicate is required" unless failure
35
+
36
+ current = items.dup
37
+ original_size = current.length
38
+ trials = 0
39
+ trials = checked_trial!(trials)
40
+ unless failure.call(current.freeze)
41
+ raise Ibex::Error, "(reduce):1:1: original input does not reproduce the failure"
42
+ end
43
+
44
+ granularity = 2
45
+ while current.length >= 1
46
+ chunks = partitions(current.length, granularity)
47
+ reduced = false
48
+ chunks.each do |range|
49
+ return result(current, trials, false, original_size) if trials >= @max_trials
50
+
51
+ candidate = current.dup
52
+ candidate.slice!(range)
53
+ trials = checked_trial!(trials)
54
+ next unless failure.call(candidate.freeze)
55
+
56
+ current = candidate
57
+ granularity = [granularity - 1, 2].max
58
+ reduced = true
59
+ break
60
+ end
61
+ next if reduced
62
+ break if granularity >= current.length
63
+
64
+ granularity = [granularity * 2, current.length].min
65
+ end
66
+
67
+ result(current, trials, true, original_size)
68
+ end
69
+
70
+ private
71
+
72
+ # @rbs (Integer length, Integer count) -> Array[Range[Integer]]
73
+ def partitions(length, count)
74
+ width = (length.to_f / count).ceil
75
+ ranges = [] #: Array[Range[Integer]]
76
+ count.times do |index|
77
+ start = index * width
78
+ break if start >= length
79
+
80
+ ranges << (start...[start + width, length].min)
81
+ end
82
+ ranges
83
+ end
84
+
85
+ # @rbs (Integer trials) -> Integer
86
+ def checked_trial!(trials)
87
+ raise Ibex::Error, "(reduce):1:1: trial limit of #{@max_trials} is exhausted" if trials >= @max_trials
88
+
89
+ trials + 1
90
+ end
91
+
92
+ # @rbs (Array[untyped] items, Integer trials, bool complete, Integer original_size) -> Result
93
+ def result(items, trials, complete, original_size)
94
+ Result.new(
95
+ items: items.dup.freeze, trials: trials, complete: complete, original_size: original_size
96
+ ).freeze
97
+ end
98
+ end
99
+ end
data/lib/ibex/diff.rb ADDED
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ # Deterministic grammar and automaton change classification.
6
+ class Diff
7
+ # @rbs (IR::Automaton before, IR::Automaton after) -> void
8
+ def initialize(before, after)
9
+ @before = before
10
+ @after = after
11
+ end
12
+
13
+ # @rbs () -> Hash[Symbol, Object?]
14
+ def to_h
15
+ {
16
+ ibex_report: "diff", schema_version: 1,
17
+ symbols: classify(symbol_records(@before.grammar), symbol_records(@after.grammar)),
18
+ rules: classify(rule_records(@before.grammar), rule_records(@after.grammar)),
19
+ conflicts: classify(conflict_records(@before), conflict_records(@after)),
20
+ warnings: classify(warning_records(@before.grammar), warning_records(@after.grammar)),
21
+ metrics: numeric_metrics
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ # @rbs (Hash[String, Object?] before, Hash[String, Object?] after) -> Hash[Symbol, Object?]
28
+ def classify(before, after)
29
+ common = before.keys & after.keys
30
+ {
31
+ added: (after.keys - before.keys).sort.map { |key| { id: key, value: after.fetch(key) } },
32
+ removed: (before.keys - after.keys).sort.map { |key| { id: key, value: before.fetch(key) } },
33
+ changed: common.sort.filter_map do |key|
34
+ next if before.fetch(key) == after.fetch(key)
35
+
36
+ { id: key, before: before.fetch(key), after: after.fetch(key) }
37
+ end
38
+ }
39
+ end
40
+
41
+ # @rbs (IR::Grammar grammar) -> Hash[String, Object?]
42
+ def symbol_records(grammar)
43
+ grammar.symbols.to_h do |symbol|
44
+ [
45
+ symbol.name,
46
+ {
47
+ kind: symbol.kind, reserved: symbol.reserved, display_name: symbol.display_name,
48
+ semantic_type: symbol.semantic_type, precedence: symbol.precedence,
49
+ documentation: symbol.documentation
50
+ }
51
+ ]
52
+ end
53
+ end
54
+
55
+ # @rbs (IR::Grammar grammar) -> Hash[String, Object?]
56
+ def rule_records(grammar)
57
+ grammar.productions.group_by(&:lhs).to_h do |lhs, productions|
58
+ name = grammar.symbol_by_id(lhs)&.name || lhs.to_s
59
+ alternatives = productions.map { |production| production_record(grammar, production) }
60
+ [name, alternatives]
61
+ end
62
+ end
63
+
64
+ # @rbs (IR::Grammar grammar, IR::Production production) -> Hash[Symbol, Object?]
65
+ def production_record(grammar, production)
66
+ precedence = production.precedence_override
67
+ {
68
+ rhs: production.rhs.map { |id| symbol_name(grammar, id) },
69
+ precedence: precedence && symbol_name(grammar, precedence),
70
+ node: production.node && {
71
+ name: production.node.fetch(:name), fields: production.node.fetch(:fields)
72
+ }
73
+ }
74
+ end
75
+
76
+ # @rbs (IR::Automaton automaton) -> Hash[String, Object?]
77
+ def conflict_records(automaton)
78
+ grouped = Hash.new { |hash, key| hash[key] = [] } #: Hash[String, Array[Object?]]
79
+ automaton.states.each do |state|
80
+ state.conflicts.each do |conflict|
81
+ identity = conflict_identity(automaton.grammar, conflict)
82
+ grouped[identity] << conflict.fetch(:resolution)
83
+ end
84
+ end
85
+ grouped.transform_values { |resolutions| resolutions.sort_by(&:inspect) }
86
+ end
87
+
88
+ # @rbs (IR::Grammar grammar, IR::conflict conflict) -> String
89
+ def conflict_identity(grammar, conflict)
90
+ if conflict.fetch(:type).to_sym == :shift_reduce
91
+ shift_reduce = conflict #: IR::shift_reduce_conflict
92
+ reduction = production_shape(grammar, shift_reduce.fetch(:reduce))
93
+ "shift_reduce:#{shift_reduce.fetch(:symbol)}:#{reduction}"
94
+ else
95
+ reduce_reduce = conflict #: IR::reduce_reduce_conflict
96
+ reductions = reduce_reduce.fetch(:reductions).map { |id| production_shape(grammar, id) }.sort
97
+ "reduce_reduce:#{reduce_reduce.fetch(:symbol)}:#{reductions.join('|')}"
98
+ end
99
+ end
100
+
101
+ # @rbs (IR::Grammar grammar, Integer id) -> String
102
+ def production_shape(grammar, id)
103
+ production = grammar.productions.fetch(id)
104
+ lhs = symbol_name(grammar, production.lhs)
105
+ rhs = production.rhs.map { |symbol| symbol_name(grammar, symbol) }
106
+ "#{lhs}->#{rhs.join(' ')}"
107
+ end
108
+
109
+ # @rbs (IR::Grammar grammar, Integer id) -> String
110
+ def symbol_name(grammar, id)
111
+ grammar.symbol_by_id(id)&.name || id.to_s
112
+ end
113
+
114
+ # @rbs (IR::Grammar grammar) -> Hash[String, Object?]
115
+ def warning_records(grammar)
116
+ grouped = Hash.new { |hash, key| hash[key] = [] } #: Hash[String, Array[Object?]]
117
+ grammar.warnings.each do |warning|
118
+ value = warning.except(:loc)
119
+ identity = [warning.fetch(:type), value.except(:message).inspect].join(":")
120
+ grouped[identity] << value
121
+ end
122
+ grouped.transform_values { |values| values.sort_by(&:inspect) }
123
+ end
124
+
125
+ # @rbs () -> Hash[Symbol, Hash[Symbol, Integer]]
126
+ def numeric_metrics
127
+ {
128
+ states: change(@before.states.length, @after.states.length),
129
+ productions: change(@before.grammar.productions.length, @after.grammar.productions.length),
130
+ warnings: change(@before.grammar.warnings.length, @after.grammar.warnings.length),
131
+ shift_reduce: change(@before.conflict_summary.fetch(:sr), @after.conflict_summary.fetch(:sr)),
132
+ reduce_reduce: change(@before.conflict_summary.fetch(:rr), @after.conflict_summary.fetch(:rr))
133
+ }
134
+ end
135
+
136
+ # @rbs (Integer before, Integer after) -> Hash[Symbol, Integer]
137
+ def change(before, after)
138
+ { before: before, after: after, delta: after - before }
139
+ end
140
+ end
141
+ end