ibex 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -123
  3. data/docs/architecture.md +477 -31
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +106 -0
  12. data/docs/cst.md +237 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +306 -0
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +26 -0
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +114 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +629 -18
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +56 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +45 -6
  63. data/docs/release-readiness.md +221 -0
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +234 -0
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/examples/README.md +60 -0
  75. data/examples/calculator.y +44 -0
  76. data/examples/csv.y +32 -0
  77. data/examples/ini.y +70 -0
  78. data/examples/json.y +48 -0
  79. data/examples/tiny_language.y +75 -0
  80. data/lib/ibex/analysis/digraph.rb +128 -0
  81. data/lib/ibex/analysis/sets.rb +5 -4
  82. data/lib/ibex/analysis.rb +1 -0
  83. data/lib/ibex/artifact_set.rb +63 -0
  84. data/lib/ibex/bison_import/importer.rb +516 -0
  85. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  86. data/lib/ibex/bison_import.rb +200 -0
  87. data/lib/ibex/bounded_subprocess.rb +171 -0
  88. data/lib/ibex/cli/ambiguity.rb +75 -0
  89. data/lib/ibex/cli/analysis.rb +134 -0
  90. data/lib/ibex/cli/bison_import.rb +121 -0
  91. data/lib/ibex/cli/config.rb +128 -0
  92. data/lib/ibex/cli/counterexample_options.rb +6 -2
  93. data/lib/ibex/cli/coverage.rb +173 -0
  94. data/lib/ibex/cli/debug.rb +106 -0
  95. data/lib/ibex/cli/diagnostics.rb +136 -0
  96. data/lib/ibex/cli/documentation.rb +70 -0
  97. data/lib/ibex/cli/equiv.rb +169 -0
  98. data/lib/ibex/cli/error_messages.rb +184 -0
  99. data/lib/ibex/cli/explain.rb +85 -0
  100. data/lib/ibex/cli/fix.rb +203 -0
  101. data/lib/ibex/cli/formatting.rb +387 -0
  102. data/lib/ibex/cli/fuzz.rb +200 -0
  103. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  104. data/lib/ibex/cli/generation_artifacts.rb +189 -0
  105. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  106. data/lib/ibex/cli/grammar_tests.rb +140 -0
  107. data/lib/ibex/cli/ir_tools.rb +128 -0
  108. data/lib/ibex/cli/lsp.rb +33 -0
  109. data/lib/ibex/cli/outputs.rb +133 -23
  110. data/lib/ibex/cli/racc_migration.rb +87 -0
  111. data/lib/ibex/cli/reduce.rb +202 -0
  112. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  113. data/lib/ibex/cli/samples.rb +118 -0
  114. data/lib/ibex/cli/verify.rb +116 -0
  115. data/lib/ibex/cli/watch.rb +104 -0
  116. data/lib/ibex/cli.rb +825 -55
  117. data/lib/ibex/codegen/action_locations.rb +103 -0
  118. data/lib/ibex/codegen/action_method_source.rb +209 -0
  119. data/lib/ibex/codegen/action_source.rb +119 -0
  120. data/lib/ibex/codegen/ambiguity.rb +174 -0
  121. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  122. data/lib/ibex/codegen/documentation.rb +170 -0
  123. data/lib/ibex/codegen/explain.rb +380 -0
  124. data/lib/ibex/codegen/generated_action_abi.rb +293 -0
  125. data/lib/ibex/codegen/html.rb +94 -10
  126. data/lib/ibex/codegen/mermaid.rb +43 -0
  127. data/lib/ibex/codegen/railroad.rb +197 -0
  128. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  129. data/lib/ibex/codegen/rbs.rb +331 -2
  130. data/lib/ibex/codegen/report.rb +67 -15
  131. data/lib/ibex/codegen/ruby.rb +305 -66
  132. data/lib/ibex/codegen/ruby_actions.rb +146 -0
  133. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  134. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  135. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  136. data/lib/ibex/codegen/ruby_syntax.rb +96 -0
  137. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  138. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  139. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  140. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  141. data/lib/ibex/configuration/explanation.rb +421 -0
  142. data/lib/ibex/configuration/inspector.rb +232 -0
  143. data/lib/ibex/configuration.rb +580 -0
  144. data/lib/ibex/coverage/collector.rb +198 -0
  145. data/lib/ibex/coverage/event_stream.rb +103 -0
  146. data/lib/ibex/coverage/report.rb +267 -0
  147. data/lib/ibex/coverage/runtime_event_validator.rb +169 -0
  148. data/lib/ibex/coverage.rb +13 -0
  149. data/lib/ibex/delta_reducer.rb +99 -0
  150. data/lib/ibex/diff.rb +140 -0
  151. data/lib/ibex/equiv/machine.rb +135 -0
  152. data/lib/ibex/equiv.rb +373 -0
  153. data/lib/ibex/error_messages/parser.rb +159 -0
  154. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  155. data/lib/ibex/error_messages/renderer.rb +65 -0
  156. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  157. data/lib/ibex/error_messages/update.rb +169 -0
  158. data/lib/ibex/error_messages.rb +165 -0
  159. data/lib/ibex/fix.rb +557 -0
  160. data/lib/ibex/frontend/ast.rb +166 -7
  161. data/lib/ibex/frontend/bootstrap_parser.rb +40 -4
  162. data/lib/ibex/frontend/diagnostic.rb +81 -0
  163. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  164. data/lib/ibex/frontend/dsl.rb +66 -7
  165. data/lib/ibex/frontend/formatter.rb +453 -0
  166. data/lib/ibex/frontend/generated_parser.rb +644 -190
  167. data/lib/ibex/frontend/generated_parser_base.rb +170 -32
  168. data/lib/ibex/frontend/generated_parser_includes.rb +51 -0
  169. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  170. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  171. data/lib/ibex/frontend/generation.rb +33 -0
  172. data/lib/ibex/frontend/lexer.rb +162 -10
  173. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  174. data/lib/ibex/frontend/parser/declarations.rb +262 -11
  175. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  176. data/lib/ibex/frontend/parser/rules.rb +43 -6
  177. data/lib/ibex/frontend/parser.rb +182 -4
  178. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  179. data/lib/ibex/frontend/regenerator.rb +27 -1
  180. data/lib/ibex/frontend/resolution.rb +70 -0
  181. data/lib/ibex/frontend/resolver.rb +217 -0
  182. data/lib/ibex/frontend/rule_documentation.rb +104 -0
  183. data/lib/ibex/frontend/source_cursor.rb +133 -9
  184. data/lib/ibex/frontend/source_document.rb +229 -0
  185. data/lib/ibex/frontend/source_loader.rb +150 -0
  186. data/lib/ibex/frontend/source_span.rb +89 -0
  187. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  188. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  189. data/lib/ibex/frontend/token_adapter/declaration_state.rb +244 -22
  190. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  191. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  192. data/lib/ibex/frontend/token_adapter.rb +18 -3
  193. data/lib/ibex/frontend.rb +14 -2
  194. data/lib/ibex/fuzz.rb +219 -0
  195. data/lib/ibex/generation_input.rb +57 -0
  196. data/lib/ibex/generation_manifest.rb +211 -0
  197. data/lib/ibex/generation_transaction.rb +261 -0
  198. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  199. data/lib/ibex/generation_transaction_validation.rb +196 -0
  200. data/lib/ibex/grammar_tests.rb +206 -0
  201. data/lib/ibex/ir/automaton_ir.rb +86 -11
  202. data/lib/ibex/ir/grammar_ir.rb +170 -26
  203. data/lib/ibex/ir/lexer_ir.rb +76 -0
  204. data/lib/ibex/ir/parser_contract.rb +135 -0
  205. data/lib/ibex/ir/serialize.rb +268 -50
  206. data/lib/ibex/ir/validator/automaton.rb +375 -0
  207. data/lib/ibex/ir/validator/base.rb +147 -0
  208. data/lib/ibex/ir/validator/grammar.rb +634 -0
  209. data/lib/ibex/ir/validator/lexer.rb +113 -0
  210. data/lib/ibex/ir/validator.rb +63 -0
  211. data/lib/ibex/ir.rb +71 -5
  212. data/lib/ibex/lalr/build_metrics.rb +71 -0
  213. data/lib/ibex/lalr/builder.rb +514 -52
  214. data/lib/ibex/lalr/conflict.rb +1 -0
  215. data/lib/ibex/lalr/conflict_search.rb +26 -9
  216. data/lib/ibex/lalr/counterexample.rb +38 -18
  217. data/lib/ibex/lalr/direct_lookaheads.rb +289 -0
  218. data/lib/ibex/lalr/goto_follows.rb +229 -0
  219. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  220. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  221. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  222. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  223. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  224. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  225. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  226. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  227. data/lib/ibex/lalr/ielr_partition.rb +166 -0
  228. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  229. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  230. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  231. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  232. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  233. data/lib/ibex/lalr.rb +59 -1
  234. data/lib/ibex/location.rb +129 -0
  235. data/lib/ibex/lsp/document_handlers.rb +68 -0
  236. data/lib/ibex/lsp/document_store.rb +264 -0
  237. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  238. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  239. data/lib/ibex/lsp/initialization_handlers.rb +80 -0
  240. data/lib/ibex/lsp/navigation_handlers.rb +70 -0
  241. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  242. data/lib/ibex/lsp/position_codec.rb +113 -0
  243. data/lib/ibex/lsp/protocol_error.rb +33 -0
  244. data/lib/ibex/lsp/request_handlers.rb +46 -0
  245. data/lib/ibex/lsp/request_support.rb +94 -0
  246. data/lib/ibex/lsp/server.rb +148 -0
  247. data/lib/ibex/lsp/symbol_index.rb +241 -0
  248. data/lib/ibex/lsp/symbol_index_builder.rb +277 -0
  249. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  250. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  251. data/lib/ibex/lsp/symbol_occurrence.rb +23 -0
  252. data/lib/ibex/lsp/transport.rb +128 -0
  253. data/lib/ibex/lsp/workspace.rb +128 -0
  254. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  255. data/lib/ibex/lsp.rb +33 -0
  256. data/lib/ibex/messages/en.yml +28 -0
  257. data/lib/ibex/messages/ja.yml +28 -0
  258. data/lib/ibex/messages.rb +68 -0
  259. data/lib/ibex/metrics.rb +175 -0
  260. data/lib/ibex/normalize/declarations.rb +142 -7
  261. data/lib/ibex/normalize/diagnostics.rb +94 -24
  262. data/lib/ibex/normalize/expander.rb +64 -59
  263. data/lib/ibex/normalize/expression.rb +63 -40
  264. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  265. data/lib/ibex/normalize/inline_expansion.rb +465 -0
  266. data/lib/ibex/normalize/inline_validation.rb +174 -0
  267. data/lib/ibex/normalize/lexer.rb +131 -0
  268. data/lib/ibex/normalize/named_references.rb +60 -0
  269. data/lib/ibex/normalize/nodes.rb +46 -0
  270. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +70 -0
  271. data/lib/ibex/normalize/parameter_lowering.rb +143 -0
  272. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  273. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  274. data/lib/ibex/normalize/parameters.rb +226 -0
  275. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  276. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  277. data/lib/ibex/normalize.rb +172 -17
  278. data/lib/ibex/racc_migration/checker.rb +122 -0
  279. data/lib/ibex/racc_migration/harness.rb +177 -0
  280. data/lib/ibex/racc_migration/report.rb +97 -0
  281. data/lib/ibex/racc_migration.rb +12 -0
  282. data/lib/ibex/rake_task.rb +116 -0
  283. data/lib/ibex/samples.rb +216 -0
  284. data/lib/ibex/table_artifact/builder.rb +266 -0
  285. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  286. data/lib/ibex/table_artifact/document.rb +39 -0
  287. data/lib/ibex/table_artifact/executor.rb +187 -0
  288. data/lib/ibex/table_artifact/serializer.rb +57 -0
  289. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  290. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  291. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  292. data/lib/ibex/table_artifact/validator.rb +253 -0
  293. data/lib/ibex/table_artifact.rb +88 -0
  294. data/lib/ibex/table_simulation/result.rb +55 -0
  295. data/lib/ibex/table_simulation/simulator.rb +253 -0
  296. data/lib/ibex/table_simulation/step.rb +62 -0
  297. data/lib/ibex/table_simulation/text.rb +31 -0
  298. data/lib/ibex/table_simulation.rb +13 -0
  299. data/lib/ibex/tables.rb +22 -69
  300. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  301. data/lib/ibex/verification_report/builder.rb +159 -0
  302. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  303. data/lib/ibex/verification_report/logical_path.rb +68 -0
  304. data/lib/ibex/verification_report/validator.rb +329 -0
  305. data/lib/ibex/verification_report.rb +69 -0
  306. data/lib/ibex/verify/action_correspondence.rb +138 -0
  307. data/lib/ibex/verify/language_witness.rb +295 -0
  308. data/lib/ibex/verify/reference_collection.rb +190 -0
  309. data/lib/ibex/verify/result.rb +73 -0
  310. data/lib/ibex/verify/verifier.rb +598 -0
  311. data/lib/ibex/verify.rb +15 -0
  312. data/lib/ibex/version.rb +1 -1
  313. data/lib/ibex/watch/runner.rb +181 -0
  314. data/lib/ibex/watch/source_snapshot.rb +98 -0
  315. data/lib/ibex/watch.rb +11 -0
  316. data/lib/ibex.rb +35 -1
  317. data/schema/automaton-ir-definitions.schema.json +401 -0
  318. data/schema/automaton-ir.schema.json +65 -0
  319. data/schema/benchmark-v1.schema.json +212 -0
  320. data/schema/benchmark-v2.schema.json +61 -0
  321. data/schema/bison-import-v1.schema.json +106 -0
  322. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  323. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  324. data/schema/construction-profile-v1.schema.json +779 -0
  325. data/schema/cst-v1.json +170 -0
  326. data/schema/diff-v1.schema.json +41 -0
  327. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  328. data/schema/equiv-v1.schema.json +64 -0
  329. data/schema/error-ux-review-v1.schema.json +609 -0
  330. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  331. data/schema/error-ux-round2-v1.schema.json +519 -0
  332. data/schema/error-ux-v1.schema.json +258 -0
  333. data/schema/explain-v1.schema.json +495 -0
  334. data/schema/fix-v1.schema.json +64 -0
  335. data/schema/fix-v2.schema.json +66 -0
  336. data/schema/fix-v3.schema.json +101 -0
  337. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  338. data/schema/fuzz-regression-v1.schema.json +79 -0
  339. data/schema/fuzz-v1.schema.json +114 -0
  340. data/schema/generation-manifest-v1.schema.json +115 -0
  341. data/schema/grammar-ir-extensions.schema.json +734 -0
  342. data/schema/grammar-ir-foundation.schema.json +426 -0
  343. data/schema/grammar-ir.schema.json +271 -0
  344. data/schema/ielr-benchmark-v1.schema.json +85 -0
  345. data/schema/lexer-ir-v1.schema.json +215 -0
  346. data/schema/lexer-profile-v1.schema.json +491 -0
  347. data/schema/metrics-v1.schema.json +60 -0
  348. data/schema/migration-check-v1.schema.json +60 -0
  349. data/schema/performance-comparison-v1.schema.json +395 -0
  350. data/schema/public-performance-comparison-v1.schema.json +506 -0
  351. data/schema/public-performance-profile-v1.schema.json +360 -0
  352. data/schema/reduce-v1.schema.json +22 -0
  353. data/schema/reduce-v2.schema.json +63 -0
  354. data/schema/runtime-coverage-v1.schema.json +86 -0
  355. data/schema/runtime-event-v1.schema.json +308 -0
  356. data/schema/table-artifact-v1.schema.json +377 -0
  357. data/schema/table-simulation-v1.schema.json +70 -0
  358. data/schema/verification-report-v1.schema.json +222 -0
  359. data/schema/verify-v1.schema.json +42 -0
  360. data/sig/ibex/analysis/digraph.rbs +19 -0
  361. data/sig/ibex/artifact_set.rbs +37 -0
  362. data/sig/ibex/bison_import/importer.rbs +140 -0
  363. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  364. data/sig/ibex/bison_import.rbs +95 -0
  365. data/sig/ibex/bounded_subprocess.rbs +76 -0
  366. data/sig/ibex/cli/ambiguity.rbs +28 -0
  367. data/sig/ibex/cli/analysis.rbs +43 -0
  368. data/sig/ibex/cli/bison_import.rbs +32 -0
  369. data/sig/ibex/cli/config.rbs +35 -0
  370. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  371. data/sig/ibex/cli/coverage.rbs +53 -0
  372. data/sig/ibex/cli/debug.rbs +28 -0
  373. data/sig/ibex/cli/diagnostics.rbs +51 -0
  374. data/sig/ibex/cli/documentation.rbs +29 -0
  375. data/sig/ibex/cli/equiv.rbs +49 -0
  376. data/sig/ibex/cli/error_messages.rbs +63 -0
  377. data/sig/ibex/cli/explain.rbs +31 -0
  378. data/sig/ibex/cli/fix.rbs +53 -0
  379. data/sig/ibex/cli/formatting.rbs +107 -0
  380. data/sig/ibex/cli/fuzz.rbs +47 -0
  381. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  382. data/sig/ibex/cli/generation_artifacts.rbs +69 -0
  383. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  384. data/sig/ibex/cli/grammar_tests.rbs +48 -0
  385. data/sig/ibex/cli/ir_tools.rbs +39 -0
  386. data/sig/ibex/cli/lsp.rbs +14 -0
  387. data/sig/ibex/cli/outputs.rbs +26 -3
  388. data/sig/ibex/cli/racc_migration.rbs +30 -0
  389. data/sig/ibex/cli/reduce.rbs +60 -0
  390. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  391. data/sig/ibex/cli/samples.rbs +38 -0
  392. data/sig/ibex/cli/verify.rbs +28 -0
  393. data/sig/ibex/cli/watch.rbs +43 -0
  394. data/sig/ibex/cli.rbs +182 -9
  395. data/sig/ibex/codegen/action_locations.rbs +45 -0
  396. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  397. data/sig/ibex/codegen/action_source.rbs +50 -0
  398. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  399. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  400. data/sig/ibex/codegen/documentation.rbs +50 -0
  401. data/sig/ibex/codegen/explain.rbs +105 -0
  402. data/sig/ibex/codegen/generated_action_abi.rbs +105 -0
  403. data/sig/ibex/codegen/html.rbs +18 -2
  404. data/sig/ibex/codegen/mermaid.rbs +16 -0
  405. data/sig/ibex/codegen/railroad.rbs +82 -0
  406. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  407. data/sig/ibex/codegen/rbs.rbs +86 -4
  408. data/sig/ibex/codegen/report.rbs +16 -8
  409. data/sig/ibex/codegen/ruby.rbs +105 -28
  410. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  411. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  412. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  413. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  414. data/sig/ibex/codegen/ruby_syntax.rbs +27 -0
  415. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  416. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  417. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  418. data/sig/ibex/configuration/explanation.rbs +195 -0
  419. data/sig/ibex/configuration/inspector.rbs +53 -0
  420. data/sig/ibex/configuration.rbs +242 -0
  421. data/sig/ibex/coverage/collector.rbs +78 -0
  422. data/sig/ibex/coverage/event_stream.rbs +44 -0
  423. data/sig/ibex/coverage/report.rbs +102 -0
  424. data/sig/ibex/coverage/runtime_event_validator.rbs +70 -0
  425. data/sig/ibex/coverage.rbs +7 -0
  426. data/sig/ibex/delta_reducer.rbs +38 -0
  427. data/sig/ibex/diff.rbs +47 -0
  428. data/sig/ibex/equiv/machine.rbs +54 -0
  429. data/sig/ibex/equiv.rbs +129 -0
  430. data/sig/ibex/error_messages/parser.rbs +58 -0
  431. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  432. data/sig/ibex/error_messages/renderer.rbs +23 -0
  433. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  434. data/sig/ibex/error_messages/update.rbs +43 -0
  435. data/sig/ibex/error_messages.rbs +85 -0
  436. data/sig/ibex/fix.rbs +138 -0
  437. data/sig/ibex/frontend/ast.rbs +237 -22
  438. data/sig/ibex/frontend/bootstrap_parser.rbs +13 -0
  439. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  440. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  441. data/sig/ibex/frontend/dsl.rbs +33 -4
  442. data/sig/ibex/frontend/formatter.rbs +143 -0
  443. data/sig/ibex/frontend/generated_parser.rbs +228 -68
  444. data/sig/ibex/frontend/generated_parser_base.rbs +65 -12
  445. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  446. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  447. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  448. data/sig/ibex/frontend/generation.rbs +6 -0
  449. data/sig/ibex/frontend/lexer.rbs +49 -2
  450. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  451. data/sig/ibex/frontend/parser/declarations.rbs +57 -0
  452. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  453. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  454. data/sig/ibex/frontend/parser.rbs +66 -0
  455. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  456. data/sig/ibex/frontend/regenerator.rbs +11 -0
  457. data/sig/ibex/frontend/resolution.rbs +34 -0
  458. data/sig/ibex/frontend/resolver.rbs +91 -0
  459. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  460. data/sig/ibex/frontend/source_cursor.rbs +38 -5
  461. data/sig/ibex/frontend/source_document.rbs +119 -0
  462. data/sig/ibex/frontend/source_loader.rbs +66 -0
  463. data/sig/ibex/frontend/source_span.rbs +56 -0
  464. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  465. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  466. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +83 -3
  467. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  468. data/sig/ibex/frontend/token_adapter.rbs +11 -2
  469. data/sig/ibex/frontend.rbs +1 -1
  470. data/sig/ibex/fuzz.rbs +71 -0
  471. data/sig/ibex/generation_input.rbs +37 -0
  472. data/sig/ibex/generation_manifest.rbs +70 -0
  473. data/sig/ibex/generation_transaction.rbs +82 -0
  474. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  475. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  476. data/sig/ibex/grammar_tests.rbs +93 -0
  477. data/sig/ibex/ir/automaton_ir.rbs +35 -10
  478. data/sig/ibex/ir/grammar_ir.rbs +96 -22
  479. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  480. data/sig/ibex/ir/parser_contract.rbs +69 -0
  481. data/sig/ibex/ir/serialize.rbs +75 -23
  482. data/sig/ibex/ir/validator/automaton.rbs +112 -0
  483. data/sig/ibex/ir/validator/base.rbs +69 -0
  484. data/sig/ibex/ir/validator/grammar.rbs +195 -0
  485. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  486. data/sig/ibex/ir/validator.rbs +16 -0
  487. data/sig/ibex/ir.rbs +45 -5
  488. data/sig/ibex/lalr/build_metrics.rbs +60 -0
  489. data/sig/ibex/lalr/builder.rbs +150 -19
  490. data/sig/ibex/lalr/conflict_search.rbs +12 -5
  491. data/sig/ibex/lalr/counterexample.rbs +20 -14
  492. data/sig/ibex/lalr/direct_lookaheads.rbs +93 -0
  493. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  494. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  495. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  496. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  497. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  498. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  499. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  500. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  501. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  502. data/sig/ibex/lalr/ielr_partition.rbs +71 -0
  503. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  504. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  505. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  506. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  507. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  508. data/sig/ibex/lalr.rbs +17 -1
  509. data/sig/ibex/location.rbs +67 -0
  510. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  511. data/sig/ibex/lsp/document_store.rbs +94 -0
  512. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  513. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  514. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  515. data/sig/ibex/lsp/navigation_handlers.rbs +33 -0
  516. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  517. data/sig/ibex/lsp/position_codec.rbs +44 -0
  518. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  519. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  520. data/sig/ibex/lsp/request_support.rbs +40 -0
  521. data/sig/ibex/lsp/server.rbs +55 -0
  522. data/sig/ibex/lsp/symbol_index.rbs +77 -0
  523. data/sig/ibex/lsp/symbol_index_builder.rbs +88 -0
  524. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  525. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  526. data/sig/ibex/lsp/symbol_occurrence.rbs +31 -0
  527. data/sig/ibex/lsp/transport.rbs +47 -0
  528. data/sig/ibex/lsp/workspace.rbs +42 -0
  529. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  530. data/sig/ibex/lsp.rbs +7 -0
  531. data/sig/ibex/messages.rbs +26 -0
  532. data/sig/ibex/metrics.rbs +50 -0
  533. data/sig/ibex/normalize/declarations.rbs +31 -0
  534. data/sig/ibex/normalize/diagnostics.rbs +13 -4
  535. data/sig/ibex/normalize/expander.rbs +22 -16
  536. data/sig/ibex/normalize/expression.rbs +11 -9
  537. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  538. data/sig/ibex/normalize/inline_expansion.rbs +129 -0
  539. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  540. data/sig/ibex/normalize/lexer.rbs +37 -0
  541. data/sig/ibex/normalize/named_references.rbs +20 -0
  542. data/sig/ibex/normalize/nodes.rbs +14 -0
  543. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +33 -0
  544. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  545. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  546. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  547. data/sig/ibex/normalize/parameters.rbs +51 -0
  548. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  549. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  550. data/sig/ibex/normalize.rbs +129 -18
  551. data/sig/ibex/racc_migration/checker.rbs +36 -0
  552. data/sig/ibex/racc_migration/harness.rbs +16 -0
  553. data/sig/ibex/racc_migration/report.rbs +55 -0
  554. data/sig/ibex/racc_migration.rbs +8 -0
  555. data/sig/ibex/rake_task.rbs +51 -0
  556. data/sig/ibex/samples.rbs +60 -0
  557. data/sig/ibex/table_artifact/builder.rbs +93 -0
  558. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  559. data/sig/ibex/table_artifact/document.rbs +27 -0
  560. data/sig/ibex/table_artifact/executor.rbs +66 -0
  561. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  562. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  563. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  564. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  565. data/sig/ibex/table_artifact/validator.rbs +66 -0
  566. data/sig/ibex/table_artifact.rbs +32 -0
  567. data/sig/ibex/table_simulation/result.rbs +36 -0
  568. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  569. data/sig/ibex/table_simulation/step.rbs +42 -0
  570. data/sig/ibex/table_simulation/text.rbs +14 -0
  571. data/sig/ibex/table_simulation.rbs +7 -0
  572. data/sig/ibex/tables.rbs +10 -35
  573. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  574. data/sig/ibex/verification_report/builder.rbs +53 -0
  575. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  576. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  577. data/sig/ibex/verification_report/validator.rbs +114 -0
  578. data/sig/ibex/verification_report.rbs +44 -0
  579. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  580. data/sig/ibex/verify/language_witness.rbs +112 -0
  581. data/sig/ibex/verify/reference_collection.rbs +63 -0
  582. data/sig/ibex/verify/result.rbs +52 -0
  583. data/sig/ibex/verify/verifier.rbs +170 -0
  584. data/sig/ibex/verify.rbs +6 -0
  585. data/sig/ibex/watch/runner.rbs +58 -0
  586. data/sig/ibex/watch/source_snapshot.rbs +46 -0
  587. data/sig/ibex/watch.rbs +7 -0
  588. data/sig/ibex.rbs +2 -0
  589. metadata +524 -19
  590. data/.rubocop.yml +0 -43
  591. data/CHANGELOG.md +0 -30
  592. data/Rakefile +0 -25
  593. data/Steepfile +0 -10
  594. data/docs/compat-notes.md +0 -37
  595. data/docs/lexer-coverage.md +0 -14
  596. data/docs/phase10-extensions.md +0 -27
  597. data/gemfiles/Gemfile +0 -7
  598. data/gemfiles/Gemfile.lock +0 -98
  599. data/lib/ibex/frontend/grammar.y +0 -156
  600. data/lib/ibex/runtime/parser.rb +0 -360
  601. data/lib/ibex/runtime.rb +0 -8
  602. data/sig/ibex/runtime/parser.rbs +0 -167
  603. data/sig/ibex/runtime.rbs +0 -6
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+ require "shellwords"
6
+ require_relative "../bounded_subprocess"
7
+ require_relative "../fuzz"
8
+ require_relative "fuzz_regressions"
9
+
10
+ module Ibex
11
+ # CLI entry point for bounded grammar-derived differential fuzzing.
12
+ module CLIFuzz
13
+ include CLIFuzzRegressions
14
+
15
+ # @rbs!
16
+ # private def print_help: (OptionParser) -> Integer
17
+ # private def input_path: (Array[String]) -> String
18
+ # private def normalize_grammar_path: (String) -> IR::Grammar
19
+ # private def handle_grammar_warnings: (IR::Grammar, String) -> void
20
+ # private def warning_categories: (String) -> Array[Symbol]
21
+ # private def select_configuration_mode: (String) -> void
22
+
23
+ private
24
+
25
+ # @rbs (Array[String] arguments) -> Integer
26
+ def run_fuzz_command(arguments)
27
+ parser = fuzz_option_parser
28
+ remaining = parser.parse(arguments)
29
+ return print_help(parser) if @options[:help]
30
+
31
+ path = input_path(remaining)
32
+ grammar = normalize_grammar_path(path)
33
+ handle_grammar_warnings(grammar, path)
34
+ external = external_fuzz_target
35
+ fuzzer = Fuzz.new(
36
+ grammar,
37
+ seed: @options.fetch(:fuzz_seed, 0), count: @options.fetch(:fuzz_count, 100),
38
+ max_tokens: @options.fetch(:fuzz_max_tokens, 32), max_depth: @options.fetch(:fuzz_max_depth, 16),
39
+ max_expansions: @options.fetch(:fuzz_max_expansions, Samples::DEFAULT_MAX_EXPANSIONS),
40
+ max_actions: @options.fetch(:fuzz_max_actions, Fuzz::DEFAULT_MAX_ACTIONS),
41
+ max_stack: @options.fetch(:fuzz_max_stack, Fuzz::DEFAULT_MAX_STACK),
42
+ coverage_guided: @options.fetch(:fuzz_coverage_guided, false),
43
+ path_length: @options.fetch(:fuzz_path_length, 2),
44
+ against: external&.[](0), against_description: external&.[](1)
45
+ )
46
+ write_fuzz_report(fuzzer.run)
47
+ 0
48
+ rescue Fuzz::Mismatch => e
49
+ raise unless fuzzer && path
50
+
51
+ begin
52
+ write_minimized_fuzz_mismatch(fuzzer, e, path, external)
53
+ rescue Fuzz::BudgetExceeded => budget
54
+ write_fuzz_budget_report(budget, external, phase: "reduction")
55
+ end
56
+ rescue Fuzz::BudgetExceeded => e
57
+ write_fuzz_budget_report(e, external, phase: "comparison")
58
+ end
59
+
60
+ # @rbs () -> OptionParser
61
+ def fuzz_option_parser
62
+ OptionParser.new do |options|
63
+ options.banner = "Usage: ibex fuzz [options] grammarfile"
64
+ add_fuzz_generation_options(options)
65
+ add_fuzz_execution_options(options)
66
+ options.on("--mode=MODE", %w[default extended], "grammar mode") { |value| select_configuration_mode(value) }
67
+ options.on("--warnings=CATEGORIES", "all, error, all,error, or none") do |value|
68
+ @options[:warnings] = warning_categories(value)
69
+ end
70
+ options.on("--help", "show help") { @options[:help] = true }
71
+ end
72
+ end
73
+
74
+ # @rbs (OptionParser options) -> void
75
+ def add_fuzz_generation_options(options)
76
+ options.on("--count=N", Integer, "generated sentences (default 100)") do |value|
77
+ @options[:fuzz_count] = positive_fuzz_option!("count", value)
78
+ end
79
+ options.on("--seed=N", Integer, "deterministic seed (default 0)") { |value| @options[:fuzz_seed] = value }
80
+ options.on("--max-tokens=N", Integer, "maximum generated tokens") do |value|
81
+ @options[:fuzz_max_tokens] = positive_fuzz_option!("max-tokens", value)
82
+ end
83
+ options.on("--max-depth=N", Integer, "maximum random expansion depth") do |value|
84
+ @options[:fuzz_max_depth] = positive_fuzz_option!("max-depth", value)
85
+ end
86
+ options.on("--max-expansions=N", Integer, "total derivation work budget") do |value|
87
+ @options[:fuzz_max_expansions] = positive_fuzz_option!("max-expansions", value)
88
+ end
89
+ end
90
+
91
+ # @rbs (OptionParser options) -> void
92
+ def add_fuzz_execution_options(options)
93
+ options.on("--max-actions=N", Integer, "actions per simulated sentence") do |value|
94
+ @options[:fuzz_max_actions] = positive_fuzz_option!("max-actions", value)
95
+ end
96
+ options.on("--max-stack=N", Integer, "simulated parser stack depth") do |value|
97
+ @options[:fuzz_max_stack] = positive_fuzz_option!("max-stack", value)
98
+ end
99
+ options.on("--coverage-guided", "prefer uncovered production paths") do
100
+ @options[:fuzz_coverage_guided] = true
101
+ end
102
+ options.on("--path-length=N", Integer, "coverage path length: 1 or 2") do |value|
103
+ raise OptionParser::InvalidArgument, "--path-length must be 1 or 2" unless [1, 2].include?(value)
104
+
105
+ @options[:fuzz_path_length] = value
106
+ end
107
+ add_fuzz_external_options(options)
108
+ end
109
+
110
+ # @rbs (OptionParser options) -> void
111
+ def add_fuzz_external_options(options)
112
+ options.on("--against=COMMAND", "compare with an explicit JSON-token subprocess") do |value|
113
+ @options[:fuzz_against] = value
114
+ end
115
+ options.on("--against-runtime=DESCRIPTION", "required target runtime configuration for --against") do |value|
116
+ raise OptionParser::InvalidArgument, "--against-runtime must not be empty" if value.empty?
117
+
118
+ @options[:fuzz_against_runtime] = value
119
+ end
120
+ options.on("--against-timeout=SECONDS", Integer, "target timeout per sentence (default 10)") do |value|
121
+ @options[:fuzz_against_timeout] = positive_fuzz_option!("against-timeout", value)
122
+ end
123
+ options.on("--against-max-output=N", Integer, "target stdout/stderr byte budget") do |value|
124
+ @options[:fuzz_against_max_output] = positive_fuzz_option!("against-max-output", value)
125
+ end
126
+ options.on("--max-reduction-trials=N", Integer, "automatic mismatch-reduction trial budget") do |value|
127
+ @options[:fuzz_max_reduction_trials] = positive_fuzz_option!("max-reduction-trials", value)
128
+ end
129
+ options.on("--regression-dir=DIR", "saved minimized regressions (default test/fuzz/regressions)") do |value|
130
+ raise OptionParser::InvalidArgument, "--regression-dir must not be empty" if value.empty?
131
+
132
+ @options[:fuzz_regression_dir] = value
133
+ end
134
+ options.on("--[no-]save-regression", "persist minimized mismatches (default enabled)") do |value|
135
+ @options[:fuzz_save_regression] = value
136
+ end
137
+ options.on("--format=FORMAT", %w[json text], "json or text (default json)") do |value|
138
+ @options[:fuzz_format] = value
139
+ end
140
+ end
141
+
142
+ # @rbs () -> fuzz_external_target?
143
+ def external_fuzz_target
144
+ command = @options[:fuzz_against]
145
+ runtime = @options[:fuzz_against_runtime]
146
+ if command.nil?
147
+ raise OptionParser::InvalidArgument, "--against-runtime requires --against" if runtime
148
+
149
+ return
150
+ end
151
+ unless runtime
152
+ raise OptionParser::InvalidArgument,
153
+ "--against requires --against-runtime so the comparison target is reproducible"
154
+ end
155
+
156
+ arguments = Shellwords.split(command)
157
+ raise OptionParser::InvalidArgument, "--against command must not be empty" if arguments.empty?
158
+
159
+ timeout = @options.fetch(:fuzz_against_timeout, BoundedSubprocess::DEFAULT_TIMEOUT_SECONDS)
160
+ max_output = @options.fetch(:fuzz_against_max_output, BoundedSubprocess::DEFAULT_MAX_OUTPUT_BYTES)
161
+ subprocess = BoundedSubprocess.new(timeout_seconds: timeout, max_output_bytes: max_output)
162
+ runner = lambda do |tokens|
163
+ external_fuzz_outcome(subprocess, arguments, tokens, timeout, max_output)
164
+ end #: ^(Array[String]) -> Symbol
165
+ description = {
166
+ command: command, target_runtime: runtime,
167
+ timeout_seconds: timeout, max_output_bytes: max_output,
168
+ host_ruby_engine: RUBY_ENGINE, host_ruby_version: RUBY_VERSION,
169
+ host_platform: RUBY_PLATFORM
170
+ } #: fuzz_external_description
171
+ [runner, description]
172
+ end
173
+
174
+ # @rbs (BoundedSubprocess subprocess, Array[String] arguments, Array[String] tokens,
175
+ # Integer timeout, Integer max_output) -> Symbol
176
+ def external_fuzz_outcome(subprocess, arguments, tokens, timeout, max_output)
177
+ result = subprocess.run(arguments, input: JSON.generate(tokens))
178
+ if result.timed_out || result.output_limited
179
+ raise Fuzz::BudgetExceeded.new(
180
+ message: "external target exceeded its #{result.timed_out ? 'timeout' : 'output limit'}",
181
+ bounds: { timeout_seconds: timeout, max_output_bytes: max_output }
182
+ )
183
+ end
184
+ unless result.status.exited? && [0, 1].include?(result.status.exitstatus)
185
+ detail = result.stderr.empty? ? result.stdout : result.stderr
186
+ raise Ibex::Error,
187
+ "(fuzz):1:1: external target did not exit with 0 or 1: #{detail}"
188
+ end
189
+
190
+ result.status.success? ? :accepted : :error
191
+ end
192
+
193
+ # @rbs (String name, Integer value) -> Integer
194
+ def positive_fuzz_option!(name, value)
195
+ return value if value.positive?
196
+
197
+ raise OptionParser::InvalidArgument, "--#{name} must be positive"
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "digest"
5
+ require "fileutils"
6
+ require "json"
7
+
8
+ module Ibex
9
+ # @rbs!
10
+ # type fuzz_external_description = {
11
+ # command: String,
12
+ # target_runtime: String,
13
+ # timeout_seconds: Integer,
14
+ # max_output_bytes: Integer,
15
+ # host_ruby_engine: String,
16
+ # host_ruby_version: String,
17
+ # host_platform: String
18
+ # }
19
+ # type fuzz_external_target = [
20
+ # ^(Array[String]) -> Symbol,
21
+ # fuzz_external_description
22
+ # ]
23
+
24
+ # Automatic bounded minimization and persistence for fuzz differences.
25
+ module CLIFuzzRegressions
26
+ # @rbs!
27
+ # private def atomic_write_ir: (String path, String source) -> void
28
+
29
+ private
30
+
31
+ # @rbs (Fuzz fuzzer, Fuzz::Mismatch mismatch, String grammar_path,
32
+ # fuzz_external_target? external) -> Integer
33
+ def write_minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
34
+ details = minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
35
+ report = {
36
+ ibex_report: "fuzz", schema_version: 1, result: "difference", mismatch: details
37
+ } #: Hash[Symbol, untyped]
38
+ report[:external] = external[1] if external
39
+ write_fuzz_report(report)
40
+ 1
41
+ end
42
+
43
+ # @rbs (Hash[Symbol, untyped] report) -> void
44
+ def write_fuzz_report(report)
45
+ if @options.fetch(:fuzz_format, "json") == "json"
46
+ @stdout.puts JSON.pretty_generate(report)
47
+ return
48
+ end
49
+
50
+ @stdout.puts("result=#{report.fetch(:result)}")
51
+ case report.fetch(:result)
52
+ when "no_difference_within_bounds"
53
+ @stdout.puts("seed=#{report.fetch(:seed)} bounds=#{report.fetch(:bounds).inspect}")
54
+ @stdout.puts("no difference found within the declared bounds; this is not a proof of equivalence")
55
+ when "difference"
56
+ mismatch = report.fetch(:mismatch) #: Hash[Symbol, untyped]
57
+ @stdout.puts("kind=#{mismatch.fetch(:kind)} tokens=#{mismatch.fetch(:tokens).inspect}")
58
+ @stdout.puts("minimized=#{mismatch.fetch(:minimized_tokens).inspect} " \
59
+ "reduction_complete=#{mismatch.dig(:reduction, :complete)}")
60
+ @stdout.puts("regression=#{mismatch.dig(:regression, :path)}") if mismatch[:regression]
61
+ when "budget_exhausted"
62
+ @stdout.puts("search incomplete: #{report.dig(:budget, :message)}")
63
+ end
64
+ external = report[:external] #: fuzz_external_description?
65
+ return unless external
66
+
67
+ @stdout.puts("target_runtime=#{external.fetch(:target_runtime)}")
68
+ @stdout.puts("target_command=#{external.fetch(:command)}")
69
+ end
70
+
71
+ # @rbs (Fuzz::BudgetExceeded error,
72
+ # fuzz_external_target? external, phase: String) -> Integer
73
+ def write_fuzz_budget_report(error, external, phase:)
74
+ report = {
75
+ ibex_report: "fuzz", schema_version: 1, result: "budget_exhausted",
76
+ budget: error.details.merge(phase: phase)
77
+ } #: Hash[Symbol, untyped]
78
+ report[:external] = external[1] if external
79
+ write_fuzz_report(report)
80
+ 2
81
+ end
82
+
83
+ # @rbs (Fuzz fuzzer, Fuzz::Mismatch mismatch, String grammar_path,
84
+ # fuzz_external_target? external) -> Hash[Symbol, untyped]
85
+ def minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external)
86
+ result = fuzzer.minimize(
87
+ mismatch, max_trials: @options.fetch(:fuzz_max_reduction_trials, 1_000)
88
+ )
89
+ details = mismatch.details.dup #: Hash[Symbol, untyped]
90
+ details[:minimized_tokens] = result.items
91
+ details[:reduction] = {
92
+ original_size: result.original_size, minimized_size: result.items.length,
93
+ trials: result.trials, complete: result.complete
94
+ }
95
+ regression = persist_fuzz_regression(
96
+ mismatch.details, result, grammar_path, external&.[](1)
97
+ )
98
+ details[:regression] = regression if regression
99
+ details
100
+ end
101
+
102
+ # @rbs (fuzz_mismatch mismatch, DeltaReducer::Result result, String grammar_path,
103
+ # fuzz_external_description? external) -> Hash[Symbol, String]?
104
+ def persist_fuzz_regression(mismatch, result, grammar_path, external)
105
+ return if @options.fetch(:fuzz_save_regression, true) == false
106
+
107
+ directory = @options.fetch(:fuzz_regression_dir, "test/fuzz/regressions")
108
+ FileUtils.mkdir_p(directory)
109
+ document = fuzz_regression_document(mismatch, result, grammar_path, external)
110
+ source = "#{JSON.pretty_generate(document)}\n"
111
+ digest = Digest::SHA256.hexdigest(source)
112
+ seed = @options.fetch(:fuzz_seed, 0)
113
+ path = File.join(directory, "fuzz-seed-#{seed}-#{digest.slice(0, 16)}.json")
114
+ validate_fuzz_regression_target!(path)
115
+ atomic_write_ir(path, source)
116
+ { path: path, sha256: digest }
117
+ end
118
+
119
+ # @rbs (String path) -> void
120
+ def validate_fuzz_regression_target!(path)
121
+ return unless File.symlink?(path) || (File.exist?(path) && File.stat(path).nlink > 1)
122
+
123
+ raise Ibex::Error, "(fuzz):1:1: regression output refuses symlinks and files with multiple hard links"
124
+ end
125
+
126
+ # @rbs (fuzz_mismatch mismatch, DeltaReducer::Result result, String grammar_path,
127
+ # fuzz_external_description? external) -> Hash[Symbol, Object?]
128
+ def fuzz_regression_document(mismatch, result, grammar_path, external)
129
+ document = {
130
+ ibex_report: "fuzz-regression", schema_version: 1, grammar: grammar_path,
131
+ seed: @options.fetch(:fuzz_seed, 0), kind: mismatch[:kind],
132
+ sentence: mismatch[:sentence],
133
+ algorithms: mismatch[:outcomes].keys.reject { |name| name == :external }.map(&:to_s),
134
+ original_tokens: mismatch[:tokens], minimized_tokens: result.items,
135
+ bounds: mismatch[:bounds],
136
+ reduction: {
137
+ original_size: result.original_size, minimized_size: result.items.length,
138
+ trials: result.trials, complete: result.complete
139
+ }
140
+ } #: Hash[Symbol, Object?]
141
+ document[:external] = external if external
142
+ document
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "../artifact_set"
5
+ require_relative "../generation_input"
6
+ require_relative "../generation_transaction"
7
+
8
+ module Ibex
9
+ # Collects, verifies, and transactionally publishes CLI generation outputs.
10
+ module CLIGenerationArtifacts
11
+ # @rbs!
12
+ # private def report_status: (String) -> void
13
+ # private def verify_file: (String, String, String) -> void
14
+ # private def default_output_path: (String, String) -> String
15
+ # private def configuration_value: (String) -> Object?
16
+ # private def effective_configuration: () -> Configuration::Resolver
17
+
18
+ private
19
+
20
+ # @rbs () -> void
21
+ def begin_artifact_generation
22
+ @generation_artifacts = ArtifactSet.new
23
+ @generation_statuses = [] #: Array[String]
24
+ @generation_sources = [] #: Array[String]
25
+ @generation_inputs = [] #: Array[GenerationInput]
26
+ @generation_automaton = nil
27
+ end
28
+
29
+ # @rbs (Symbol kind, String path, String content, ?mode: Integer?, ?status: bool) -> Artifact
30
+ def register_artifact(kind, path, content, mode: nil, status: false)
31
+ artifact = generation_artifacts.add(kind: kind, path: path, content: content, mode: mode)
32
+ @generation_statuses << "wrote #{path}" if status
33
+ artifact
34
+ end
35
+
36
+ # @rbs (Array[String] source_paths) -> Integer
37
+ def finish_artifact_generation(_source_paths)
38
+ add_generation_manifest if @options[:manifest]
39
+ return 0 if @defer_generation_publication
40
+
41
+ ensure_generation_inputs_stable!
42
+ if @options[:verify_output]
43
+ verify_artifact_generation
44
+ ensure_generation_inputs_stable!
45
+ elsif !generation_artifacts.empty?
46
+ GenerationTransaction.new(
47
+ generation_artifacts,
48
+ warning: ->(message) { @stderr.puts("ibex: warning: #{message}") },
49
+ stability_check: -> { generation_inputs_stable? },
50
+ source_records: @generation_inputs
51
+ ).commit
52
+ @generation_statuses.each { |message| report_status(message) }
53
+ end
54
+ 0
55
+ end
56
+
57
+ # @rbs () -> void
58
+ def add_generation_manifest
59
+ require_relative "../generation_manifest"
60
+
61
+ path = manifest_output_path
62
+ source = GenerationManifest.render(
63
+ generation_artifacts,
64
+ source_records: @generation_inputs,
65
+ options: generation_manifest_options
66
+ )
67
+ register_artifact(:manifest, path, source, status: true)
68
+ end
69
+
70
+ # @rbs () -> void
71
+ def verify_artifact_generation
72
+ generation_artifacts.each do |artifact|
73
+ verify_file(artifact.path, artifact.content, artifact_label(artifact.kind))
74
+ end
75
+ parser = generation_artifacts.find { |artifact| artifact.kind == :parser }
76
+ report_status("verified #{parser.path}") if parser
77
+ end
78
+
79
+ # @rbs (Symbol kind) -> String
80
+ def artifact_label(kind)
81
+ {
82
+ parser: "parser",
83
+ rbs: "RBS signature",
84
+ action_source: "action source",
85
+ manifest: "generation manifest"
86
+ }.fetch(kind, kind.to_s.tr("_", " "))
87
+ end
88
+
89
+ # @rbs () -> ArtifactSet
90
+ def generation_artifacts
91
+ @generation_artifacts || raise(Ibex::Error, "(generation):1:1: artifact collection is not active")
92
+ end
93
+
94
+ # @rbs () -> bool
95
+ def generation_inputs_stable?
96
+ @generation_inputs.all?(&:current?)
97
+ end
98
+
99
+ # @rbs () -> void
100
+ def ensure_generation_inputs_stable!
101
+ return if generation_inputs_stable?
102
+
103
+ raise GenerationTransaction::SourceChanged, "(generation):1:1: source changed while rendering outputs"
104
+ end
105
+
106
+ # @rbs (String path, String source) -> GenerationInput
107
+ def record_generation_input(path, source)
108
+ input = GenerationInput.new(path, source)
109
+ @generation_inputs.reject! { |entry| entry.path == input.path }
110
+ @generation_inputs << input
111
+ @generation_sources = @generation_inputs.map(&:path)
112
+ input
113
+ end
114
+
115
+ # @rbs () -> String
116
+ def manifest_output_path
117
+ configured = @options[:manifest]
118
+ return configured if configured.is_a?(String) && !configured.empty?
119
+
120
+ parser = generation_artifacts.find { |artifact| artifact.kind == :parser }
121
+ raise Ibex::Error, "(generation):1:1: generation manifest requires a parser artifact" unless parser
122
+
123
+ default_output_path(parser.path, ".ibex.json")
124
+ end
125
+
126
+ # @rbs () -> Hash[String, Object?]
127
+ def generation_manifest_options
128
+ keys = %i[
129
+ action_source algorithm counterexample_max_configurations counterexample_max_tokens debug
130
+ embedded emit entry_isolation executable frozen line_convert line_convert_all messages mode omit_actions rbs
131
+ superclass table
132
+ ]
133
+ options = {} #: Hash[String, Object?]
134
+ keys.each do |key|
135
+ value = @options[key]
136
+ options[key.to_s] = value unless value.nil?
137
+ end
138
+ options["cst_trivia"] = effective_cst_trivia.to_s
139
+ append_ir_manifest_options(options)
140
+ options
141
+ end
142
+
143
+ # @rbs (Hash[String, Object?] options) -> void
144
+ def append_ir_manifest_options(options)
145
+ automaton = @generation_automaton
146
+ return unless automaton
147
+
148
+ grammar = automaton.grammar
149
+ contract = grammar.parser_contract
150
+ options["grammar_ir"] = {
151
+ "schema_version" => grammar.schema_version,
152
+ "digest" => automaton.grammar_digest,
153
+ "parser_contract" => stringify_contract(contract)
154
+ }
155
+ options["automaton_ir"] = {
156
+ "schema_version" => automaton.schema_version,
157
+ "algorithm" => automaton.algorithm,
158
+ "entry_construction" => automaton.entry_construction,
159
+ "construction_authority" => construction_authority
160
+ }
161
+ options["effective_configuration"] = manifest_effective_configuration
162
+ end
163
+
164
+ # @rbs () -> String
165
+ def construction_authority
166
+ @options[:from] == "automaton-ir" ? "embedded_automaton" : "grammar_contract"
167
+ end
168
+
169
+ # @rbs () -> Array[Hash[String, Object?]]
170
+ def manifest_effective_configuration
171
+ values = effective_configuration.to_h.fetch("configuration")
172
+ return values unless @options[:from] == "automaton-ir"
173
+
174
+ values.reject { |entry| %w[parser.algorithm parser.entries].include?(entry.fetch("key")) }
175
+ end
176
+
177
+ # @rbs (IR::ParserContract contract) -> Hash[String, Object?]
178
+ def stringify_contract(contract)
179
+ contract.to_h.to_h do |name, entry|
180
+ [name.to_s, entry.to_h { |key, value| [key.to_s, value] }]
181
+ end
182
+ end
183
+
184
+ # @rbs () -> Symbol
185
+ def effective_cst_trivia
186
+ configuration_value("cst.trivia") #: Symbol
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ # Optional example-keyed error-message support for parser generation.
6
+ module CLIGenerationErrorMessages
7
+ # @rbs!
8
+ # private def record_generation_input: (String, String) -> GenerationInput
9
+
10
+ private
11
+
12
+ # @rbs (OptionParser options) -> void
13
+ def add_error_messages_generation_option(options)
14
+ options.on("--messages=FILE", "embed example-keyed syntax error messages") do |value|
15
+ @options[:messages] = value
16
+ end
17
+ end
18
+
19
+ # @rbs (IR::Automaton automaton) -> Hash[Integer, String | { id: String, message: String }]
20
+ def configured_error_messages(automaton)
21
+ path = @options[:messages]
22
+ return {} unless path
23
+
24
+ require_relative "../error_messages"
25
+ source = File.binread(path)
26
+ record_generation_input(path, source)
27
+ document = ErrorMessages.parse(source, file: path)
28
+ ErrorMessages.records_for(document, automaton, file: path)
29
+ end
30
+
31
+ # @rbs () -> void
32
+ def validate_messages_options
33
+ return unless @options[:messages]
34
+ raise Ibex::Error, "(cli):1:1: --messages is available only with --emit=ruby" unless @options[:emit] == "ruby"
35
+
36
+ raise Ibex::Error, "(cli):1:1: --messages cannot be combined with --check-only" if @options[:check_only]
37
+ end
38
+ end
39
+ end