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,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../grammar_tests"
5
+
6
+ module Ibex
7
+ # `%test` command parsing and human-readable result reporting.
8
+ module CLIGrammarTests
9
+ # @rbs!
10
+ # type grammar_test_settings = Hash[Symbol, untyped]
11
+ # private def print_help: (OptionParser) -> Integer
12
+ # private def resolve_grammar_path: (String) -> Frontend::Resolution
13
+ # private def handle_grammar_warnings: (IR::Grammar, String) -> void
14
+ # private def build_automaton: (IR::Grammar, String) -> IR::Automaton
15
+ # private def configuration_value: (String) -> untyped
16
+ # private def mark_configuration_option: (Symbol) -> void
17
+
18
+ private
19
+
20
+ # @rbs (Array[String] arguments) -> Integer
21
+ def run_grammar_tests_command(arguments)
22
+ settings = default_grammar_test_settings
23
+ options = grammar_test_option_parser(settings)
24
+ remaining = options.parse(arguments)
25
+ return print_help(options) if settings[:help]
26
+ unless settings[:coverage].nil? || settings[:coverage].between?(1, 100)
27
+ raise OptionParser::InvalidArgument, "coverage must be between 1 and 100"
28
+ end
29
+
30
+ path = single_grammar_test_path(remaining, options)
31
+ @options[:mode] = settings[:mode]
32
+ @options[:algorithm] = settings[:algorithm]
33
+ @options[:entry_isolation] = settings[:entry_isolation]
34
+ resolution = resolve_grammar_path(path)
35
+ grammar = normalize_analysis_grammar(resolution)
36
+ handle_grammar_warnings(grammar, path)
37
+ automaton = build_automaton(grammar, path)
38
+ runner = GrammarTests::Runner.new(automaton, timeout: settings[:timeout])
39
+ results = runner.run
40
+ failures = render_grammar_test_results(results)
41
+ coverage = settings[:coverage]
42
+ return failures unless coverage
43
+
44
+ render_grammar_test_coverage(runner.production_coverage(results), coverage, failures)
45
+ end
46
+
47
+ # @rbs () -> Hash[Symbol, untyped]
48
+ def default_grammar_test_settings
49
+ {
50
+ mode: Configuration::Registry.fetch("grammar.mode").default,
51
+ algorithm: Configuration::Registry.fetch("parser.algorithm").default,
52
+ entry_isolation: Configuration::Registry.fetch("parser.entries").default == :isolated,
53
+ timeout: GrammarTests::DEFAULT_TIMEOUT, coverage: nil
54
+ }
55
+ end
56
+
57
+ # @rbs (Hash[Symbol, untyped] settings) -> OptionParser
58
+ def grammar_test_option_parser(settings)
59
+ OptionParser.new do |options|
60
+ options.banner = "Usage: ibex test [options] grammarfile"
61
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
62
+ settings[:mode] = value.to_sym
63
+ mark_configuration_option(:mode)
64
+ end
65
+ options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
66
+ settings[:algorithm] = value.to_sym
67
+ mark_configuration_option(:algorithm)
68
+ end
69
+ options.on("--entry-isolation", "build independent state sets for each start symbol") do
70
+ settings[:entry_isolation] = true
71
+ mark_configuration_option(:entry_isolation)
72
+ end
73
+ options.on("--timeout=SECONDS", Integer, "whole-suite timeout (default: 10)") do |value|
74
+ raise OptionParser::InvalidArgument, "timeout must be positive" unless value.positive?
75
+
76
+ settings[:timeout] = value
77
+ end
78
+ options.on("--coverage=PERCENT", Integer, "require production coverage (1..100)") do |value|
79
+ settings[:coverage] = value
80
+ end
81
+ options.on("-h", "--help", "show this help") { settings[:help] = true }
82
+ end
83
+ end
84
+
85
+ # @rbs (Array[String] remaining, OptionParser options) -> String
86
+ def single_grammar_test_path(remaining, options)
87
+ return remaining.fetch(0) if remaining.one?
88
+
89
+ raise OptionParser::MissingArgument, options.banner if remaining.empty?
90
+
91
+ raise OptionParser::InvalidArgument, "test accepts exactly one grammarfile"
92
+ end
93
+
94
+ # @rbs (Frontend::Resolution resolution) -> IR::Grammar
95
+ def normalize_analysis_grammar(resolution)
96
+ grammar = Normalizer.new(resolution, mode: configuration_value("grammar.mode")).normalize
97
+ activate_analysis_grammar(grammar)
98
+ end
99
+
100
+ # @rbs!
101
+ # private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
102
+ # ?explicit_keys: Array[Symbol]) -> IR::Grammar
103
+
104
+ # @rbs (Array[GrammarTests::Result] results) -> Integer
105
+ def render_grammar_test_results(results)
106
+ failures = 0
107
+ results.each_with_index do |result, index|
108
+ label = "#{result.expectation} #{result.location[:file]}:#{result.location[:line]}"
109
+ if result.passed?
110
+ @stdout.puts("ok #{index + 1} - #{label}")
111
+ else
112
+ failures += 1
113
+ @stdout.puts("not ok #{index + 1} - #{label} (#{grammar_test_failure(result)})")
114
+ end
115
+ end
116
+ @stdout.puts("#{results.length} tests, #{failures} failures")
117
+ failures.zero? ? 0 : 1
118
+ end
119
+
120
+ # @rbs (GrammarTests::ProductionCoverage coverage, Integer minimum, Integer failures) -> Integer
121
+ def render_grammar_test_coverage(coverage, minimum, failures)
122
+ @stdout.puts(
123
+ format(
124
+ "production coverage: %<covered>d/%<total>d (%<percentage>.1f%%), required %<minimum>d%%",
125
+ covered: coverage.covered_ids.length, total: coverage.production_count,
126
+ percentage: coverage.percentage, minimum: minimum
127
+ )
128
+ )
129
+ @stdout.puts("missing production ids: #{coverage.missing_ids.join(', ')}") unless coverage.complete?
130
+ failures.zero? && coverage.meets?(minimum) ? 0 : 1
131
+ end
132
+
133
+ # @rbs (GrammarTests::Result result) -> String
134
+ def grammar_test_failure(result)
135
+ return "parser #{result.actual}ed the input" unless result.actual == :error
136
+
137
+ "#{result.error_class}: #{result.error_message}"
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ir"
4
+
5
+ module Ibex
6
+ # CLI validation and structural comparison for current IR documents.
7
+ module CLIIRTools
8
+ private
9
+
10
+ # @rbs (Array[String] arguments) -> Integer
11
+ def run_validate_ir_command(arguments)
12
+ path = single_ir_path(arguments, "validate-ir")
13
+ value = IR::Validator.validate(File.read(path))
14
+ kind = if value.is_a?(IR::Grammar)
15
+ "grammar"
16
+ elsif value.is_a?(IR::Lexer)
17
+ "lexer"
18
+ else
19
+ "automaton"
20
+ end
21
+ @stdout.puts("valid current #{kind} IR")
22
+ 0
23
+ end
24
+
25
+ # @rbs (Array[String] arguments) -> Integer
26
+ def run_compare_command(arguments)
27
+ raise Ibex::Error, "(cli):1:1: compare requires exactly two IR files" unless arguments.length == 2
28
+
29
+ before = IR::Validator.validate(File.read(arguments.fetch(0)))
30
+ after = IR::Validator.validate(File.read(arguments.fetch(1)))
31
+ compatible = (before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)) ||
32
+ (before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)) ||
33
+ (before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer))
34
+ raise Ibex::Error, "(cli):1:1: cannot compare different IR kinds" unless compatible
35
+
36
+ @stdout.puts(JSON.pretty_generate(compare_ir(before, after)))
37
+ 0
38
+ end
39
+
40
+ # @rbs (Array[String] arguments, String command) -> String
41
+ def single_ir_path(arguments, command)
42
+ return arguments.first if arguments.length == 1
43
+
44
+ raise Ibex::Error, "(cli):1:1: #{command} requires exactly one IR file"
45
+ end
46
+
47
+ # @rbs (IR::Grammar | IR::Automaton | IR::Lexer before,
48
+ # IR::Grammar | IR::Automaton | IR::Lexer after) -> Hash[Symbol, Object?]
49
+ def compare_ir(before, after)
50
+ return compare_grammars(before, after) if before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)
51
+ if before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)
52
+ return {
53
+ kind: "automaton",
54
+ algorithm: { before: before.algorithm, after: after.algorithm },
55
+ states: numeric_change(before.states.length, after.states.length),
56
+ transitions: numeric_change(transition_count(before), transition_count(after)),
57
+ conflicts: %i[sr resolved_sr rr].to_h do |kind|
58
+ [kind, numeric_change(summary_count(before, kind), summary_count(after, kind))]
59
+ end,
60
+ grammar: compare_grammars(before.grammar, after.grammar)
61
+ }
62
+ end
63
+ return compare_lexers(before, after) if before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer)
64
+
65
+ raise Ibex::Error, "(cli):1:1: unsupported IR comparison"
66
+ end
67
+
68
+ # @rbs (IR::Lexer before, IR::Lexer after) -> Hash[Symbol, Object?]
69
+ def compare_lexers(before, after)
70
+ before_rules = before.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
71
+ after_rules = after.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.options] }
72
+ {
73
+ kind: "lexer",
74
+ states: { added: after.states - before.states, removed: before.states - after.states },
75
+ rules: {
76
+ added: after_rules - before_rules, removed: before_rules - after_rules,
77
+ count: numeric_change(before.rules.length, after.rules.length)
78
+ },
79
+ warnings: numeric_change(before.warnings.length, after.warnings.length)
80
+ }
81
+ end
82
+
83
+ # @rbs (IR::Grammar before, IR::Grammar after) -> Hash[Symbol, Object?]
84
+ def compare_grammars(before, after)
85
+ before_symbols = before.symbols.map(&:name)
86
+ after_symbols = after.symbols.map(&:name)
87
+ before_productions = production_shapes(before)
88
+ after_productions = production_shapes(after)
89
+ {
90
+ kind: "grammar",
91
+ symbols: { added: (after_symbols - before_symbols).sort, removed: (before_symbols - after_symbols).sort },
92
+ productions: {
93
+ added: (after_productions - before_productions).sort,
94
+ removed: (before_productions - after_productions).sort,
95
+ count: numeric_change(before.productions.length, after.productions.length)
96
+ },
97
+ warnings: numeric_change(before.warnings.length, after.warnings.length)
98
+ }
99
+ end
100
+
101
+ # @rbs (IR::Grammar grammar) -> Array[String]
102
+ def production_shapes(grammar)
103
+ grammar.productions.map do |production|
104
+ lhs = grammar.symbol_by_id(production.lhs)&.name || production.lhs.to_s
105
+ rhs = production.rhs.map { |id| grammar.symbol_by_id(id)&.name || id.to_s }
106
+ "#{lhs} -> #{rhs.join(' ')}"
107
+ end
108
+ end
109
+
110
+ # @rbs (IR::Automaton automaton) -> Integer
111
+ def transition_count(automaton)
112
+ automaton.states.sum { |state| state.transitions.length }
113
+ end
114
+
115
+ # @rbs (IR::Automaton automaton, Symbol kind) -> Integer
116
+ def summary_count(automaton, kind)
117
+ value = automaton.conflict_summary.fetch(kind)
118
+ return value if value.is_a?(Integer)
119
+
120
+ raise Ibex::Error, "(ir):1:1: conflict count #{kind} must be an integer"
121
+ end
122
+
123
+ # @rbs (Integer before, Integer after) -> Hash[Symbol, Integer]
124
+ def numeric_change(before, after)
125
+ { before: before, after: after, delta: after - before }
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lsp"
4
+
5
+ module Ibex
6
+ # CLI boundary for the stdio-only language server.
7
+ module CLILSP
8
+ private
9
+
10
+ # @rbs (Array[String] arguments) -> Integer
11
+ def run_lsp_command(arguments)
12
+ settings = { help: false, stdio: false } #: Hash[Symbol, bool]
13
+ remaining = lsp_option_parser(settings).parse(arguments)
14
+ raise Ibex::Error, "(cli):1:1: ibex lsp does not accept file arguments" unless remaining.empty?
15
+
16
+ if settings.fetch(:help)
17
+ @stdout.puts(lsp_option_parser(settings))
18
+ return 0
19
+ end
20
+
21
+ LSP::Server.new(stdin: @stdin, stdout: @stdout, stderr: @stderr).run
22
+ end
23
+
24
+ # @rbs (Hash[Symbol, bool] settings) -> OptionParser
25
+ def lsp_option_parser(settings)
26
+ OptionParser.new do |options|
27
+ options.banner = "Usage: ibex lsp [--stdio]"
28
+ options.on("--stdio", "use Content-Length framed JSON-RPC on standard IO") { settings[:stdio] = true }
29
+ options.on("--help", "show help") { settings[:help] = true }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -3,15 +3,21 @@
3
3
  module Ibex
