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,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module ErrorMessages
5
+ # Sentence-keyed ibex-messages v2 parsing, mixed into Parser so both
6
+ # versions share UTF-8, escape, and positioned-diagnostic helpers.
7
+ module ParserV2
8
+ # @rbs!
9
+ # private def ignorable?: (String) -> bool
10
+ # private def column: (String) -> Integer
11
+ # private def fail_at: (Integer, Integer, String) -> bot
12
+ # private def decode_line: (String, Integer, Integer) -> String
13
+
14
+ private
15
+
16
+ # @rbs () -> Document
17
+ def parse_v2
18
+ entries = [] #: Array[Entry]
19
+ identifiers = {} #: Hash[String, Integer]
20
+ keys = {} #: Hash[String, Integer]
21
+ index = 1
22
+ while index < @lines.length
23
+ line = @lines.fetch(index)
24
+ if ignorable?(line) && !line.lstrip.start_with?("##")
25
+ index += 1
26
+ next
27
+ end
28
+
29
+ entry, index = parse_v2_entry(index)
30
+ record_v2_entry(entry, entries, identifiers, keys)
31
+ end
32
+ Document.new(version: 2, entries: entries)
33
+ end
34
+
35
+ # @rbs (Entry entry, Array[Entry] entries, Hash[String, Integer] identifiers,
36
+ # Hash[String, Integer] keys) -> void
37
+ def record_v2_entry(entry, entries, identifiers, keys)
38
+ error_id = entry.error_id || raise(Ibex::Error, "missing error id")
39
+ reject_v2_duplicate(identifiers, error_id, entry.line, "error id")
40
+ key = entry.sentence ? "#{entry.entry}\0#{entry.sentence.join("\0")}" : "legacy\0#{entry.state}"
41
+ reject_v2_duplicate(keys, key, entry.line, "sentence")
42
+ identifiers[error_id] = entry.line
43
+ keys[key] = entry.line
44
+ entries << entry
45
+ end
46
+
47
+ # @rbs (Integer opening_index) -> [Entry, Integer]
48
+ def parse_v2_entry(opening_index)
49
+ status, sentence, state = parse_v2_opening(opening_index)
50
+ read_v2_entry_body(opening_index, status, sentence, state)
51
+ end
52
+
53
+ # @rbs (Integer opening_index) ->
54
+ # [:active | :unreachable | :removed, Array[String]?, Integer?]
55
+ def parse_v2_opening(opening_index)
56
+ opening = @lines.fetch(opening_index)
57
+ if (legacy = opening.match(/\Alegacy-state:[ \t]*([0-9]+)\z/))
58
+ return [:removed, nil, Integer(legacy[1] || "0", 10)]
59
+ end
60
+
61
+ if (sentence = opening.match(/\A(sentence|unreachable):[ \t]*(.*)\z/))
62
+ status = sentence[1] == "unreachable" ? :unreachable : :active #: :unreachable | :active
63
+ return [status, parse_sentence(sentence[2] || "", opening_index + 1), nil]
64
+ end
65
+
66
+ fail_at(opening_index + 1, column(opening),
67
+ "expected `sentence: TOKENS`, `unreachable: TOKENS`, or `legacy-state: N`")
68
+ end
69
+
70
+ # @rbs (Integer opening_index, :active | :unreachable | :removed status, Array[String]? sentence,
71
+ # Integer? state) -> [Entry, Integer]
72
+ def read_v2_entry_body(opening_index, status, sentence, state)
73
+ error_id = nil #: String?
74
+ entry_name = nil #: String?
75
+ message_lines = [] #: Array[String]
76
+ index = opening_index + 1
77
+ while index < @lines.length
78
+ line = @lines.fetch(index)
79
+ if line.strip == "end"
80
+ return close_v2_entry(
81
+ opening_index, index, status, sentence, state, error_id, entry_name, message_lines
82
+ )
83
+ end
84
+
85
+ error_id, state, entry_name = read_v2_metadata(
86
+ line, index, error_id, state, entry_name, message_lines
87
+ )
88
+ index += 1
89
+ end
90
+ fail_at(opening_index + 1, 1, "unterminated error-sentence entry")
91
+ end
92
+
93
+ # @rbs (Integer opening_index, Integer index, :active | :unreachable | :removed status,
94
+ # Array[String]? sentence, Integer? state, String? error_id, String? entry_name,
95
+ # Array[String] message_lines) -> [Entry, Integer]
96
+ def close_v2_entry(opening_index, index, status, sentence, state, error_id, entry_name, message_lines)
97
+ fail_at(opening_index + 1, 1, "missing `## E0001` error id") unless error_id
98
+ message = message_lines.empty? ? nil : message_lines.join("\n")
99
+ fail_at(opening_index + 1, 1, "message must not be empty") if message&.strip&.empty?
100
+ entry = Entry.new(
101
+ state: state, status: status, message: message, line: opening_index + 1,
102
+ sentence: sentence, error_id: error_id, entry: entry_name
103
+ )
104
+ [entry, index + 1]
105
+ end
106
+
107
+ # @rbs (String line, Integer index, String? error_id, Integer? state, String? entry_name,
108
+ # Array[String] message_lines) -> [String?, Integer?, String?]
109
+ def read_v2_metadata(line, index, error_id, state, entry_name, message_lines)
110
+ stripped = line.strip
111
+ if (match = stripped.match(/\A##[ \t]+(E[0-9]{4,})\z/))
112
+ fail_at(index + 1, column(line), "duplicate error id") if error_id
113
+ error_id = match[1]
114
+ elsif (match = stripped.match(/\A# state:[ \t]*([0-9]+)\z/))
115
+ state = Integer(match[1] || "0", 10)
116
+ elsif (match = stripped.match(/\A# entry:[ \t]*(\S+)\z/))
117
+ entry_name = match[1]
118
+ elsif line.start_with?("|")
119
+ append_v2_message_line(message_lines, line, index)
120
+ elsif !ignorable?(line)
121
+ fail_at(index + 1, column(line), "expected metadata, a `| ` message line, a comment, or `end`")
122
+ end
123
+ [error_id, state, entry_name]
124
+ end
125
+
126
+ # @rbs (Array[String] message_lines, String line, Integer index) -> void
127
+ def append_v2_message_line(message_lines, line, index)
128
+ content = line.delete_prefix("|").delete_prefix(" ")
129
+ content_column = line.start_with?("| ") ? 3 : 2
130
+ message_lines << decode_line(content, index + 1, content_column)
131
+ end
132
+
133
+ # @rbs (String source, Integer line) -> Array[String]
134
+ def parse_sentence(source, line)
135
+ tokens = [] #: Array[String]
136
+ index = 0
137
+ loop do
138
+ index = skip_sentence_space(source, index)
139
+ break if index == source.length
140
+
141
+ token, index = read_sentence_token(source, index, line)
142
+ tokens << token
143
+ end
144
+ fail_at(line, 1, "error sentence must contain at least one token") if tokens.empty?
145
+ tokens
146
+ end
147
+
148
+ # @rbs (String source, Integer index) -> Integer
149
+ def skip_sentence_space(source, index)
150
+ index += 1 while index < source.length && source[index]&.match?(/\s/)
151
+ index
152
+ end
153
+
154
+ # @rbs (String source, Integer index, Integer line) -> [String, Integer]
155
+ def read_sentence_token(source, index, line)
156
+ start = index
157
+ quote = source[index]
158
+ if ["'", '"'].include?(quote)
159
+ index = quoted_token_end(source, index, quote || "", line)
160
+ else
161
+ index += 1 while index < source.length && !source[index]&.match?(/\s/)
162
+ end
163
+ token = source[start...index] || raise(Ibex::Error, "missing sentence token")
164
+ [token, index]
165
+ end
166
+
167
+ # @rbs (String source, Integer index, String quote, Integer line) -> Integer
168
+ def quoted_token_end(source, index, quote, line)
169
+ ending = scan_quoted_token(source, index, quote, line)
170
+ return ending if ending == source.length || source[ending]&.match?(/\s/)
171
+
172
+ fail_at(line, ending + 1, "quoted token must be followed by whitespace")
173
+ end
174
+
175
+ # @rbs (String source, Integer index, String quote, Integer line) -> Integer
176
+ def scan_quoted_token(source, index, quote, line)
177
+ index += 1
178
+ while index < source.length
179
+ character = source[index]
180
+ if character == "\\"
181
+ index += 2
182
+ elsif character == quote
183
+ return index + 1
184
+ else
185
+ index += 1
186
+ end
187
+ end
188
+ fail_at(line, source.length + 1, "unterminated quoted token")
189
+ end
190
+
191
+ # @rbs (Hash[String, Integer] declarations, String key, Integer line, String label) -> void
192
+ def reject_v2_duplicate(declarations, key, line, label)
193
+ previous = declarations[key]
194
+ fail_at(line, 1, "duplicate #{label}; first declared at line #{previous}") if previous
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module ErrorMessages
5
+ # @rbs (IR::Automaton automaton, Array[Entry] entries) -> String
6
+ def render_entries(automaton, entries)
7
+ lines = [
8
+ HEADER,
9
+ "# Generated by `ibex errors --update`; sentences and IDs are stable review keys.",
10
+ "# Edit only `| ` message lines.",
11
+ ""
12
+ ]
13
+ entries.each { |entry| append_entry(lines, automaton, entry) }
14
+ "#{lines.join("\n").rstrip}\n"
15
+ end
16
+
17
+ # @rbs (Array[String] lines, IR::Automaton automaton, Entry entry) -> void
18
+ def append_entry(lines, automaton, entry)
19
+ lines << entry_heading(entry)
20
+ lines << "## #{entry.error_id}"
21
+ lines << "# entry: #{entry.entry}" if entry.entry
22
+ lines << "# state: #{entry.state}" if entry.state
23
+ append_expected_tokens(lines, automaton, entry)
24
+ lines << "# Add one or more `| ` lines to customize this error." unless entry.message
25
+ entry.message&.split("\n", -1)&.each { |line| lines << "| #{encode_line(line)}" }
26
+ lines.push("end", "")
27
+ end
28
+
29
+ # @rbs (Entry entry) -> String
30
+ def entry_heading(entry)
31
+ return "legacy-state: #{entry.state}" unless entry.sentence
32
+
33
+ prefix = entry.status == :active ? "sentence" : "unreachable"
34
+ "#{prefix}: #{entry.sentence.join(' ')}"
35
+ end
36
+
37
+ # @rbs (Array[String] lines, IR::Automaton automaton, Entry entry) -> void
38
+ def append_expected_tokens(lines, automaton, entry)
39
+ return unless entry.status == :active && entry.state
40
+
41
+ expected = expected_tokens(automaton, automaton.states.fetch(entry.state))
42
+ lines << "# expected: #{expected.empty? ? '(none)' : expected.join(', ')}"
43
+ end
44
+
45
+ # @rbs (String line) -> String
46
+ def encode_line(line)
47
+ line.gsub("\\", "\\\\").gsub("\t", "\\t").gsub("\r", "\\r")
48
+ end
49
+
50
+ # @rbs (IR::Automaton automaton, IR::AutomatonState state) -> Array[String]
51
+ def expected_tokens(automaton, state)
52
+ ordinary_terminals(automaton).filter_map do |terminal|
53
+ action = state.actions[terminal.id] || state.default_action
54
+ terminal.display_name || terminal.name unless error_action?(action)
55
+ end
56
+ end
57
+
58
+ module_function :render_entries, :append_entry, :entry_heading, :append_expected_tokens, :encode_line,
59
+ :expected_tokens
60
+
61
+ class << self
62
+ private :render_entries, :append_entry, :entry_heading, :append_expected_tokens, :encode_line, :expected_tokens
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lalr/conflict_search_limits"
4
+
5
+ module Ibex
6
+ module ErrorMessages
7
+ # Finds deterministic shortest token sentences that reach each syntax
8
+ # error state without running semantic actions.
9
+ class SentenceSearch
10
+ DEFAULT_MAX_TOKENS = LALR::ConflictSearchLimits::DEFAULT_MAX_TOKENS #: Integer
11
+ DEFAULT_MAX_CONFIGURATIONS = LALR::ConflictSearchLimits::DEFAULT_MAX_CONFIGURATIONS #: Integer
12
+
13
+ Witness = Struct.new(
14
+ :entry, #: String
15
+ :tokens, #: Array[String]
16
+ :state, #: Integer
17
+ keyword_init: true
18
+ )
19
+
20
+ # @rbs @automaton: IR::Automaton
21
+ # @rbs @grammar: IR::Grammar
22
+ # @rbs @max_tokens: Integer
23
+ # @rbs @max_configurations: Integer
24
+ # @rbs @candidates: Array[IR::GrammarSymbol]
25
+ # @rbs @input_candidates: Array[IR::GrammarSymbol]
26
+ # @rbs @explored: Integer
27
+
28
+ # @rbs (IR::Automaton automaton, ?max_tokens: Integer, ?max_configurations: Integer) -> void
29
+ def initialize(automaton, max_tokens: DEFAULT_MAX_TOKENS,
30
+ max_configurations: DEFAULT_MAX_CONFIGURATIONS)
31
+ LALR::ConflictSearchLimits.validate!(
32
+ max_tokens: max_tokens, max_configurations: max_configurations
33
+ )
34
+ @automaton = automaton
35
+ @grammar = automaton.grammar
36
+ @max_tokens = max_tokens
37
+ @max_configurations = max_configurations
38
+ @candidates = grammar_terminals
39
+ @input_candidates = @candidates.reject { |symbol| symbol.id.zero? }
40
+ @explored = 0
41
+ end
42
+
43
+ # @rbs () -> Hash[Integer, Witness]
44
+ def all
45
+ targets = ErrorMessages.error_states(@automaton).to_h { |state| [state.id, true] }
46
+ witnesses = {} #: Hash[Integer, Witness]
47
+ @automaton.entry_states.each do |entry, initial_state|
48
+ search_entry(entry, initial_state, targets, witnesses)
49
+ break if witnesses.length == targets.length
50
+ end
51
+ witnesses.sort.to_h.freeze
52
+ end
53
+
54
+ # @rbs (Array[String] tokens, ?entry: String?) -> Integer?
55
+ def state_for(tokens, entry: nil)
56
+ return nil if tokens.empty?
57
+
58
+ start = entry || @grammar.start
59
+ initial = @automaton.entry_states[start]
60
+ return nil unless initial
61
+
62
+ states = [initial]
63
+ tokens.each_with_index do |name, index|
64
+ status, value = consume_sentence_token(states, name, index == tokens.length - 1)
65
+ return value if status == :error && value.is_a?(Integer)
66
+ return nil unless status == :continue && value.is_a?(Array)
67
+
68
+ states = value
69
+ end
70
+ nil
71
+ end
72
+
73
+ private
74
+
75
+ # @rbs (Array[Integer] states, String name, bool final) ->
76
+ # [Symbol, Array[Integer] | Integer | nil]
77
+ def consume_sentence_token(states, name, final)
78
+ symbol = sentence_symbol(name, final)
79
+ return [:invalid, nil] unless symbol
80
+
81
+ status, value = advance(states, symbol.id)
82
+ return [:error, value] if status == :error && final && value.is_a?(Integer)
83
+ return [:continue, value] if status == :shift && value.is_a?(Array)
84
+
85
+ [:invalid, nil]
86
+ end
87
+
88
+ # @rbs (String name, bool final) -> IR::GrammarSymbol?
89
+ def sentence_symbol(name, final)
90
+ symbol = @grammar.symbol(name)
91
+ return nil unless symbol&.terminal?
92
+ return nil if symbol.name == "error" || (symbol.id.zero? && !final)
93
+
94
+ symbol
95
+ end
96
+
97
+ # @rbs () -> Array[IR::GrammarSymbol]
98
+ def grammar_terminals
99
+ @grammar.terminals
100
+ .reject { |symbol| symbol.name == "error" }
101
+ .sort_by { |symbol| [symbol.id.zero? ? 1 : 0, symbol.name] }
102
+ end
103
+
104
+ # @rbs (String entry, Integer initial_state, Hash[Integer, bool] targets,
105
+ # Hash[Integer, Witness] witnesses) -> void
106
+ def search_entry(entry, initial_state, targets, witnesses)
107
+ queue = [[[initial_state], Array.new(0)]] #: Array[[Array[Integer], Array[String]]]
108
+ visited = { [initial_state] => true }
109
+ until queue.empty? || witnesses.length == targets.length
110
+ states, prefix = queue.shift
111
+ count_configuration!
112
+ collect_errors(entry, states, prefix, targets, witnesses)
113
+ enqueue_shifts(queue, visited, states, prefix) if prefix.length < @max_tokens - 1
114
+ end
115
+ end
116
+
117
+ # @rbs (String entry, Array[Integer] states, Array[String] prefix, Hash[Integer, bool] targets,
118
+ # Hash[Integer, Witness] witnesses) -> void
119
+ def collect_errors(entry, states, prefix, targets, witnesses)
120
+ @candidates.each do |symbol|
121
+ status, state = advance(states, symbol.id)
122
+ next unless status == :error && state.is_a?(Integer)
123
+
124
+ error_state = state #: Integer
125
+ next unless targets[error_state] && !witnesses[error_state]
126
+
127
+ witnesses[error_state] = Witness.new(
128
+ entry: entry, tokens: (prefix + [symbol.name]).freeze, state: error_state
129
+ ).freeze
130
+ end
131
+ end
132
+
133
+ # @rbs (Array[[Array[Integer], Array[String]]] queue, Hash[Array[Integer], bool] visited,
134
+ # Array[Integer] states, Array[String] prefix) -> void
135
+ def enqueue_shifts(queue, visited, states, prefix)
136
+ @input_candidates.each do |symbol|
137
+ status, shifted = advance(states, symbol.id)
138
+ next unless status == :shift && shifted.is_a?(Array)
139
+
140
+ shifted_states = shifted #: Array[Integer]
141
+ next if visited[shifted_states]
142
+
143
+ visited[shifted_states] = true
144
+ queue << [shifted_states, prefix + [symbol.name]]
145
+ end
146
+ end
147
+
148
+ # @rbs (Array[Integer] initial, Integer token_id) -> [Symbol, Array[Integer] | Integer | nil]
149
+ def advance(initial, token_id)
150
+ states = initial
151
+ visited = {} #: Hash[Array[Integer], bool]
152
+ loop do
153
+ return [:invalid, nil] if visited[states]
154
+
155
+ visited[states] = true
156
+ state = @automaton.states.fetch(states.last)
157
+ action = state.actions[token_id] || state.default_action
158
+ return [:error, state.id] if action.nil? || action[:type] == :error
159
+
160
+ case action[:type]
161
+ when :shift
162
+ shift = action #: IR::shift_action
163
+ return [:shift, (states + [shift[:state]]).freeze]
164
+ when :reduce
165
+ reduce = action #: IR::reduce_action
166
+ states = reduce_stack(states, reduce[:production])
167
+ return [:invalid, nil] unless states
168
+ when :accept
169
+ return [:accept, nil]
170
+ else
171
+ return [:invalid, nil]
172
+ end
173
+ end
174
+ end
175
+
176
+ # @rbs (Array[Integer] states, Integer production_id) -> Array[Integer]?
177
+ def reduce_stack(states, production_id)
178
+ production = @grammar.productions.fetch(production_id)
179
+ return nil if production.rhs.length >= states.length
180
+
181
+ remaining = states.take(states.length - production.rhs.length)
182
+ target = @automaton.states.fetch(remaining.last).gotos[production.lhs]
183
+ target && (remaining + [target]).freeze
184
+ end
185
+
186
+ # @rbs () -> void
187
+ def count_configuration!
188
+ @explored += 1
189
+ return if @explored <= @max_configurations
190
+
191
+ raise Ibex::Error,
192
+ "(messages):1:1: error-sentence search exceeded #{@max_configurations} configurations"
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "renderer"
4
+
5
+ module Ibex
6
+ module ErrorMessages
7
+ # @rbs (IR::Automaton automaton, ?existing: Document?, ?max_tokens: Integer,
8
+ # ?max_configurations: Integer) -> String
9
+ def render(automaton, existing: nil, max_tokens: SentenceSearch::DEFAULT_MAX_TOKENS,
10
+ max_configurations: SentenceSearch::DEFAULT_MAX_CONFIGURATIONS)
11
+ update(
12
+ automaton, existing: existing, max_tokens: max_tokens, max_configurations: max_configurations
13
+ ).source
14
+ end
15
+ module_function :render
16
+
17
+ # @rbs (IR::Automaton automaton, ?existing: Document?, ?max_tokens: Integer,
18
+ # ?max_configurations: Integer) -> Update
19
+ def update(automaton, existing: nil, max_tokens: SentenceSearch::DEFAULT_MAX_TOKENS,
20
+ max_configurations: SentenceSearch::DEFAULT_MAX_CONFIGURATIONS)
21
+ document = existing || Document.new(entries: [])
22
+ search = SentenceSearch.new(
23
+ automaton, max_tokens: max_tokens, max_configurations: max_configurations
24
+ )
25
+ witnesses = search.all
26
+ classifications = { uncovered: [], unreachable: [], moved: [] } #: Hash[Symbol, Array[String]]
27
+ next_id = next_error_id(document)
28
+ entries, covered, next_id = update_existing_entries(
29
+ document, search, witnesses, classifications, next_id
30
+ )
31
+ add_uncovered_entries(entries, covered, witnesses, classifications[:uncovered], next_id)
32
+ entries.sort_by! { |entry| entry_sort_key(entry) }
33
+ Update.new(
34
+ source: render_entries(automaton, entries),
35
+ uncovered: classifications[:uncovered],
36
+ unreachable: classifications[:unreachable],
37
+ moved: classifications[:moved]
38
+ )
39
+ end
40
+ module_function :update
41
+
42
+ # @rbs (Array[Entry] entries, Hash[Integer, bool] covered,
43
+ # Hash[Integer, SentenceSearch::Witness] witnesses, Array[String] uncovered, Integer next_id) -> void
44
+ def add_uncovered_entries(entries, covered, witnesses, uncovered, next_id)
45
+ witnesses.each do |state, witness|
46
+ next if covered[state]
47
+
48
+ error_id = format_error_id(next_id)
49
+ next_id += 1
50
+ entries << Entry.new(
51
+ state: state, status: :active, message: nil, line: 1,
52
+ sentence: witness.tokens, error_id: error_id, entry: witness.entry
53
+ )
54
+ uncovered << "#{error_id} state #{state}: #{witness.tokens.join(' ')}"
55
+ end
56
+ end
57
+
58
+ # @rbs (Document document, SentenceSearch search, Hash[Integer, SentenceSearch::Witness] witnesses,
59
+ # Hash[Symbol, Array[String]] classifications, Integer next_id) ->
60
+ # [Array[Entry], Hash[Integer, bool], Integer]
61
+ def update_existing_entries(document, search, witnesses, classifications, next_id)
62
+ return migrate_legacy_entries(document, witnesses, classifications, next_id) if document.version == 1
63
+
64
+ entries = [] #: Array[Entry]
65
+ covered = {} #: Hash[Integer, bool]
66
+ document.entries.each do |entry|
67
+ if entry.sentence
68
+ next_id = update_sentence_entry(
69
+ entry, search, entries, covered, classifications, next_id
70
+ )
71
+ else
72
+ entries << with_error_id(entry, next_id)
73
+ next_id += 1 unless entry.error_id
74
+ end
75
+ end
76
+ [entries, covered, next_id]
77
+ end
78
+
79
+ # @rbs (Entry entry, SentenceSearch search, Array[Entry] entries, Hash[Integer, bool] covered,
80
+ # Hash[Symbol, Array[String]] classifications, Integer next_id) -> Integer
81
+ def update_sentence_entry(entry, search, entries, covered, classifications, next_id)
82
+ error_id = entry.error_id || format_error_id(next_id)
83
+ next_id += 1 unless entry.error_id
84
+ sentence = entry.sentence || raise(Ibex::Error, "missing error sentence")
85
+ state = search.state_for(sentence, entry: entry.entry)
86
+ unless state
87
+ entries << Entry.new(
88
+ state: entry.state, status: :unreachable, message: entry.message, line: entry.line,
89
+ sentence: sentence, error_id: error_id, entry: entry.entry
90
+ )
91
+ classifications[:unreachable] << "#{error_id}: #{sentence.join(' ')}"
92
+ return next_id
93
+ end
94
+
95
+ classifications[:moved] << "#{error_id}: state #{entry.state} -> #{state}" if entry.state && entry.state != state
96
+ covered[state] = true
97
+ entries << Entry.new(
98
+ state: state, status: :active, message: entry.message, line: entry.line,
99
+ sentence: sentence, error_id: error_id, entry: entry.entry
100
+ )
101
+ next_id
102
+ end
103
+
104
+ # @rbs (Document document, Hash[Integer, SentenceSearch::Witness] witnesses,
105
+ # Hash[Symbol, Array[String]] classifications, Integer next_id) ->
106
+ # [Array[Entry], Hash[Integer, bool], Integer]
107
+ def migrate_legacy_entries(document, witnesses, classifications, next_id)
108
+ by_state = document.entries.to_h { |entry| [entry.state, entry] }
109
+ entries = [] #: Array[Entry]
110
+ covered = {} #: Hash[Integer, bool]
111
+ witnesses.each do |state, witness|
112
+ previous = by_state.delete(state)
113
+ error_id = format_error_id(next_id)
114
+ next_id += 1
115
+ entries << Entry.new(
116
+ state: state, status: :active, message: previous&.message, line: previous&.line || 1,
117
+ sentence: witness.tokens, error_id: error_id, entry: witness.entry
118
+ )
119
+ covered[state] = true
120
+ classifications[:uncovered] << "#{error_id} state #{state}: #{witness.tokens.join(' ')}" unless previous
121
+ end
122
+ by_state.values.sort_by { |entry| entry.state || -1 }.each do |entry|
123
+ error_id = format_error_id(next_id)
124
+ next_id += 1
125
+ entries << Entry.new(
126
+ state: entry.state, status: :removed, message: entry.message, line: entry.line, error_id: error_id
127
+ )
128
+ classifications[:unreachable] << "#{error_id}: legacy state #{entry.state}"
129
+ end
130
+ [entries, covered, next_id]
131
+ end
132
+
133
+ # @rbs (Entry entry, Integer next_id) -> Entry
134
+ def with_error_id(entry, next_id)
135
+ Entry.new(
136
+ state: entry.state, status: entry.status, message: entry.message, line: entry.line,
137
+ sentence: entry.sentence, error_id: entry.error_id || format_error_id(next_id), entry: entry.entry
138
+ )
139
+ end
140
+
141
+ # @rbs (Document document) -> Integer
142
+ def next_error_id(document)
143
+ maximum = document.entries.filter_map do |entry|
144
+ match = entry.error_id&.match(/\AE([0-9]{4,})\z/)
145
+ match && Integer(match[1] || "0", 10)
146
+ end.max
147
+ (maximum || 0) + 1
148
+ end
149
+
150
+ # @rbs (Integer number) -> String
151
+ def format_error_id(number)
152
+ format("E%04d", number)
153
+ end
154
+
155
+ # @rbs (Entry entry) -> [Integer, Integer, String]
156
+ def entry_sort_key(entry)
157
+ rank = { active: 0, unreachable: 1, removed: 2 }.fetch(entry.status)
158
+ [rank, entry.state || (1 << 62), entry.error_id || ""]
159
+ end
160
+
161
+ module_function :add_uncovered_entries, :update_existing_entries, :update_sentence_entry, :migrate_legacy_entries,
162
+ :with_error_id, :next_error_id, :format_error_id, :entry_sort_key
163
+
164
+ class << self
165
+ private :add_uncovered_entries, :update_existing_entries, :update_sentence_entry, :migrate_legacy_entries,
166
+ :with_error_id, :next_error_id, :format_error_id, :entry_sort_key
167
+ end
168
+ end
169
+ end