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,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+ require_relative "../frontend"
6
+
7
+ module Ibex
8
+ # Batch frontend diagnostics subcommand.
9
+ module CLIDiagnostics
10
+ DIAGNOSTICS_SCHEMA_VERSION = 1 #: Integer
11
+ DEFAULT_MAX_DIAGNOSTICS = 20 #: Integer
12
+
13
+ # @rbs!
14
+ # type diagnostic_settings = Hash[Symbol, untyped]
15
+ #
16
+ # private def input_path: (Array[String]) -> String
17
+ # private def local_configuration_value: (Hash[Symbol, Object?], String) -> Object?
18
+ # private def set_local_configuration_option: (Hash[Symbol, Object?], Symbol, Object?) -> void
19
+
20
+ private
21
+
22
+ # @rbs (Array[String] arguments) -> Integer
23
+ def run_diagnose_command(arguments)
24
+ settings = {
25
+ format: "text", max_diagnostics: DEFAULT_MAX_DIAGNOSTICS,
26
+ mode: Configuration::Registry.fetch("grammar.mode").default, configuration_explicit: []
27
+ } #: diagnostic_settings
28
+ parser = diagnostics_option_parser(settings)
29
+ remaining = parser.parse(arguments)
30
+ settings[:mode] = local_configuration_value(settings, "grammar.mode")
31
+ if settings[:help]
32
+ @stdout.puts(parser)
33
+ return 0
34
+ end
35
+
36
+ path = input_path(remaining)
37
+ result = Frontend::Parser.new(File.binread(path), file: path, mode: settings.fetch(:mode))
38
+ .parse_with_diagnostics(max_diagnostics: settings.fetch(:max_diagnostics))
39
+ result = resolve_diagnostic_result(result, path, settings.fetch(:mode)) if result.success?
40
+ write_diagnostics(result, format: settings.fetch(:format))
41
+ result.success? ? 0 : 1
42
+ end
43
+
44
+ # Cross-file recovery is deliberately bounded to the first resolver error.
45
+ # @rbs (Frontend::ParseResult result, String path, Symbol mode) -> Frontend::ParseResult
46
+ def resolve_diagnostic_result(result, path, mode)
47
+ Frontend::Resolver.new(path, mode: mode).resolve
48
+ result
49
+ rescue Frontend::ResolutionIOError
50
+ raise
51
+ rescue Ibex::Error => e
52
+ Frontend::ParseResult.new(diagnostics: [resolution_diagnostic(e, path)], ast: nil, document: nil)
53
+ end
54
+
55
+ # @rbs (Ibex::Error error, String fallback_file) -> Frontend::Diagnostic
56
+ def resolution_diagnostic(error, fallback_file)
57
+ match = error.message.match(/\A(.+):(\d+):(\d+): (.*)\z/m)
58
+ location = if match
59
+ Frontend::Location.new(
60
+ file: match[1].to_s, line: Integer(match[2].to_s), column: Integer(match[3].to_s)
61
+ )
62
+ else
63
+ Frontend::Location.new(file: fallback_file, line: 1, column: 1)
64
+ end
65
+ message = match ? match[4].to_s : error.message
66
+ Frontend::Diagnostic.new(
67
+ code: "frontend.resolution_error", phase: :syntax, message: message,
68
+ location: location, rendered: error.message
69
+ )
70
+ end
71
+
72
+ # @rbs (diagnostic_settings settings) -> OptionParser
73
+ def diagnostics_option_parser(settings)
74
+ OptionParser.new do |options|
75
+ options.banner = "Usage: ibex diagnose [options] grammarfile"
76
+ options.on("--format=FORMAT", %w[text json], "text or json") { |value| settings[:format] = value }
77
+ options.on("--max-diagnostics=N", "positive diagnostic limit") do |value|
78
+ settings[:max_diagnostics] = positive_diagnostic_limit(value)
79
+ end
80
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
81
+ set_local_configuration_option(settings, :mode, value.to_sym)
82
+ end
83
+ options.on("--help", "show help") { settings[:help] = true }
84
+ end
85
+ end
86
+
87
+ # @rbs (String value) -> Integer
88
+ def positive_diagnostic_limit(value)
89
+ limit = Integer(value, 10)
90
+ return limit if limit.positive?
91
+
92
+ raise OptionParser::InvalidArgument, "max diagnostics must be positive"
93
+ rescue ArgumentError
94
+ raise OptionParser::InvalidArgument, "max diagnostics must be a positive integer"
95
+ end
96
+
97
+ # @rbs (Frontend::ParseResult result, format: String) -> void
98
+ def write_diagnostics(result, format:)
99
+ if format == "json"
100
+ @stdout.puts(JSON.pretty_generate(diagnostics_document(result)))
101
+ else
102
+ result.diagnostics.each { |diagnostic| @stdout.puts(localized_diagnostic_text(diagnostic)) }
103
+ end
104
+ end
105
+
106
+ # @rbs (Frontend::ParseResult result) -> Hash[String, Object?]
107
+ def diagnostics_document(result)
108
+ {
109
+ "ibex_diagnostics" => "frontend",
110
+ "schema_version" => DIAGNOSTICS_SCHEMA_VERSION,
111
+ "success" => result.success?,
112
+ "ast_available" => !result.ast.nil?,
113
+ "diagnostics" => result.diagnostics.map { |diagnostic| localized_diagnostic_hash(diagnostic) }
114
+ }
115
+ end
116
+
117
+ # @rbs (Frontend::Diagnostic diagnostic) -> String
118
+ def localized_diagnostic_text(diagnostic)
119
+ return diagnostic.to_s if @language == "en"
120
+
121
+ "#{diagnostic.location}: #{localized_diagnostic_message(diagnostic)}"
122
+ end
123
+
124
+ # @rbs (Frontend::Diagnostic diagnostic) -> Hash[Symbol, Object?]
125
+ def localized_diagnostic_hash(diagnostic)
126
+ diagnostic.to_h.merge(message: localized_diagnostic_message(diagnostic))
127
+ end
128
+
129
+ # @rbs (Frontend::Diagnostic diagnostic) -> String
130
+ def localized_diagnostic_message(diagnostic)
131
+ id = "diagnostic.#{diagnostic.code}"
132
+ id = "diagnostic.generic" unless Messages.known?(id)
133
+ Messages.translate(id, language: @language, detail: diagnostic.message)
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../frontend"
5
+ require_relative "../normalize"
6
+ require_relative "../codegen/documentation"
7
+
8
+ module Ibex
9
+ # Generates standalone grammar documentation from the canonical include closure.
10
+ module CLIDocumentation
11
+ # @rbs!
12
+ # type documentation_settings = Hash[Symbol, untyped]
13
+ #
14
+ # private def input_path: (Array[String]) -> String
15
+ # private def same_file_target?: (String left, String right) -> bool
16
+ # private def atomic_write_ir: (String path, String source) -> void
17
+ # private def local_configuration_value: (Hash[Symbol, Object?], String) -> Object?
18
+ # private def set_local_configuration_option: (Hash[Symbol, Object?], Symbol, Object?) -> void
19
+
20
+ private
21
+
22
+ # @rbs (Array[String] arguments) -> Integer
23
+ def run_documentation_command(arguments)
24
+ settings = {
25
+ format: "markdown", mode: Configuration::Registry.fetch("grammar.mode").default,
26
+ configuration_explicit: []
27
+ } #: documentation_settings
28
+ parser = documentation_option_parser(settings)
29
+ remaining = parser.parse(arguments)
30
+ settings[:mode] = local_configuration_value(settings, "grammar.mode")
31
+ if settings[:help]
32
+ @stdout.puts(parser)
33
+ return 0
34
+ end
35
+
36
+ path = input_path(remaining)
37
+ output = settings[:output]
38
+ resolution = Frontend::Resolver.new(path, mode: settings.fetch(:mode)).resolve
39
+ reject_documentation_output_collision(output, resolution.files) if output
40
+ grammar = Normalizer.new(resolution, mode: settings.fetch(:mode)).normalize
41
+ rendered = Codegen::Documentation.render(grammar, format: settings.fetch(:format))
42
+ output ? atomic_write_ir(output, rendered) : @stdout.write(rendered)
43
+ 0
44
+ end
45
+
46
+ # @rbs (String output, Array[String] grammar_files) -> void
47
+ def reject_documentation_output_collision(output, grammar_files)
48
+ collision = grammar_files.find { |grammar_file| same_file_target?(grammar_file, output) }
49
+ return unless collision
50
+
51
+ raise Ibex::Error,
52
+ "(cli):1:1: doc input and output paths must be distinct; output aliases grammar source #{collision}"
53
+ end
54
+
55
+ # @rbs (documentation_settings settings) -> OptionParser
56
+ def documentation_option_parser(settings)
57
+ OptionParser.new do |options|
58
+ options.banner = "Usage: ibex doc [options] grammarfile"
59
+ options.on("--format=FORMAT", Codegen::Documentation::FORMATS, "markdown, html, or railroad") do |value|
60
+ settings[:format] = value
61
+ end
62
+ options.on("-o FILE", "--output=FILE", "write atomically to FILE") { |value| settings[:output] = value }
63
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
64
+ set_local_configuration_option(settings, :mode, value.to_sym)
65
+ end
66
+ options.on("--help", "show help") { settings[:help] = true }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require "optparse"
6
+ require_relative "../equiv"
7
+
8
+ module Ibex
9
+ # CLI entry point for bounded language comparison.
10
+ module CLIEquiv
11
+ # @rbs!
12
+ # type equiv_options = Hash[Symbol, untyped]
13
+ # private def normalize_grammar_path: (String) -> IR::Grammar
14
+ # private def configuration_value: (String) -> untyped
15
+ # private def set_configuration_option: (Symbol, Object?) -> void
16
+ # private def local_configuration_value: (Hash[Symbol, Object?], String) -> Object?
17
+ # private def set_local_configuration_option: (Hash[Symbol, Object?], Symbol, Object?) -> void
18
+
19
+ private
20
+
21
+ # @rbs (Array[String] arguments) -> Integer
22
+ def run_equiv_command(arguments)
23
+ settings = equiv_options(arguments)
24
+ if settings[:help]
25
+ @stdout.puts(settings.fetch(:help))
26
+ return 0
27
+ end
28
+ paths = settings.fetch(:paths)
29
+ raise Ibex::Error, "(equiv):1:1: equiv requires exactly two grammar or IR files" unless paths.length == 2
30
+
31
+ algorithm = settings.fetch(:algorithm)
32
+ left = load_equiv_automaton(paths.fetch(0), algorithm, explicit: algorithm_explicit_equiv?(settings))
33
+ right = load_equiv_automaton(paths.fetch(1), algorithm, explicit: algorithm_explicit_equiv?(settings))
34
+ comparison = Equiv.new(
35
+ left, right,
36
+ sample_count: settings.fetch(:sample_count), seed: settings.fetch(:seed),
37
+ max_tokens: settings.fetch(:max_tokens), max_configurations: settings.fetch(:max_configurations),
38
+ max_actions: settings.fetch(:max_actions), max_stack: settings.fetch(:max_stack),
39
+ rule_map: settings.fetch(:rule_map)
40
+ )
41
+ write_equiv_report(comparison.run, settings.fetch(:format))
42
+ 0
43
+ rescue Equiv::Difference => e
44
+ write_equiv_report(e.details, settings&.fetch(:format) || "json")
45
+ 1
46
+ rescue Equiv::BudgetExceeded => e
47
+ report = { ibex_report: "equiv", schema_version: 1 }.merge(e.details)
48
+ write_equiv_report(report, settings&.fetch(:format) || "json")
49
+ 2
50
+ end
51
+
52
+ # @rbs (Array[String] arguments) -> equiv_options
53
+ def equiv_options(arguments)
54
+ settings = default_equiv_options
55
+ parser = OptionParser.new do |options|
56
+ options.banner = "Usage: ibex equiv [options] LEFT RIGHT"
57
+ add_equiv_search_options(options, settings)
58
+ options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "algorithm for grammar inputs") do |value|
59
+ set_local_configuration_option(settings, :algorithm, value.to_sym)
60
+ end
61
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
62
+ set_local_configuration_option(settings, :mode, value.to_sym)
63
+ set_configuration_option(:mode, value.to_sym)
64
+ end
65
+ options.on("--map=OLD=NEW", "declare a nonterminal correspondence; repeatable") do |value|
66
+ old_name, new_name = value.split("=", 2)
67
+ if old_name.nil? || old_name.empty? || new_name.nil? || new_name.empty?
68
+ raise OptionParser::InvalidArgument, "--map must be OLD=NEW"
69
+ end
70
+
71
+ settings.fetch(:rule_map)[old_name] = new_name
72
+ end
73
+ options.on("--format=FORMAT", %w[json text], "json or text") { |value| settings[:format] = value }
74
+ options.on("--help", "show help") { settings[:help] = options.to_s }
75
+ end
76
+ settings[:paths] = parser.parse(arguments)
77
+ settings[:algorithm] = local_configuration_value(settings, "parser.algorithm")
78
+ settings
79
+ end
80
+
81
+ # @rbs () -> equiv_options
82
+ def default_equiv_options
83
+ {
84
+ paths: [], sample_count: 100, seed: 0, max_tokens: 8, max_configurations: 50_000,
85
+ max_actions: Equiv::DEFAULT_MAX_ACTIONS, max_stack: Equiv::DEFAULT_MAX_STACK,
86
+ algorithm: Configuration::Registry.fetch("parser.algorithm").default,
87
+ mode: Configuration::Registry.fetch("grammar.mode").default, format: "json", rule_map: {},
88
+ configuration_explicit: []
89
+ }
90
+ end
91
+
92
+ # @rbs (OptionParser options, equiv_options settings) -> void
93
+ def add_equiv_search_options(options, settings)
94
+ options.on("--samples=N", Integer, "samples generated in each direction") do |value|
95
+ settings[:sample_count] = positive_equiv_option(value, "samples")
96
+ end
97
+ options.on("--seed=N", Integer, "deterministic sampling seed") { |value| settings[:seed] = value }
98
+ options.on("--max-tokens=N", Integer, "maximum counterexample length") do |value|
99
+ settings[:max_tokens] = positive_equiv_option(value, "max-tokens")
100
+ end
101
+ options.on("--max-configurations=N", Integer, "product-state exploration budget") do |value|
102
+ settings[:max_configurations] = positive_equiv_option(value, "max-configurations")
103
+ end
104
+ options.on("--max-actions=N", Integer, "actions per simulated token sequence") do |value|
105
+ settings[:max_actions] = positive_equiv_option(value, "max-actions")
106
+ end
107
+ options.on("--max-stack=N", Integer, "simulated parser stack depth") do |value|
108
+ settings[:max_stack] = positive_equiv_option(value, "max-stack")
109
+ end
110
+ end
111
+
112
+ # @rbs (String path, Symbol algorithm, explicit: bool) -> IR::Automaton
113
+ def load_equiv_automaton(path, algorithm, explicit:)
114
+ source = File.binread(path)
115
+ if source.lstrip.start_with?("{")
116
+ value = IR::Validator.validate(source)
117
+ if value.is_a?(IR::Automaton)
118
+ raise Ibex::Error, "(cli):1:1: --algorithm cannot be combined with Automaton IR analysis input" if explicit
119
+
120
+ return value
121
+ end
122
+ return build_equiv_automaton(value, algorithm, explicit) if value.is_a?(IR::Grammar)
123
+
124
+ raise Ibex::Error, "#{path}:1:1: equiv does not accept Lexer IR"
125
+ end
126
+
127
+ build_equiv_automaton(normalize_grammar_path(path), algorithm, explicit)
128
+ end
129
+
130
+ # @rbs (IR::Grammar grammar, Symbol algorithm, bool algorithm_explicit) -> IR::Automaton
131
+ def build_equiv_automaton(grammar, algorithm, algorithm_explicit)
132
+ explicit_keys = algorithm_explicit ? [:algorithm] : [] #: Array[Symbol]
133
+ active = activate_analysis_grammar(
134
+ grammar, options: { algorithm: algorithm }, explicit_keys: explicit_keys
135
+ )
136
+ LALR::Builder.new(
137
+ active, algorithm: configuration_value("parser.algorithm"),
138
+ entry_isolation: configuration_value("parser.entries") == :isolated
139
+ ).build
140
+ end
141
+
142
+ # @rbs (Hash[Symbol, Object?] report, String format) -> void
143
+ def write_equiv_report(report, format)
144
+ if format == "json"
145
+ @stdout.puts(JSON.pretty_generate(report))
146
+ else
147
+ @stdout.puts("result=#{report.fetch(:result)}")
148
+ @stdout.puts("witness=#{report[:witness].inspect}") if report[:witness]
149
+ @stdout.puts(report.fetch(:statement))
150
+ end
151
+ end
152
+
153
+ # @rbs (equiv_options settings) -> bool
154
+ def algorithm_explicit_equiv?(settings)
155
+ settings.fetch(:configuration_explicit).include?(:algorithm)
156
+ end
157
+
158
+ # @rbs!
159
+ # private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
160
+ # ?explicit_keys: Array[Symbol]) -> IR::Grammar
161
+
162
+ # @rbs (Integer value, String name) -> Integer
163
+ def positive_equiv_option(value, name)
164
+ return value if value.positive?
165
+
166
+ raise OptionParser::InvalidArgument, "--#{name} must be positive"
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+ require_relative "../error_messages"
5
+ require_relative "../ir"
6
+
7
+ module Ibex
8
+ # CLI subcommand and generation-file handling for example-keyed errors.
9
+ module CLIErrorMessages
10
+ # @rbs!
11
+ # private def print_help: (OptionParser) -> Integer
12
+ # private def input_path: (Array[String]) -> String
13
+ # private def warning_categories: (String) -> Array[Symbol]
14
+ # private def report_status: (String) -> void
15
+ # private def handle_grammar_warnings: (IR::Grammar, String) -> void
16
+ # private def build_automaton: (IR::Grammar, String) -> IR::Automaton
17
+ # private def prepare_loaded_automaton: (IR::Automaton, String) -> void
18
+ # private def default_output_path: (String, String) -> String
19
+ # private def same_file_target?: (String, String) -> bool
20
+ # private def normalize_grammar_path: (String) -> IR::Grammar
21
+ # private def record_generation_input: (String, String) -> GenerationInput
22
+ # private def select_configuration_mode: (String) -> void
23
+ # private def set_configuration_option: (Symbol, Object?) -> void
24
+
25
+ private
26
+
27
+ # @rbs (Array[String] arguments) -> Integer
28
+ def run_error_messages_command(arguments)
29
+ parser = error_messages_option_parser
30
+ remaining = parser.parse(arguments)
31
+ return print_help(parser) if @options[:help]
32
+ if @options[:messages_list] && @options[:messages_update]
33
+ raise Ibex::Error, "(cli):1:1: errors command accepts only one of --list or --update"
34
+ end
35
+ unless @options[:messages_list] || @options[:messages_update]
36
+ raise Ibex::Error, "(cli):1:1: errors command requires --list or --update[=FILE]"
37
+ end
38
+
39
+ input = input_path(remaining)
40
+ automaton = automaton_for_error_messages(input)
41
+ if @options[:messages_list]
42
+ @stdout.write(ErrorMessages.render(automaton, **error_message_search_limits))
43
+ else
44
+ write_error_messages(automaton, input)
45
+ end
46
+ 0
47
+ end
48
+
49
+ # @rbs () -> OptionParser
50
+ def error_messages_option_parser
51
+ OptionParser.new do |options|
52
+ options.banner = "Usage: ibex errors (--list | --update[=FILE]) [options] grammarfile"
53
+ options.on("--list", "list shortest error sentences on stdout") { @options[:messages_list] = true }
54
+ options.on("--update[=FILE]", "update messages (defaults to grammar.messages)") do |value|
55
+ @options[:messages_update] = value || true
56
+ end
57
+ options.on("--max-tokens=N", Integer, "maximum error-sentence token budget") do |value|
58
+ @options[:messages_max_tokens] = positive_messages_limit(value, "--max-tokens")
59
+ end
60
+ options.on("--max-configurations=N", Integer, "maximum error-sentence configuration budget") do |value|
61
+ @options[:messages_max_configurations] = positive_messages_limit(value, "--max-configurations")
62
+ end
63
+ options.on("--from=FORMAT", %w[grammar-ir automaton-ir], "resume from IR JSON") do |value|
64
+ @options[:from] = value
65
+ end
66
+ options.on("--mode=MODE", %w[default extended], "grammar mode") { |value| select_configuration_mode(value) }
67
+ options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
68
+ set_configuration_option(:algorithm, value.to_sym)
69
+ @options[:messages_algorithm_explicit] = true
70
+ end
71
+ options.on("--warnings=CATEGORIES", "all, error, all,error, or none") do |value|
72
+ @options[:warnings] = warning_categories(value)
73
+ end
74
+ options.on("-S", "--output-status", "show pipeline status") { @options[:status] = true }
75
+ options.on("--help", "show help") { @options[:help] = true }
76
+ end
77
+ end
78
+
79
+ # @rbs (String input_path) -> IR::Automaton
80
+ def automaton_for_error_messages(input_path)
81
+ return automaton_from_ir_for_messages(input_path) if @options[:from]
82
+
83
+ report_status("reading #{input_path}")
84
+ grammar = normalize_grammar_path(input_path)
85
+ grammar = activate_analysis_grammar(grammar)
86
+ handle_grammar_warnings(grammar, input_path)
87
+ build_automaton(grammar, input_path)
88
+ end
89
+
90
+ # @rbs (String input_path) -> IR::Automaton
91
+ def automaton_from_ir_for_messages(input_path)
92
+ report_status("reading #{input_path}")
93
+ value = IR::Validator.validate(File.read(input_path))
94
+ expected = @options[:from] == "grammar-ir" ? IR::Grammar : IR::Automaton
95
+ raise Ibex::Error, "#{input_path}:1:1: expected #{@options[:from]} input" unless value.is_a?(expected)
96
+ if value.is_a?(IR::Automaton) && @options[:messages_algorithm_explicit]
97
+ raise Ibex::Error, "(cli):1:1: --algorithm cannot be combined with --from=automaton-ir"
98
+ end
99
+
100
+ grammar = if value.is_a?(IR::Grammar)
101
+ activate_analysis_grammar(value)
102
+ elsif value.is_a?(IR::Automaton)
103
+ value.grammar
104
+ else
105
+ raise Ibex::Error, "#{input_path}:1:1: expected #{@options[:from]} input"
106
+ end
107
+ handle_grammar_warnings(grammar, input_path)
108
+ return build_automaton(grammar, input_path) if value.is_a?(IR::Grammar)
109
+
110
+ if value.is_a?(IR::Automaton)
111
+ prepare_loaded_automaton(value, input_path)
112
+ return value
113
+ end
114
+
115
+ raise Ibex::Error, "#{input_path}:1:1: expected #{@options[:from]} input"
116
+ end
117
+
118
+ # @rbs (IR::Automaton automaton, String input_path) -> void
119
+ def write_error_messages(automaton, input_path)
120
+ configured = @options[:messages_update]
121
+ path = configured == true ? default_output_path(input_path, ".messages") : configured
122
+ raise Ibex::Error, "(cli):1:1: messages update path must not be empty" unless path.is_a?(String) && !path.empty?
123
+ if same_file_target?(path, input_path)
124
+ raise Ibex::Error, "(cli):1:1: messages update path must differ from the input path"
125
+ end
126
+
127
+ existing = if File.exist?(path)
128
+ ErrorMessages.load(path)
129
+ else
130
+ ErrorMessages::Document.new(entries: [])
131
+ end
132
+ update = ErrorMessages.update(automaton, existing: existing, **error_message_search_limits)
133
+ target_path = File.symlink?(path) ? File.realpath(path) : path
134
+ directory = File.dirname(File.expand_path(target_path))
135
+ Tempfile.create([".ibex-messages-", ".tmp"], directory, encoding: "UTF-8") do |file|
136
+ file.write(update.source)
137
+ File.chmod(messages_file_mode(target_path), file.path)
138
+ file.flush
139
+ file.fsync
140
+ File.rename(file.path, target_path)
141
+ end
142
+ write_error_message_update_report(update)
143
+ report_status("wrote #{path}")
144
+ end
145
+
146
+ # @rbs () -> { max_tokens: Integer, max_configurations: Integer }
147
+ def error_message_search_limits
148
+ {
149
+ max_tokens: @options.fetch(:messages_max_tokens, ErrorMessages::SentenceSearch::DEFAULT_MAX_TOKENS),
150
+ max_configurations: @options.fetch(
151
+ :messages_max_configurations, ErrorMessages::SentenceSearch::DEFAULT_MAX_CONFIGURATIONS
152
+ )
153
+ }
154
+ end
155
+
156
+ # @rbs (Integer value, String option) -> Integer
157
+ def positive_messages_limit(value, option)
158
+ return value if value.positive?
159
+
160
+ raise Ibex::Error, "(cli):1:1: #{option} must be positive"
161
+ end
162
+
163
+ # @rbs (ErrorMessages::Update update) -> void
164
+ def write_error_message_update_report(update)
165
+ classifications = {
166
+ "uncovered" => update.uncovered, "unreachable" => update.unreachable, "moved" => update.moved
167
+ }
168
+ classifications.each do |label, items|
169
+ items.each { |item| @stdout.puts("#{label}: #{item}") }
170
+ end
171
+ end
172
+
173
+ # @rbs (String path) -> Integer
174
+ def messages_file_mode(path)
175
+ return File.stat(path).mode & 0o777 if File.exist?(path)
176
+
177
+ 0o666 & ~File.umask
178
+ end
179
+
180
+ # @rbs!
181
+ # private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
182
+ # ?explicit_keys: Array[Symbol]) -> IR::Grammar
183
+ end
184
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+ require_relative "../codegen/explain"
6
+
7
+ module Ibex
8
+ # Dedicated command-line view for selected parser conflicts.
9
+ module CLIExplain
10
+ # @rbs!
11
+ # private def print_help: (OptionParser) -> Integer
12
+ # private def input_path: (Array[String]) -> String
13
+ # private def positive_counterexample_limit: (Integer, String) -> Integer
14
+ # private def normalize_grammar_path: (String) -> IR::Grammar
15
+ # private def configuration_value: (String) -> untyped
16
+ # private def set_configuration_option: (Symbol, Object?) -> void
17
+
18
+ private
19
+
20
+ # @rbs (Array[String] arguments) -> Integer
21
+ def run_explain_command(arguments)
22
+ parser = explain_option_parser
23
+ remaining = parser.parse(arguments)
24
+ return print_help(parser) if @options[:help]
25
+
26
+ path = input_path(remaining)
27
+ grammar = activate_analysis_grammar(normalize_grammar_path(path))
28
+ automaton = LALR::Builder.new(
29
+ grammar, algorithm: configuration_value("parser.algorithm"),
30
+ entry_isolation: configuration_value("parser.entries") == :isolated
31
+ ).build
32
+ explanation = Codegen::Explain.new(
33
+ automaton, state: @options[:explain_state], token: @options[:explain_token],
34
+ max_tokens: @options.fetch(:counterexample_max_tokens),
35
+ max_configurations: @options.fetch(:counterexample_max_configurations)
36
+ )
37
+ output = @options[:explain_format] == "json" ? JSON.pretty_generate(explanation.to_h) : explanation.render_text
38
+ @stdout.puts(output)
39
+ 0
40
+ end
41
+
42
+ # @rbs () -> OptionParser
43
+ def explain_option_parser
44
+ OptionParser.new do |options|
45
+ options.banner = "Usage: ibex explain [options] grammarfile"
46
+ options.on("--state=N", Integer, "select one automaton state") { |value| @options[:explain_state] = value }
47
+ options.on("--token=NAME", "select a grammar token name or exact display name") do |value|
48
+ @options[:explain_token] = value
49
+ end
50
+ options.on("--format=FORMAT", %w[text json], "text or json (default: text)") do |value|
51
+ @options[:explain_format] = value
52
+ end
53
+ options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
54
+ set_configuration_option(:algorithm, value.to_sym)
55
+ end
56
+ options.on("--entry-isolation", "build independent state sets for each start symbol") do
57
+ set_configuration_option(:entry_isolation, true)
58
+ end
59
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
60
+ set_configuration_option(:mode, value.to_sym)
61
+ end
62
+ add_explain_search_options(options)
63
+ options.on("--help", "show help") { @options[:help] = true }
64
+ end
65
+ end
66
+
67
+ # @rbs (OptionParser options) -> void
68
+ def add_explain_search_options(options)
69
+ options.on("--counterexample-max-tokens=N", Integer, "maximum counterexample search token budget") do |value|
70
+ @options[:counterexample_max_tokens] = positive_counterexample_limit(value, "--counterexample-max-tokens")
71
+ end
72
+ options.on(
73
+ "--counterexample-max-configurations=N", Integer, "maximum counterexample search configuration budget"
74
+ ) do |value|
75
+ @options[:counterexample_max_configurations] = positive_counterexample_limit(
76
+ value, "--counterexample-max-configurations"
77
+ )
78
+ end
79
+ end
80
+
81
+ # @rbs!
82
+ # private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
83
+ # ?explicit_keys: Array[Symbol]) -> IR::Grammar
84
+ end
85
+ end