4
4
  # Files, warnings, and progress emitted by CLI pipeline stages.
5
5
  module CLIOutputs
6
- WARNING_MESSAGES = {
7
- undeclared_terminal: ->(warning) { "undeclared terminal #{warning[:symbol]}" },
8
- unused_terminal: ->(warning) { "unused terminal #{warning[:symbol]}" },
9
- unreachable_nonterminal: ->(warning) { "unreachable nonterminal #{warning[:symbol]}" },
10
- duplicate_production: lambda do |warning|
11
- "duplicate production #{warning[:production]} (first defined as #{warning[:original]})"
12
- end,
13
- empty_language: ->(warning) { "start symbol #{warning[:symbol]} derives no terminal sentence" }
14
- }.freeze #: Hash[Symbol, ^(IR::grammar_warning) -> String]
6
+ # @rbs!
7
+ # private def register_artifact: (Symbol, String, String, ?mode: Integer?, ?status: bool) -> Artifact
8
+ # private def configuration_value: (String) -> Object?
9
+
10
+ WARNING_MESSAGE_IDS = {
11
+ undeclared_terminal: "warning.undeclared_terminal",
12
+ unused_terminal: "warning.unused_terminal",
13
+ unused_precedence: "warning.unused_precedence",
14
+ unreachable_terminal: "warning.unreachable_terminal",
15
+ unreachable_nonterminal: "warning.unreachable_nonterminal",
16
+ duplicate_production: "warning.duplicate_production",
17
+ implicit_empty: "warning.implicit_empty",
18
+ empty_language: "warning.empty_language",
19
+ lexer_redos: "warning.lexer_redos"
20
+ }.freeze #: Hash[Symbol, String]
15
21
 
