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,380 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lalr"
4
+ require_relative "symbol_labels"
5
+
6
+ module Ibex
7
+ module Codegen
8
+ # @rbs!
9
+ # type explain_selection = {
10
+ # state: IR::AutomatonState,
11
+ # conflict: IR::conflict,
12
+ # conflict_index: Integer
13
+ # }
14
+ #
15
+ # type explain_entry = {
16
+ # state: IR::AutomatonState,
17
+ # conflict: IR::conflict,
18
+ # example: LALR::search_counterexample
19
+ # }
20
+ #
21
+ # type explain_token = { name: String, display_name: String?, label: String, ?query: String }
22
+ # type explain_alternative = { kind: String, ?state: Integer, ?production: Integer }
23
+ # type explain_search = {
24
+ # status: String, explored: Integer, exhausted: bool, bounds: Hash[Symbol, Integer]
25
+ # }
26
+ # type explain_tree_node = {
27
+ # ?symbol: explain_token, ?token: explain_token, ?production: Integer, ?children: Array[explain_tree]
28
+ # }
29
+ # type explain_tree = untyped
30
+ # type explain_interpretation = Hash[Symbol, untyped]
31
+ # type string_value = String | Integer | Symbol
32
+
33
+ # Renders a selected conflict explanation from Automaton IR and counterexamples.
34
+ # rubocop:disable Metrics/ClassLength -- inline contracts and two stable output formats stay near selection policy.
35
+ class Explain
36
+ SCHEMA_VERSION = 1 #: Integer
37
+
38
+ # @rbs (IR::Automaton automaton, ?state: Integer?, ?token: String?, ?max_tokens: Integer,
39
+ # ?max_configurations: Integer) -> void
40
+ def initialize(automaton, state: nil, token: nil, max_tokens: LALR::Counterexample::DEFAULT_MAX_TOKENS,
41
+ max_configurations: LALR::Counterexample::DEFAULT_MAX_CONFIGURATIONS)
42
+ @automaton = automaton
43
+ @grammar = automaton.grammar
44
+ @state_selector = state
45
+ @token_query = token
46
+ @max_tokens = max_tokens
47
+ @max_configurations = max_configurations
48
+ @labels = SymbolLabels.build(@grammar)
49
+ @token_selector = resolve_token(token)
50
+ validate_state!
51
+ @entries = select_entries
52
+ end
53
+
54
+ # @rbs () -> Hash[Symbol, Object?]
55
+ def to_h
56
+ entries = @entries #: Array[explain_entry]
57
+ automaton = @automaton #: IR::Automaton
58
+ unifying = entries.count { |entry| entry.fetch(:example).fetch(:unifying) }
59
+ inconclusive = entries.count { |entry| entry.fetch(:example).fetch(:inconclusive) }
60
+ {
61
+ ibex_explain: "conflicts",
62
+ schema_version: SCHEMA_VERSION,
63
+ algorithm: automaton.algorithm,
64
+ selectors: {
65
+ state: @state_selector,
66
+ token: @token_selector && token_reference(@token_selector, query: @token_query)
67
+ },
68
+ search: { max_tokens: @max_tokens, max_configurations: @max_configurations },
69
+ summary: {
70
+ matched_conflicts: entries.length,
71
+ unifying_counterexamples: unifying,
72
+ nonunifying_witnesses: entries.length - unifying - inconclusive,
73
+ inconclusive_searches: inconclusive
74
+ },
75
+ conflicts: entries.map { |entry| conflict_document(entry) }
76
+ }
77
+ end
78
+
79
+ # @rbs () -> String
80
+ def render_text
81
+ automaton = @automaton #: IR::Automaton
82
+ entries = @entries #: Array[explain_entry]
83
+ state_selector = @state_selector #: Integer?
84
+ token_selector = @token_selector #: IR::GrammarSymbol?
85
+ lines = [
86
+ "Ibex conflict explanation v#{SCHEMA_VERSION}",
87
+ "Algorithm: #{automaton.algorithm}",
88
+ "State selector: #{state_selector || 'all'}",
89
+ "Token selector: #{token_selector ? token_label(token_selector, query: @token_query) : 'all'}",
90
+ "Search budget: #{@max_tokens} tokens, #{@max_configurations} configurations",
91
+ "Matched conflicts: #{entries.length}",
92
+ ""
93
+ ]
94
+ if entries.empty?
95
+ lines << "No conflicts matched the selectors."
96
+ else
97
+ entries.each_with_index { |entry, index| append_conflict(lines, entry, index + 1) }
98
+ end
99
+ "#{lines.join("\n")}\n"
100
+ end
101
+
102
+ private
103
+
104
+ # @rbs (String? query) -> IR::GrammarSymbol?
105
+ def resolve_token(query)
106
+ return unless query
107
+
108
+ grammar = @grammar #: IR::Grammar
109
+ canonical = grammar.terminals.find { |symbol| symbol.name == query }
110
+ return canonical if canonical
111
+
112
+ displayed = grammar.terminals.select { |symbol| symbol.display_name == query }
113
+ return displayed.first if displayed.one?
114
+
115
+ if displayed.length > 1
116
+ names = displayed.map(&:name).sort.join(", ")
117
+ raise Ibex::Error, "(cli):1:1: token display name #{query.inspect} is ambiguous; use one of: #{names}"
118
+ end
119
+
120
+ raise Ibex::Error,
121
+ "(cli):1:1: unknown token #{query.inspect}; use a grammar token name or an exact display name"
122
+ end
123
+
124
+ # @rbs () -> void
125
+ def validate_state!
126
+ state_selector = @state_selector #: Integer?
127
+ automaton = @automaton #: IR::Automaton
128
+ return unless state_selector
129
+ return if state_selector >= 0 && automaton.states.any? { |state| state.id == state_selector }
130
+
131
+ raise Ibex::Error, "(cli):1:1: unknown automaton state #{state_selector}"
132
+ end
133
+
134
+ # @rbs () -> Array[explain_entry]
135
+ def select_entries
136
+ selections = selected_conflicts
137
+ return [] if selections.empty?
138
+
139
+ automaton = @automaton #: IR::Automaton
140
+ counterexamples = LALR::Counterexample.new(
141
+ automaton, max_tokens: @max_tokens, max_configurations: @max_configurations
142
+ )
143
+ selections.map do |selection|
144
+ state = selection.fetch(:state)
145
+ {
146
+ state: state,
147
+ conflict: selection.fetch(:conflict),
148
+ example: counterexamples.for_conflict(state.id, selection.fetch(:conflict_index))
149
+ }
150
+ end
151
+ end
152
+
153
+ # @rbs () -> Array[explain_selection]
154
+ def selected_conflicts
155
+ automaton = @automaton #: IR::Automaton
156
+ state_selector = @state_selector #: Integer?
157
+ token_selector = @token_selector #: IR::GrammarSymbol?
158
+ automaton.states.flat_map do |state|
159
+ state.conflicts.each_with_index.filter_map do |conflict, conflict_index|
160
+ next if state_selector && state.id != state_selector
161
+ next if token_selector && conflict[:symbol] != token_selector.name
162
+
163
+ { state: state, conflict: conflict, conflict_index: conflict_index }
164
+ end
165
+ end
166
+ end
167
+
168
+ # @rbs (explain_entry entry) -> Hash[Symbol, Object?]
169
+ def conflict_document(entry)
170
+ state = entry.fetch(:state)
171
+ conflict = entry.fetch(:conflict)
172
+ example = entry.fetch(:example)
173
+ document = {
174
+ state: state.id,
175
+ type: conflict.fetch(:type).to_s,
176
+ token: symbol_reference(conflict.fetch(:symbol)),
177
+ alternatives: conflict_alternatives(conflict),
178
+ resolution: string_values(conflict.fetch(:resolution)),
179
+ witness: {
180
+ kind: witness_kind(example),
181
+ sentence: example.fetch(:sentence).map { |name| symbol_reference(name) },
182
+ lookahead_index: example.fetch(:lookahead_index),
183
+ symbol_path: example.fetch(:symbol_path).map { |name| symbol_reference(name) },
184
+ search: search_document(example.fetch(:search)),
185
+ interpretations: example.fetch(:interpretations).map { |item| interpretation_document(item) }
186
+ }
187
+ } #: Hash[Symbol, Object?]
188
+ document[:midrule_origins] = conflict[:midrule_origins] if conflict[:midrule_origins]
189
+ document
190
+ end
191
+
192
+ # @rbs (LALR::search_counterexample example) -> String
193
+ def witness_kind(example)
194
+ return "unifying_counterexample" if example.fetch(:unifying)
195
+ return "inconclusive" if example.fetch(:inconclusive)
196
+
197
+ "nonunifying_witness"
198
+ end
199
+
200
+ # @rbs (LALR::search_outcome outcome) -> explain_search
201
+ def search_document(outcome)
202
+ {
203
+ status: outcome.fetch(:status).to_s,
204
+ explored: outcome.fetch(:explored),
205
+ exhausted: outcome.fetch(:exhausted),
206
+ bounds: outcome.fetch(:bounds)
207
+ }
208
+ end
209
+
210
+ # @rbs (IR::conflict conflict) -> Array[explain_alternative]
211
+ def conflict_alternatives(conflict)
212
+ case conflict[:type]
213
+ when :shift_reduce
214
+ shift = { kind: "shift", state: conflict.fetch(:shift_to) } # @type var shift: explain_alternative
215
+ reduce = { kind: "reduce", production: conflict.fetch(:reduce) } # @type var reduce: explain_alternative
216
+ [shift, reduce]
217
+ when :reduce_reduce
218
+ reduce_reduce = conflict #: IR::reduce_reduce_conflict
219
+ reduce_reduce[:reductions].map do |production|
220
+ { kind: "reduce", production: production }
221
+ end #: Array[explain_alternative]
222
+ else
223
+ []
224
+ end
225
+ end
226
+
227
+ # @rbs (IR::interpretation interpretation) -> explain_interpretation
228
+ def interpretation_document(interpretation)
229
+ kind = interpretation.fetch(:kind) #: Symbol
230
+ tree = interpretation.fetch(:tree) #: explain_tree
231
+ value = { kind: kind.to_s,
232
+ tree: tree_document(tree) } # @type var value: explain_interpretation
233
+ value[:state] = interpretation[:state] if interpretation.key?(:state)
234
+ value[:production] = interpretation[:production] if interpretation.key?(:production)
235
+ value
236
+ end
237
+
238
+ # @rbs (explain_tree tree) -> explain_tree
239
+ def tree_document(tree)
240
+ return symbol_reference(tree.to_s) unless tree.is_a?(Hash)
241
+
242
+ node = tree #: explain_tree_node
243
+ value = {} # @type var value: explain_tree_node
244
+ value[:symbol] = symbol_reference(node[:symbol].to_s) if node[:symbol]
245
+ value[:token] = symbol_reference(node[:token].to_s) if node[:token]
246
+ value[:production] = node[:production] if node[:production]
247
+ value[:children] = node[:children].map { |child| tree_document(child) } if node[:children]
248
+ value
249
+ end
250
+
251
+ # @rbs (Hash[Symbol, string_value] value) -> Hash[Symbol, string_value]
252
+ def string_values(value)
253
+ values = value #: Hash[Symbol, string_value]
254
+ values.transform_values { |item| item.is_a?(Symbol) ? item.to_s : item }
255
+ end
256
+
257
+ # @rbs (String name) -> explain_token
258
+ def symbol_reference(name)
259
+ grammar = @grammar #: IR::Grammar
260
+ symbol = grammar.symbol(name)
261
+ return { name: name, display_name: nil, label: name } unless symbol
262
+
263
+ token_reference(symbol)
264
+ end
265
+
266
+ # @rbs (IR::GrammarSymbol symbol, ?query: String?) -> explain_token
267
+ def token_reference(symbol, query: nil)
268
+ labels = @labels #: Hash[Integer, String]
269
+ value = { name: symbol.name, display_name: symbol.display_name,
270
+ label: labels.fetch(symbol.id, symbol.name) } # @type var value: explain_token
271
+ value[:query] = query if query
272
+ value
273
+ end
274
+
275
+ # @rbs (Array[String] lines, explain_entry entry, Integer number) -> void
276
+ def append_conflict(lines, entry, number)
277
+ state = entry.fetch(:state)
278
+ conflict = entry.fetch(:conflict)
279
+ example = entry.fetch(:example)
280
+ grammar = @grammar #: IR::Grammar
281
+ token = grammar.symbol(conflict.fetch(:symbol))
282
+ token_text = token ? token_label(token) : conflict.fetch(:symbol)
283
+ type = conflict.fetch(:type).to_s.tr("_", "/")
284
+ lines << "Conflict #{number}: state #{state.id}, #{type}, token #{token_text}"
285
+ append_midrule_origins(lines, conflict)
286
+ append_witness_steps(lines, state, conflict, example)
287
+ append_interpretations(lines, example)
288
+ if example.fetch(:inconclusive)
289
+ search = example.fetch(:search)
290
+ lines << " Search exhausted after #{search.fetch(:explored)} configurations; classification is inconclusive."
291
+ elsif !example.fetch(:unifying)
292
+ lines << " The search found no shared sentence within the configured budget; this witness is deterministic."
293
+ end
294
+ lines << ""
295
+ end
296
+
297
+ # @rbs (Array[String] lines, IR::conflict conflict) -> void
298
+ def append_midrule_origins(lines, conflict)
299
+ origins = conflict[:midrule_origins]
300
+ return unless origins
301
+
302
+ origins.each do |origin|
303
+ lines << " Midrule action origin: #{origin.fetch(:file)}:#{origin.fetch(:line)}:#{origin.fetch(:column)}"
304
+ end
305
+ end
306
+
307
+ # @rbs (Array[String] lines, IR::AutomatonState state, IR::conflict conflict,
308
+ # LALR::search_counterexample example) -> void
309
+ def append_witness_steps(lines, state, conflict, example)
310
+ lines << " 1. Reach state #{state.id} with the witness prefix."
311
+ sentence = example.fetch(:sentence).map { |name| display_name(name) }
312
+ sentence.insert(example.fetch(:lookahead_index), "•")
313
+ kind = case witness_kind(example)
314
+ when "unifying_counterexample" then "Unifying counterexample"
315
+ when "inconclusive" then "Inconclusive reachability witness"
316
+ else "Nonunifying reachability witness"
317
+ end
318
+ lines << " #{kind}: #{sentence.join(' ')}"
319
+ lines << " 2. The lookahead permits these competing actions:"
320
+ conflict_alternatives(conflict).each { |alternative| lines << " - #{alternative_text(alternative)}" }
321
+ resolution = conflict.fetch(:resolution)
322
+ lines << " 3. Resolution: #{resolution.fetch(:by)} chose #{resolution.fetch(:chose)}."
323
+ end
324
+
325
+ # @rbs (Array[String] lines, LALR::search_counterexample example) -> void
326
+ def append_interpretations(lines, example)
327
+ lines << " Competing derivations:"
328
+ example.fetch(:interpretations).each_with_index do |interpretation, index|
329
+ lines << " #{index + 1}. #{interpretation.fetch(:kind)}"
330
+ append_tree(lines, interpretation.fetch(:tree), " ", "")
331
+ end
332
+ end
333
+
334
+ # @rbs (explain_alternative alternative) -> String
335
+ def alternative_text(alternative)
336
+ return "shift to state #{alternative.fetch(:state)}" if alternative.fetch(:kind) == "shift"
337
+
338
+ "reduce by production #{alternative.fetch(:production)}"
339
+ end
340
+
341
+ # @rbs (Array[String] lines, explain_tree tree, String prefix, String connector) -> void
342
+ def append_tree(lines, tree, prefix, connector)
343
+ unless tree.is_a?(Hash)
344
+ lines << "#{prefix}#{connector}#{display_name(tree.to_s)}"
345
+ return
346
+ end
347
+
348
+ node = tree #: explain_tree_node
349
+ name = node[:symbol] || node[:token]
350
+ production = node[:production] ? " (production #{node[:production]})" : ""
351
+ lines << "#{prefix}#{connector}#{display_name(name.to_s)}#{production}"
352
+ children = node.fetch(:children, [])
353
+ continuation = { "" => "", "|- " => "| " }.fetch(connector, " ")
354
+ child_prefix = "#{prefix}#{continuation}"
355
+ children.each_with_index do |child, index|
356
+ branch = index == children.length - 1 ? "`- " : "|- "
357
+ append_tree(lines, child, child_prefix, branch)
358
+ end
359
+ end
360
+
361
+ # @rbs (IR::GrammarSymbol symbol, ?query: String?) -> String
362
+ def token_label(symbol, query: nil)
363
+ label = if symbol.display_name && symbol.display_name != symbol.name
364
+ "#{symbol.name} (display #{symbol.display_name.inspect})"
365
+ else
366
+ symbol.name
367
+ end
368
+ query && query != symbol.name ? "#{label}, selected by #{query.inspect}" : label
369
+ end
370
+
371
+ # @rbs (String name) -> String
372
+ def display_name(name)
373
+ grammar = @grammar #: IR::Grammar
374
+ labels = @labels #: Hash[Integer, String]
375
+ grammar.symbol(name)&.then { |symbol| labels.fetch(symbol.id, symbol.name) } || name
376
+ end
377
+ end
378
+ # rubocop:enable Metrics/ClassLength
379
+ end
380
+ end
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "action_locations"
5
+
6
+ module Ibex
7
+ module Codegen
8
+ # Selects the smallest generated semantic-action ABI that preserves the
9
+ # action's declared inputs.
10
+ # rubocop:disable Metrics/ModuleLength -- ABI proof and source rewriting must evolve together.
11
+ module GeneratedActionABI
12
+ # @rbs!
13
+ # type ripper_token = [[Integer, Integer], Symbol, String, Integer]
14
+ # type sexp_value = Symbol | String | Integer | nil | Array[sexp_value]
15
+
16
+ LEGACY_PARAMETERS = %w[
17
+ _values
18
+ _ibex_locations
19
+ _ibex_location_stack
20
+ _ibex_location
21
+ _ibex_lookahead_location
22
+ ].freeze #: Array[String]
23
+ SIMPLE_POSITIONAL_READ = /(?<![.\w@$])val\[\s*(\d+)\s*\]/ #: Regexp
24
+ SIMPLE_POSITIONAL_UNSAFE = %r{['"`#%/\\]|<<} #: Regexp
25
+ private_constant :LEGACY_PARAMETERS
26
+ private_constant :SIMPLE_POSITIONAL_READ, :SIMPLE_POSITIONAL_UNSAFE
27
+
28
+ # Keeps semantic-action analysis local to one code-generation run.
29
+ class Cache
30
+ # @rbs @values_only: Hash[Integer, bool]
31
+ # @rbs @borrowed_values: Hash[Integer, bool]
32
+ # @rbs @positional_actions: Hash[Integer, String?]
33
+
34
+ # @rbs () -> void
35
+ def initialize
36
+ @values_only = {}
37
+ @borrowed_values = {}
38
+ @positional_actions = {}
39
+ end
40
+
41
+ # @rbs (IR::Production production) -> bool
42
+ def values_only?(production)
43
+ @values_only.fetch(production.id) do
44
+ @values_only[production.id] = GeneratedActionABI.values_only?(production)
45
+ end
46
+ end
47
+
48
+ # @rbs (IR::Production production) -> bool
49
+ def borrowed_values?(production)
50
+ @borrowed_values.fetch(production.id) do
51
+ @borrowed_values[production.id] =
52
+ GeneratedActionABI.borrowed_values?(production, analysis: self)
53
+ end
54
+ end
55
+
56
+ # @rbs (IR::Production production) -> String?
57
+ def positional_action_source(production)
58
+ @positional_actions.fetch(production.id) do
59
+ @positional_actions[production.id] =
60
+ GeneratedActionABI.positional_action_source(production, analysis: self)
61
+ end
62
+ end
63
+
64
+ # @rbs (IR::Production production) -> bool
65
+ def positional_values?(production)
66
+ !positional_action_source(production).nil?
67
+ end
68
+ end
69
+
70
+ module_function
71
+
72
+ # @rbs (IR::Production production) -> bool
73
+ def values_only?(production)
74
+ return false if production.action&.composition
75
+
76
+ action = production.action
77
+ return true unless action
78
+ return false if action.context_length.positive?
79
+
80
+ maximum = production.rhs.length
81
+ locations = ActionLocations.new(action.code, maximum: maximum, location: action.location)
82
+ !locations.references? && !references_legacy_parameter?(action.code)
83
+ end
84
+
85
+ # The direct runtime can share one reduction-values array with an action
86
+ # and a hook installed by that action only when the action cannot mutate
87
+ # or retain the array itself. Element reads and parallel assignment copy
88
+ # values out without exposing the container.
89
+ # @rbs (IR::Production production, ?analysis: singleton(GeneratedActionABI) | Cache) -> bool
90
+ def borrowed_values?(production, analysis: self)
91
+ return false unless analysis.values_only?(production)
92
+
93
+ action = production.action
94
+ return false unless action
95
+
96
+ maximum = production.rhs.length
97
+ source = ActionLocations.new(action.code, maximum: maximum, location: action.location).rewrite
98
+ return true if simple_indexed_values_action?(source)
99
+
100
+ require "ripper"
101
+ syntax = Object.const_get(:Ripper).__send__(:sexp, source)
102
+ !syntax.nil? && read_only_value_references?(syntax)
103
+ end
104
+
105
+ # Return semantic source rewritten for zero-to-four positional RHS
106
+ # arguments, or nil when the values container remains observable.
107
+ # @rbs (IR::Production production, ?analysis: singleton(GeneratedActionABI) | Cache) -> String?
108
+ def positional_action_source(production, analysis: self)
109
+ return nil unless analysis.values_only?(production)
110
+ return nil unless production.rhs.length <= 4
111
+
112
+ action = production.action
113
+ return "" unless action
114
+ return nil unless analysis.borrowed_values?(production)
115
+
116
+ parameters = Array.new(production.rhs.length) { |index| "v#{index}" }
117
+ return nil if action.named_refs.any? { |reference| parameters.include?(reference[:name]) }
118
+
119
+ maximum = production.rhs.length
120
+ source = ActionLocations.new(action.code, maximum: maximum, location: action.location).rewrite
121
+ simple = fast_positional_action_source(source, parameters)
122
+ return simple unless simple.nil?
123
+
124
+ require "ripper"
125
+ tokens = Object.const_get(:Ripper).__send__(:lex, source)
126
+ return nil unless tokens.map { |_position, _event, token, _state| token }.join == source
127
+
128
+ rewrite_positional_values(tokens, parameters)
129
+ end
130
+
131
+ # @rbs (String source, Array[String] parameters) -> String?
132
+ def fast_positional_action_source(source, parameters)
133
+ simple = simple_positional_action_source(source, parameters)
134
+ return simple unless simple.nil?
135
+
136
+ simple_indexed_positional_action_source(source, parameters)
137
+ end
138
+ private_class_method :fast_positional_action_source
139
+
140
+ # @rbs (IR::Production production) -> bool
141
+ def positional_values?(production)
142
+ !positional_action_source(production).nil?
143
+ end
144
+
145
+ # Avoid loading a Ruby parser for the overwhelmingly common generated
146
+ # action shape. This accepts only a single result assignment (or a bare
147
+ # expression), simple numeric val reads, and no further assignment.
148
+ # Anything less obvious falls back to Ripper below.
149
+ # @rbs (String source) -> bool
150
+ def simple_indexed_values_action?(source)
151
+ body = source.strip
152
+ assignment = /\Aresult\s*=\s*(.*)\z/m.match(body)
153
+ body = (assignment[1] || "").strip if assignment
154
+ return false if body.empty? || body.match?(/(?<![=!<>])=(?!=|>)/)
155
+ return true unless body.match?(/\bval\b/)
156
+
157
+ without_reads = body.gsub(/\bval\[\s*\d+\s*\]/, "")
158
+ without_reads != body && !without_reads.match?(/\bval\b/)
159
+ end
160
+ private_class_method :simple_indexed_values_action?
161
+
162
+ # Keep the most common identity action off the Ripper load path. The
163
+ # whole-source match excludes strings, comments, receivers, and compound
164
+ # expressions before replacing the one direct read.
165
+ # @rbs (String source, Array[String] parameters) -> String?
166
+ def simple_positional_action_source(source, parameters)
167
+ match = /\A\s*(?:result\s*=\s*)?val\[\s*(\d+)\s*\]\s*\z/.match(source)
168
+ return nil unless match
169
+
170
+ parameter = parameters[Integer(match[1] || "", 10)]
171
+ source.sub(/\bval\[\s*\d+\s*\]/, parameter) if parameter
172
+ end
173
+ private_class_method :simple_positional_action_source
174
+
175
+ # Rewrite the common string/comment/regexp-free action shape without
176
+ # loading Ripper. The borrowed-values proof has already rejected writes
177
+ # and escaping uses of the values container.
178
+ # @rbs (String source, Array[String] parameters) -> String?
179
+ def simple_indexed_positional_action_source(source, parameters)
180
+ return nil if source.match?(SIMPLE_POSITIONAL_UNSAFE)
181
+ return nil if parameters.any? { |parameter| source.match?(/\b#{Regexp.escape(parameter)}\b/) }
182
+
183
+ valid = true
184
+ rewritten = source.gsub(SIMPLE_POSITIONAL_READ) do
185
+ parameter = parameters.fetch(Integer(Regexp.last_match(1) || "", 10), nil)
186
+ valid = false unless parameter
187
+ parameter || Regexp.last_match(0)
188
+ end
189
+ return nil unless valid
190
+ return nil if rewritten.match?(/\bval\b/)
191
+
192
+ rewritten
193
+ end
194
+ private_class_method :simple_indexed_positional_action_source
195
+
196
+ # @rbs (Array[ripper_token] tokens, Array[String] parameters) -> String?
197
+ def rewrite_positional_values(tokens, parameters)
198
+ result = [] #: Array[String]
199
+ index = 0
200
+ while index < tokens.length
201
+ _position, event, token, _state = tokens[index]
202
+ return nil if event == :on_ident && parameters.include?(token)
203
+
204
+ unless event == :on_ident && token == "val"
205
+ result << token
206
+ index += 1
207
+ next
208
+ end
209
+
210
+ reference = positional_value_reference(tokens, index, parameters)
211
+ if reference
212
+ result << reference.fetch(0)
213
+ index = reference.fetch(1)
214
+ elsif receiver_before_value?(tokens, index) || parameters.empty?
215
+ return nil
216
+ else
217
+ # borrowed_values? has already proven that a bare `val` occurs
218
+ # only as the right-hand side of parallel assignment.
219
+ result << parameters.join(", ")
220
+ index += 1
221
+ end
222
+ end
223
+ result.join
224
+ end
225
+ private_class_method :rewrite_positional_values
226
+
227
+ # @rbs (Array[ripper_token] tokens, Integer index, Array[String] parameters) -> [String, Integer]?
228
+ def positional_value_reference(tokens, index, parameters)
229
+ return nil if receiver_before_value?(tokens, index)
230
+
231
+ reference = tokens.slice(index + 1, 3)
232
+ return nil unless reference
233
+ return nil unless reference.map { |entry| entry[1] } == %i[on_lbracket on_int on_rbracket]
234
+
235
+ parameter = parameters[Integer(reference.fetch(1).fetch(2), 10)]
236
+ [parameter, index + 4] if parameter
237
+ rescue ArgumentError
238
+ nil
239
+ end
240
+ private_class_method :positional_value_reference
241
+
242
+ # @rbs (Array[ripper_token] tokens, Integer index) -> bool
243
+ def receiver_before_value?(tokens, index)
244
+ previous = tokens.first(index).reverse.find do |entry|
245
+ !%i[on_sp on_nl on_ignored_nl on_comment].include?(entry[1])
246
+ end
247
+ return false unless previous
248
+
249
+ previous[1] == :on_period || (previous[1] == :on_op && %w[&. ::].include?(previous[2]))
250
+ end
251
+ private_class_method :receiver_before_value?
252
+
253
+ # @rbs (String source) -> bool
254
+ def references_legacy_parameter?(source)
255
+ return false unless LEGACY_PARAMETERS.any? { |parameter| source.include?(parameter) }
256
+
257
+ require "ripper"
258
+ tokens = Object.const_get(:Ripper).__send__(:lex, source.gsub(/@(?:\$|\d+)/) { |match| "_" * match.bytesize })
259
+ tokens.any? do |_position, type, token, _state|
260
+ type == :on_ident && LEGACY_PARAMETERS.include?(token)
261
+ end
262
+ end
263
+ private_class_method :references_legacy_parameter?
264
+
265
+ # @rbs (sexp_value node, ?sexp_value parent, ?Integer? child_index) -> bool
266
+ def read_only_value_references?(node, parent = nil, child_index = nil)
267
+ return true unless node.is_a?(Array)
268
+ return safe_value_reference?(parent, child_index) if value_reference?(node)
269
+
270
+ node.each_with_index.all? do |child, index|
271
+ read_only_value_references?(child, node, index)
272
+ end
273
+ end
274
+ private_class_method :read_only_value_references?
275
+
276
+ # @rbs (untyped node) -> bool
277
+ def value_reference?(node)
278
+ node[0] == :vcall && node.dig(1, 0) == :@ident && node.dig(1, 1) == "val"
279
+ end
280
+ private_class_method :value_reference?
281
+
282
+ # @rbs (sexp_value? parent, Integer? child_index) -> bool
283
+ def safe_value_reference?(parent, child_index)
284
+ return false unless parent.is_a?(Array)
285
+
286
+ (parent[0] == :aref && child_index == 1) ||
287
+ (parent[0] == :massign && child_index == 2)
288
+ end
289
+ private_class_method :safe_value_reference?
290
+ end
291
+ # rubocop:enable Metrics/ModuleLength
292
+ end
293
+ end