16
22
  private
17
23
 
@@ -32,57 +38,161 @@ module Ibex
32
38
  categories = @options[:warnings]
33
39
  return if categories.nil? || categories.include?(:none) || grammar.warnings.empty?
34
40
 
35
- messages = grammar.warnings.map { |warning| format_grammar_warning(warning, input_path) }
36
41
  if categories.include?(:error)
37
- promoted = messages.map { |message| message.sub(": warning:", ": warning treated as error:") }
42
+ promoted = grammar.warnings.map do |warning|
43
+ format_grammar_warning(warning, input_path, promoted: true)
44
+ end
38
45
  raise Ibex::Error, promoted.join("\n")
39
46
  end
40
47
 
41
- messages.each { |message| @stderr.puts(message) }
48
+ grammar.warnings.each { |warning| @stderr.puts(format_grammar_warning(warning, input_path)) }
42
49
  end
43
50
 
44
- # @rbs (IR::grammar_warning warning, String input_path) -> String
45
- def format_grammar_warning(warning, input_path)
51
+ # @rbs (IR::grammar_warning warning, String input_path, ?promoted: bool) -> String
52
+ def format_grammar_warning(warning, input_path, promoted: false)
46
53
  location = warning[:loc]
47
54
  rendered = if location
48
55
  "#{location[:file] || input_path}:#{location[:line] || 1}:#{location[:column] || 1}"
49
56
  else
50
57
  "#{input_path}:1:1"
51
58
  end
52
- formatter = WARNING_MESSAGES.fetch(warning[:type]) { ->(item) { item[:type].to_s.tr("_", " ") } }
53
- "#{rendered}: warning: #{formatter.call(warning)}"
59
+ label_id = promoted ? "label.warning_as_error" : "label.warning"
60
+ label = Messages.translate(label_id, language: @language)
61
+ "#{rendered}: #{label}: #{grammar_warning_message(warning)}"
62
+ end
63
+
64
+ # @rbs (IR::grammar_warning warning) -> String
65
+ def grammar_warning_message(warning)
66
+ id = WARNING_MESSAGE_IDS.fetch(warning[:type])
67
+ id = "warning.lexer_redos_symbol" if warning[:type] == :lexer_redos && warning[:symbol]
68
+ values = warning.to_h.transform_values(&:to_s) #: Hash[Symbol, String]
69
+ Messages.translate(id, language: @language, **values)
54
70
  end
55
71
 
56
72
  # @rbs (IR::Automaton automaton, String input_path) -> void
57
73
  def report_conflicts(automaton, input_path)
58
- summary = automaton.conflict_summary
74
+ messages = conflict_messages(automaton.conflict_summary, input_path)
75
+ if @options[:warnings]&.include?(:error) && messages.any?
76
+ promoted = messages.map do |message|
77
+ Messages.translate("conflict.treated_as_error", language: @language, detail: message)
78
+ end
79
+ raise Ibex::Error, promoted.join("\n")
80
+ end
81
+
82
+ messages.each { |message| @stderr.puts(message) }
83
+ end
84
+
85
+ # @rbs (IR::conflict_summary summary, String input_path) -> Array[String]
86
+ def conflict_messages(summary, input_path)
87
+ messages = [] #: Array[String]
59
88
  unless summary[:expectation_met]
60
- @stderr.puts("#{input_path}:1:1: #{summary[:sr]} shift/reduce conflicts; expected #{summary[:expected_sr]}")
89
+ detail = Messages.translate(
90
+ "conflict.shift_reduce_expected",
91
+ language: @language,
92
+ actual: summary[:sr],
93
+ expected: summary[:expected_sr]
94
+ )
95
+ messages << "#{input_path}:1:1: #{detail}"
61
96
  end
62
- @stderr.puts("#{input_path}:1:1: #{summary[:rr]} reduce/reduce conflicts") if summary[:rr].positive?
97
+ if summary.key?(:rr_expectation_met)
98
+ unless summary[:rr_expectation_met]
99
+ detail = Messages.translate(
100
+ "conflict.reduce_reduce_expected",
101
+ language: @language,
102
+ actual: summary[:rr],
103
+ expected: summary[:expected_rr]
104
+ )
105
+ messages << "#{input_path}:1:1: #{detail}"
106
+ end
107
+ elsif summary[:rr].positive?
108
+ detail = Messages.translate("conflict.reduce_reduce", language: @language, actual: summary[:rr])
109
+ messages << "#{input_path}:1:1: #{detail}"
110
+ end
111
+ messages
112
+ end
113
+
114
+ # @rbs (IR::Automaton automaton, String input_path) -> void
115
+ # rubocop:disable Metrics/AbcSize -- the bounded comparison reports each conflict class independently.
116
+ def suggest_ielr(automaton, input_path)
117
+ return unless @options[:suggest_ielr]
118
+ return unless automaton.algorithm == "lalr1"
119
+ return unless configuration_value("parser.algorithm") == :lalr
120
+
121
+ summary = automaton.conflict_summary
122
+ return if summary[:expectation_met] && summary.fetch(:rr_expectation_met, summary[:rr].zero?)
123
+
124
+ ielr = LALR::Builder.new(
125
+ Configuration::AnalysisGrammar.for_algorithm(automaton.grammar, :ielr),
126
+ algorithm: :ielr, entry_isolation: configuration_value("parser.entries") == :isolated
127
+ ).build
128
+ removed_sr = [summary[:sr] - ielr.conflict_summary[:sr], 0].max
129
+ removed_rr = [summary[:rr] - ielr.conflict_summary[:rr], 0].max
130
+ avoided = [] #: Array[String]
131
+ avoided << conflict_count(removed_sr, "shift/reduce") if removed_sr.positive?
132
+ avoided << conflict_count(removed_rr, "reduce/reduce") if removed_rr.positive?
133
+ return if avoided.empty?
134
+
135
+ conjunction = @language == "ja" ? "、" : " and "
136
+ detail = Messages.translate("note.ielr", language: @language, avoided: avoided.join(conjunction))
137
+ @stderr.puts("#{input_path}:1:1: #{detail}")
138
+ end
139
+ # rubocop:enable Metrics/AbcSize
140
+
141
+ # @rbs (Integer count, String kind) -> String
142
+ def conflict_count(count, kind)
143
+ plural = @language == "en" && count != 1 ? "s" : ""
144
+ Messages.translate("conflict.count", language: @language, count: count, kind: kind, plural: plural)
63
145
  end
64
146
 
65
147
  # @rbs (IR::Automaton automaton, String input_path) -> void
66
148
  def write_report(automaton, input_path)
149
+ require_relative "../codegen/report"
67
150
  path = @options[:log_file] || default_output_path(input_path, ".output")
68
151
  report = Codegen::Report.render(
69
152
  automaton,
70
153
  max_tokens: @options[:counterexample_max_tokens],
71
154
  max_configurations: @options[:counterexample_max_configurations]
72
155
  )
73
- File.write(path, report)
74
- report_status("wrote #{path}")
156
+ register_artifact(:report, path, report, status: true)
75
157
  end
76
158
 
77
159
  # @rbs (String input_path, String extension) -> String
78
160
  def default_output_path(input_path, extension)
79
- replaced = input_path.sub(/\.[^.]+\z/, extension)
80
- replaced == input_path ? "#{input_path}#{extension}" : replaced
161
+ directory = File.dirname(input_path)
162
+ basename = File.basename(input_path)
163
+ replaced = basename.sub(/\.[^.]+\z/, extension)
164
+ replaced = "#{basename}#{extension}" if replaced == basename
165
+ directory == "." ? replaced : File.join(directory, replaced)
81
166
  end
82
167
 
83
168
  # @rbs (String message) -> void
84
169
  def report_status(message)
85
170
  @stderr.puts("ibex: #{message}") if @options[:status]
86
171
  end
172
+
173
+ # @rbs (String path, String source) -> void
174
+ def atomic_write_ir(path, source)
175
+ require "tempfile"
176
+
177
+ target_path = File.symlink?(path) ? File.realpath(path) : path
178
+ directory = File.dirname(File.expand_path(target_path))
179
+ basename = File.basename(target_path)
180
+ Tempfile.create([".#{basename}.", ".tmp"], directory) do |temporary|
181
+ temporary.binmode
182
+ temporary.write(source)
183
+ temporary.flush
184
+ temporary.fsync
185
+ temporary.chmod(ir_file_mode(target_path))
186
+ temporary.close
187
+ File.rename(temporary.path, target_path)
188
+ end
189
+ end
190
+
191
+ # @rbs (String path) -> Integer
192
+ def ir_file_mode(path)
193
+ return File.stat(path).mode & 0o777 if File.exist?(path)
194
+
195
+ 0o666 & ~File.umask
196
+ end
87
197
  end
88
198
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require "optparse"
6
+ require_relative "../racc_migration"
7
+
8
+ module Ibex
9
+ # Static racc migration checks and differential harness generation.
10
+ module CLIRaccMigration
11
+ # @rbs!
12
+ # type migration_check_settings = {
13
+ # format: String,
14
+ # ?help: bool
15
+ # }
16
+ # type migration_harness_settings = {
17
+ # ?output: String,
18
+ # ?help: bool
19
+ # }
20
+ #
21
+ # private def input_path: (Array[String]) -> String
22
+ # private def atomic_write_ir: (String path, String source) -> void
23
+ # private def same_file_target?: (String left, String right) -> bool
24
+
25
+ private
26
+
27
+ # @rbs (Array[String] arguments) -> Integer
28
+ def run_migrate_check_command(arguments)
29
+ settings = { format: "text" } #: migration_check_settings
30
+ parser = migrate_check_options(settings)
31
+ paths = parser.parse(arguments)
32
+ if settings[:help]
33
+ @stdout.puts(parser)
34
+ return 0
35
+ end
36
+
37
+ path = input_path(paths)
38
+ report = RaccMigration::Checker.new.check(File.binread(path), file: path)
39
+ @stdout.write(settings.fetch(:format) == "json" ? "#{report.to_json}\n" : report.to_text)
40
+ report.compatible? ? 0 : 1
41
+ end
42
+
43
+ # @rbs (Array[String] arguments) -> Integer
44
+ def run_migrate_harness_command(arguments)
45
+ settings = {} #: migration_harness_settings
46
+ parser = migrate_harness_options(settings)
47
+ paths = parser.parse(arguments)
48
+ if settings[:help]
49
+ @stdout.puts(parser)
50
+ return 0
51
+ end
52
+
53
+ path = input_path(paths)
54
+ report = RaccMigration::Checker.new.check(File.binread(path), file: path)
55
+ unless report.compatible? && report.class_name
56
+ raise Ibex::Error, "(migration):1:1: grammar must pass migrate-check before harness generation"
57
+ end
58
+
59
+ source = RaccMigration::Harness.generate(report.class_name)
60
+ output = settings[:output]
61
+ if output && same_file_target?(path, output)
62
+ raise Ibex::Error, "(migration):1:1: grammar and harness output paths must be distinct"
63
+ end
64
+
65
+ output ? atomic_write_ir(output, source) : @stdout.write(source)
66
+ 0
67
+ end
68
+
69
+ # @rbs (migration_check_settings settings) -> OptionParser
70
+ def migrate_check_options(settings)
71
+ OptionParser.new do |options|
72
+ options.banner = "Usage: ibex migrate-check [--format=text|json] grammarfile"
73
+ options.on("--format=FORMAT", %w[text json], "text or json") { |value| settings[:format] = value }
74
+ options.on("--help", "show help") { settings[:help] = true }
75
+ end
76
+ end
77
+
78
+ # @rbs (migration_harness_settings settings) -> OptionParser
79
+ def migrate_harness_options(settings)
80
+ OptionParser.new do |options|
81
+ options.banner = "Usage: ibex migrate-harness [-o FILE] grammarfile"
82
+ options.on("-o FILE", "--output=FILE", "write the harness atomically") { |value| settings[:output] = value }
83
+ options.on("--help", "show help") { settings[:help] = true }
84
+ end
85
+ end
86
+ end
87
+ end