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
@@ -4,19 +4,197 @@ module Ibex
4
4
  module Frontend
5
5
  # Public grammar parser backed by Ibex's generated LR frontend.
6
6
  class Parser
7
+ DEFAULT_MAX_DIAGNOSTICS = 20 #: Integer
8
+ STRICT_LEXICAL_DIAGNOSTIC_LIMIT = 1 #: Integer
9
+ private_constant :STRICT_LEXICAL_DIAGNOSTIC_LIMIT
10
+
7
11
  attr_reader :implementation #: GeneratedParser
8
12
 
13
+ # @rbs @source_document: SourceDocument?
14
+ # @rbs @parsed_document: SourceDocument?
15
+ # @rbs @parsed_node: AST::Root | AST::Fragment | nil
16
+ # @rbs @parse_error_message: String?
17
+ # @rbs @source_document_error_message: String?
18
+ # @rbs @tokens: Array[Token]
19
+ # @rbs @mode: Symbol
20
+ # @rbs @lexical_diagnostics: Array[Diagnostic]
21
+
9
22
  # @rbs (String | Array[Token] source, ?file: String, ?mode: Symbol) -> void
10
- def initialize(source, file: "(grammar)", mode: :racc)
11
- raise ArgumentError, "mode must be :racc or :extended" unless %i[racc extended].include?(mode)
23
+ def initialize(source, file: "(grammar)", mode: :default)
24
+ raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
12
25
 
13
- tokens = source.is_a?(Array) ? source : Lexer.new(source, file: file).tokenize
26
+ @mode = mode
27
+ @lexical_diagnostics = [] #: Array[Diagnostic]
28
+ @parse_error_message = nil
29
+ @source_document_error_message = nil
30
+ tokens = source.is_a?(Array) ? source : tokenize_source(source, file)
31
+ @tokens = tokens
14
32
  @implementation = GeneratedParser.new(tokens, mode: mode)
15
33
  end
16
34
 
17
35
  # @rbs () -> AST::Root
18
36
  def parse
19
- @implementation.parse
37
+ node = parse_node
38
+ return node if node.is_a?(AST::Root)
39
+
40
+ raise Ibex::Error, "#{node.loc}: fragment input requires Parser#parse_fragment"
41
+ end
42
+
43
+ # Parse an explicit fragment without resolving its includes.
44
+ # @rbs () -> AST::Fragment
45
+ def parse_fragment
46
+ node = parse_node
47
+ return node if node.is_a?(AST::Fragment)
48
+
49
+ raise Ibex::Error, "#{node.loc}: root grammar input cannot be parsed as a fragment"
50
+ end
51
+
52
+ # Parse the grammar and return its lossless source model.
53
+ # @rbs () -> SourceDocument
54
+ def parse_document
55
+ parsed_document = @parsed_document
56
+ return parsed_document if parsed_document && parsed_document.ast.is_a?(AST::Root)
57
+
58
+ @parsed_document = source_document!.with_ast(parse)
59
+ end
60
+
61
+ # Parse either a root grammar or an explicit fragment and return its lossless source model.
62
+ # @rbs () -> SourceDocument
63
+ def parse_source_document
64
+ parsed_document = @parsed_document
65
+ return parsed_document if parsed_document
66
+
67
+ @parsed_document = source_document!.with_ast(parse_node)
68
+ end
69
+
70
+ # Parse with conservative boundary recovery and collect multiple errors.
71
+ # @rbs (?max_diagnostics: Integer) -> ParseResult
72
+ def parse_with_diagnostics(max_diagnostics: DEFAULT_MAX_DIAGNOSTICS)
73
+ unless max_diagnostics.is_a?(Integer) && max_diagnostics.positive?
74
+ raise ArgumentError, "max_diagnostics must be a positive integer"
75
+ end
76
+
77
+ lexical_diagnostics = lexical_diagnostics_for(max_diagnostics)
78
+ if @source_document.nil? && @lexical_diagnostics.any?
79
+ return ParseResult.new(diagnostics: lexical_diagnostics, ast: nil, document: nil)
80
+ end
81
+
82
+ recovery = DiagnosticRecovery.new(
83
+ @tokens, mode: @mode, max_diagnostics: max_diagnostics
84
+ )
85
+ node, syntax_diagnostics = recovery.parse
86
+ node = enrich_rule_documentation(node) if node
87
+ ast, syntax_diagnostics = root_diagnostic_result(node, syntax_diagnostics)
88
+ diagnostics = merge_diagnostics(lexical_diagnostics, syntax_diagnostics, max_diagnostics)
89
+ document = if diagnostics.empty? && ast
90
+ @source_document&.with_ast(ast)
91
+ else
92
+ @source_document
93
+ end
94
+ ParseResult.new(diagnostics: diagnostics, ast: ast, document: document)
95
+ end
96
+
97
+ private
98
+
99
+ # @rbs () -> SourceDocument
100
+ def source_document!
101
+ source_document_error_message = @source_document_error_message
102
+ raise Ibex::Error, source_document_error_message if source_document_error_message
103
+
104
+ source_document = @source_document
105
+ raise ArgumentError, "source document parsing requires String source" unless source_document
106
+
107
+ source_document
108
+ end
109
+
110
+ # @rbs () -> (AST::Root | AST::Fragment)
111
+ def parse_node
112
+ lexical = @lexical_diagnostics.first
113
+ raise Ibex::Error, lexical.to_s if lexical
114
+
115
+ parse_error_message = @parse_error_message
116
+ raise Ibex::Error, parse_error_message if parse_error_message
117
+
118
+ node = @parsed_node
119
+ return node if node
120
+
121
+ begin
122
+ @parsed_node = enrich_rule_documentation(@implementation.parse)
123
+ rescue Ibex::Error => e
124
+ @parse_error_message = e.message.dup.freeze
125
+ raise
126
+ end
127
+ end
128
+
129
+ # @rbs (AST::Root | AST::Fragment node) -> (AST::Root | AST::Fragment)
130
+ def enrich_rule_documentation(node)
131
+ source_document = @source_document
132
+ return node unless source_document
133
+
134
+ RuleDocumentation.enrich(node, source_document)
135
+ end
136
+
137
+ # @rbs (AST::Root | AST::Fragment | nil node, Array[Diagnostic] diagnostics) ->
138
+ # [AST::Root?, Array[Diagnostic]]
139
+ def root_diagnostic_result(node, diagnostics)
140
+ return [node, diagnostics] unless node.is_a?(AST::Fragment)
141
+
142
+ diagnostic = Diagnostic.new(
143
+ code: "frontend.syntax_error", phase: :syntax,
144
+ message: "fragment input requires Parser#parse_fragment", location: node.loc
145
+ )
146
+ [nil, diagnostics + [diagnostic]]
147
+ end
148
+
149
+ # @rbs (String source, String file) -> Array[Token]
150
+ def tokenize_source(source, file)
151
+ document, diagnostics = Lexer.new(source, file: file).tokenize_document_recovering(
152
+ max_diagnostics: STRICT_LEXICAL_DIAGNOSTIC_LIMIT
153
+ )
154
+ @source_document = document
155
+ @lexical_diagnostics = diagnostics
156
+ document.tokens
157
+ rescue Ibex::Error => e
158
+ location = Location.new(file: file, line: 1, column: 1)
159
+ @lexical_diagnostics = [
160
+ Diagnostic.new(code: "frontend.lexical_error", phase: :lexical, message: e.message,
161
+ location: location, rendered: e.message)
162
+ ]
163
+ @source_document_error_message = e.message.dup.freeze
164
+ [Token.new(type: :eof, value: nil, location: location, span: nil)]
165
+ end
166
+
167
+ # @rbs (Integer max_diagnostics) -> Array[Diagnostic]
168
+ def lexical_diagnostics_for(max_diagnostics)
169
+ return @lexical_diagnostics if @lexical_diagnostics.empty? || max_diagnostics == 1
170
+
171
+ source_document = @source_document
172
+ return @lexical_diagnostics.first(max_diagnostics) unless source_document
173
+
174
+ _, diagnostics = Lexer.new(source_document.source, file: source_document.file)
175
+ .tokenize_document_recovering(max_diagnostics: max_diagnostics)
176
+ diagnostics
177
+ end
178
+
179
+ # @rbs (Array[Diagnostic] lexical, Array[Diagnostic] syntax, Integer limit) -> Array[Diagnostic]
180
+ def merge_diagnostics(lexical, syntax, limit)
181
+ (lexical + syntax)
182
+ .uniq { |diagnostic| diagnostic_key(diagnostic) }
183
+ .sort_by { |diagnostic| diagnostic_sort_key(diagnostic) }
184
+ .first(limit)
185
+ .freeze
186
+ end
187
+
188
+ # @rbs (Diagnostic diagnostic) -> [String, String, Integer, Integer, String]
189
+ def diagnostic_key(diagnostic)
190
+ location = diagnostic.location
191
+ [diagnostic.phase.to_s, location.file, location.line, location.column, diagnostic.message]
192
+ end
193
+
194
+ # @rbs (Diagnostic diagnostic) -> [String, Integer, Integer, String, String]
195
+ def diagnostic_sort_key(diagnostic)
196
+ location = diagnostic.location
197
+ [location.file, location.line, location.column, diagnostic.phase.to_s, diagnostic.code]
20
198
  end
21
199
  end
22
200
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # Shares the closed parser-declaration semantics between bootstrap and generated frontends.
6
+ module ParserConfigurationSupport
7
+ # @rbs!
8
+ # private def token_string: (Token) -> String
9
+ # private def fail_at: (Location, String) -> bot
10
+ # private def extended_only!: (Location, String) -> void
11
+
12
+ PARSER_SETTING_VALUES = {
13
+ "algorithm" => %w[slr lalr ielr lr1].freeze,
14
+ "entries" => %w[shared isolated].freeze,
15
+ "cst_trivia" => %w[leading balanced drop].freeze
16
+ }.freeze #: Hash[String, Array[String]]
17
+
18
+ private
19
+
20
+ # @rbs (Token key, Token value) -> AST::ParserSetting
21
+ def build_parser_setting(key, value)
22
+ name = token_string(key)
23
+ admitted = PARSER_SETTING_VALUES[name]
24
+ fail_at(key.location, "unknown parser setting #{name}") unless admitted
25
+
26
+ selected = token_string(value)
27
+ unless admitted.include?(selected)
28
+ fail_at(value.location, "parser.#{name} must be one of #{admitted.join(', ')}; got #{selected}")
29
+ end
30
+
31
+ AST::ParserSetting.new(key: name.to_sym, value: selected.to_sym, loc: key.location)
32
+ end
33
+
34
+ # @rbs (Token keyword, Array[AST::ParserSetting] settings) -> AST::ParserConfiguration
35
+ def build_parser_configuration(keyword, settings)
36
+ extended_only!(keyword.location, "parser declarations")
37
+ seen = {} #: Hash[Symbol, bool]
38
+ settings.each do |setting|
39
+ fail_at(setting.loc, "duplicate parser setting #{setting.key}") if seen[setting.key]
40
+
41
+ seen[setting.key] = true
42
+ end
43
+ fail_at(keyword.location, "parser declaration requires at least one setting") if settings.empty?
44
+
45
+ AST::ParserConfiguration.new(settings: settings, loc: keyword.location)
46
+ end
47
+
48
+ # @rbs (Array[AST::declaration] declarations) -> void
49
+ def validate_root_parser_configuration(declarations)
50
+ configurations = declarations.grep(AST::ParserConfiguration)
51
+ duplicate = configurations.fetch(1, nil)
52
+ fail_at(duplicate.loc, "duplicate parser declaration") if duplicate
53
+ end
54
+ end
55
+ end
56
+ end
@@ -2,12 +2,16 @@
2
2
 
3
3
  require_relative "../error"
4
4
  require_relative "../tables"
5
+ require_relative "source_span"
5
6
  require_relative "source_cursor"
7
+ require_relative "source_document"
6
8
  require_relative "action_scanner"
7
9
  require_relative "lexer"
8
10
  require_relative "ast"
11
+ require_relative "parser_configuration_support"
9
12
  require_relative "parser/declarations"
10
13
  require_relative "parser/rules"
14
+ require_relative "parser/parameters"
11
15
  require_relative "bootstrap_parser"
12
16
  require_relative "../ir"
13
17
  require_relative "../normalize"
@@ -20,6 +24,7 @@ module Ibex
20
24
  # Builds the committed frontend parser from its canonical Ibex grammar.
21
25
  module Regenerator
22
26
  GRAMMAR_PATH = File.expand_path("grammar.y", File.dirname(__FILE__)) #: String
27
+ SHADOW_GRAMMAR_PATH = File.expand_path("shadow_grammar.y", File.dirname(__FILE__)) #: String
23
28
 
24
29
  module_function
25
30
 
@@ -29,13 +34,34 @@ module Ibex
29
34
  ast = BootstrapParser.new(source, file: relative_grammar_path).parse
30
35
  grammar = Normalizer.new(ast).normalize
31
36
  automaton = LALR::Builder.new(grammar).build
32
- Codegen::Ruby.new(automaton, table: :compact, line_convert: false).generate
37
+ Codegen::Ruby.new(
38
+ automaton, table: :compact, line_convert: false, runtime_require: nil
39
+ ).generate
40
+ end
41
+
42
+ # Build the non-published parameterized/inline grammar used to verify
43
+ # that preview composition features can describe the production
44
+ # frontend without changing its observable AST.
45
+ # @rbs () -> String
46
+ def generate_shadow
47
+ source = File.read(SHADOW_GRAMMAR_PATH)
48
+ ast = BootstrapParser.new(source, file: relative_shadow_grammar_path, mode: :extended).parse
49
+ grammar = Normalizer.new(ast, mode: :extended).normalize
50
+ automaton = LALR::Builder.new(grammar).build
51
+ Codegen::Ruby.new(
52
+ automaton, table: :compact, line_convert: false, runtime_require: nil
53
+ ).generate
33
54
  end
34
55
 
35
56
  # @rbs () -> String
36
57
  def relative_grammar_path
37
58
  "lib/ibex/frontend/grammar.y"
38
59
  end
60
+
61
+ # @rbs () -> String
62
+ def relative_shadow_grammar_path
63
+ "lib/ibex/frontend/shadow_grammar.y"
64
+ end
39
65
  end
40
66
  end
41
67
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # Immutable result of resolving one root grammar and its fragment closure.
6
+ class Resolution
7
+ EMPTY_CHAIN = Array.new(0).freeze #: Array[IR::source_provenance]
8
+ private_constant :EMPTY_CHAIN
9
+
10
+ attr_reader :root #: AST::Root
11
+ attr_reader :root_path #: String
12
+ attr_reader :root_directory #: String
13
+ attr_reader :files #: Array[String]
14
+
15
+ # @rbs (root: AST::Root, root_path: String, root_directory: String, files: Array[String],
16
+ # include_chains: Hash[AST::Rule, Array[IR::source_provenance]]) -> void
17
+ def initialize(root:, root_path:, root_directory:, files:, include_chains:)
18
+ @root = deep_freeze_ast(root)
19
+ @root_path = root_path.dup.freeze
20
+ @root_directory = root_directory.dup.freeze
21
+ @files = files.map { |file| file.dup.freeze }.freeze
22
+ chains = {} #: Hash[AST::Rule, Array[IR::source_provenance]]
23
+ @include_chains = chains.compare_by_identity
24
+ include_chains.each do |rule, chain|
25
+ frozen_chain = chain.map { |entry| copy_provenance(entry) }.freeze
26
+ @include_chains[rule] = frozen_chain
27
+ end
28
+ @include_chains.freeze
29
+ freeze
30
+ end
31
+
32
+ # @rbs (AST::Rule rule) -> Array[IR::source_provenance]
33
+ def include_chain_for(rule)
34
+ @include_chains.fetch(rule, EMPTY_CHAIN)
35
+ end
36
+
37
+ private
38
+
39
+ # AST payloads are recursively heterogeneous and are only traversed to freeze them.
40
+ # @rbs (Object? value) -> Object?
41
+ def deep_freeze_ast(value)
42
+ case value
43
+ when Struct
44
+ value.each_pair { |_name, child| deep_freeze_ast(child) }
45
+ when Array
46
+ value.each { |child| deep_freeze_ast(child) }
47
+ when Hash
48
+ value.each do |key, child|
49
+ deep_freeze_ast(key)
50
+ deep_freeze_ast(child)
51
+ end
52
+ end
53
+ value.freeze
54
+ end
55
+
56
+ # @rbs (IR::source_provenance entry) -> IR::source_provenance
57
+ def copy_provenance(entry)
58
+ span = entry[:byte_span]
59
+ frozen_span = span && { start: span[:start], end: span[:end] } #: IR::byte_span?
60
+ frozen_span&.freeze
61
+ provenance = {
62
+ file: entry[:file]&.dup&.freeze,
63
+ root: entry[:root]&.dup&.freeze,
64
+ byte_span: frozen_span
65
+ } #: IR::source_provenance
66
+ provenance.freeze
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # An actual filesystem read failure while resolving a grammar closure.
6
+ class ResolutionIOError < Ibex::Error; end
7
+
8
+ # Loads an explicit root grammar and resolves its extended-mode fragment graph.
9
+ class Resolver
10
+ GLOB_CHARACTERS = /[*?\[\]{}]/ #: Regexp
11
+ WINDOWS_ABSOLUTE = %r{\A(?:[A-Za-z]:[\\/]|\\\\)} #: Regexp
12
+
13
+ # @rbs @input_path: String
14
+ # @rbs @mode: Symbol
15
+ # @rbs @loader: SourceLoader
16
+ # @rbs @root_path: String
17
+ # @rbs @root_directory: String
18
+ # @rbs @files: Array[String]
19
+ # @rbs @visiting: Array[String]
20
+ # @rbs @loaded: Hash[String, bool]
21
+ # @rbs @include_chains: Hash[AST::Rule, Array[IR::source_provenance]]
22
+ # @rbs @attempted_paths: Array[String]
23
+
24
+ attr_reader :attempted_paths #: Array[String]
25
+
26
+ # @rbs (String path, ?mode: Symbol, ?loader: SourceLoader) -> void
27
+ def initialize(path, mode: :default, loader: SourceLoader.new)
28
+ raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
29
+
30
+ @input_path = path
31
+ @mode = mode
32
+ @loader = loader
33
+ @attempted_paths = [File.expand_path(path)]
34
+ end
35
+
36
+ # @rbs () -> Resolution
37
+ def resolve
38
+ cached = @resolution
39
+ return cached if cached
40
+
41
+ prepare_resolution
42
+ parsed_root = parse_root(@root_path)
43
+ declarations, rules = expand_node(parsed_root, [])
44
+ root = AST::Root.new(
45
+ class_name: parsed_root.class_name, superclass: parsed_root.superclass,
46
+ declarations: declarations, rules: rules, user_code: parsed_root.user_code, loc: parsed_root.loc,
47
+ extended: parsed_root.extended, cst: parsed_root.cst, extended_loc: parsed_root.extended_loc
48
+ )
49
+ @loaded[@root_path] = true
50
+ @visiting.pop
51
+ @resolution = Resolution.new(
52
+ root: root, root_path: @root_path, root_directory: @root_directory,
53
+ files: @files, include_chains: @include_chains
54
+ )
55
+ end
56
+
57
+ # @rbs () -> Array[String]
58
+ def dependencies
59
+ resolve.files
60
+ end
61
+
62
+ # Source bytes actually consumed by the parser, in resolution order.
63
+ # @rbs () -> Array[GenerationInput]
64
+ def source_records
65
+ @loader.read_records
66
+ end
67
+
68
+ private
69
+
70
+ # @rbs () -> void
71
+ def prepare_resolution
72
+ @root_path = canonical_root
73
+ @root_directory = File.dirname(@root_path)
74
+ @files = [@root_path] #: Array[String]
75
+ @visiting = [@root_path] #: Array[String]
76
+ @loaded = {} #: Hash[String, bool]
77
+ chains = {} #: Hash[AST::Rule, Array[IR::source_provenance]]
78
+ @include_chains = chains.compare_by_identity
79
+ @attempted_paths = [File.expand_path(@input_path)]
80
+ end
81
+
82
+ # @rbs () -> String
83
+ def canonical_root
84
+ path = @loader.canonical_path(@input_path)
85
+ raise Ibex::Error, "#{@input_path}:1:1: root grammar must be a file" unless @loader.file?(path)
86
+
87
+ @loader.record_access(path, @input_path)
88
+ path
89
+ rescue SystemCallError => e
90
+ raise ResolutionIOError, "#{@input_path}:1:1: cannot read root grammar: #{e.message}"
91
+ end
92
+
93
+ # @rbs (String path) -> AST::Root
94
+ def parse_root(path)
95
+ Parser.new(@loader.read(path), file: path, mode: @mode).parse
96
+ rescue SystemCallError => e
97
+ raise ResolutionIOError, "#{path}:1:1: cannot read grammar: #{e.message}"
98
+ end
99
+
100
+ # @rbs (String path) -> AST::Fragment
101
+ def parse_fragment(path)
102
+ Parser.new(@loader.read(path), file: path, mode: :extended).parse_fragment
103
+ rescue SystemCallError => e
104
+ raise ResolutionIOError, "#{path}:1:1: cannot read fragment: #{e.message}"
105
+ end
106
+
107
+ # @rbs (AST::Root | AST::Fragment node, Array[IR::source_provenance] chain) ->
108
+ # [Array[AST::declaration], Array[AST::Rule]]
109
+ def expand_node(node, chain)
110
+ declarations = [] #: Array[AST::declaration]
111
+ rules = [] #: Array[AST::Rule]
112
+ node.declarations.each do |declaration|
113
+ if declaration.is_a?(AST::Include)
114
+ included_declarations, included_rules = expand_include(declaration, chain)
115
+ declarations.concat(included_declarations)
116
+ rules.concat(included_rules)
117
+ else
118
+ declarations << declaration
119
+ end
120
+ end
121
+ node.rules.each do |rule|
122
+ @include_chains[rule] = chain
123
+ rules << rule
124
+ end
125
+ [declarations, rules]
126
+ end
127
+
128
+ # @rbs (AST::Include include_node, Array[IR::source_provenance] chain) ->
129
+ # [Array[AST::declaration], Array[AST::Rule]]
130
+ def expand_include(include_node, chain)
131
+ target = canonical_include(include_node)
132
+ reject_cycle(include_node, target)
133
+ return [[], []] if @loaded[target]
134
+
135
+ @files << target
136
+ @visiting << target
137
+ fragment = parse_fragment(target)
138
+ next_chain = chain + [source_provenance(target)]
139
+ declarations, rules = expand_node(fragment, next_chain)
140
+ @visiting.pop
141
+ @loaded[target] = true
142
+ [declarations, rules]
143
+ end
144
+
145
+ # @rbs (AST::Include include_node) -> String
146
+ def canonical_include(include_node)
147
+ validate_include_path(include_node)
148
+ candidate = File.expand_path(include_node.path, File.dirname(include_node.loc.file))
149
+ @attempted_paths << candidate unless @attempted_paths.include?(candidate)
150
+ canonical = @loader.canonical_path(candidate)
151
+ unless @loader.file?(canonical)
152
+ fail_include(include_node, "include path is not a file: #{include_node.path.inspect}")
153
+ end
154
+
155
+ unless inside_root?(canonical)
156
+ message = "include resolves outside the root grammar directory: #{include_node.path.inspect}"
157
+ fail_include(include_node, message)
158
+ end
159
+ @loader.record_access(canonical, candidate)
160
+ canonical
161
+ rescue Errno::ENOENT, Errno::ENOTDIR
162
+ fail_include(include_node, "include file does not exist: #{include_node.path.inspect}")
163
+ rescue SystemCallError => e
164
+ message = "#{include_node.loc}: cannot read include #{include_node.path.inspect}: #{e.message}"
165
+ raise ResolutionIOError, message
166
+ end
167
+
168
+ # @rbs (AST::Include include_node) -> void
169
+ def validate_include_path(include_node)
170
+ path = include_node.path
171
+ fail_include(include_node, "include path must not be empty") if path.empty?
172
+ fail_include(include_node, "include path must not contain NUL") if path.include?("\0")
173
+ if path.start_with?("/") || path.match?(WINDOWS_ABSOLUTE)
174
+ fail_include(include_node, "include path must be relative")
175
+ end
176
+ if path.split(%r{[\\/]}).include?("..")
177
+ fail_include(include_node, "include path must not contain parent traversal")
178
+ end
179
+ return unless path.match?(GLOB_CHARACTERS)
180
+
181
+ fail_include(include_node, "include path must not contain glob metacharacters")
182
+ end
183
+
184
+ # @rbs (String path) -> bool
185
+ def inside_root?(path)
186
+ directory = File.dirname(path)
187
+ loop do
188
+ return true if directory == @root_directory
189
+
190
+ parent = File.dirname(directory)
191
+ return false if parent == directory
192
+
193
+ directory = parent
194
+ end
195
+ end
196
+
197
+ # @rbs (AST::Include include_node, String target) -> void
198
+ def reject_cycle(include_node, target)
199
+ start = @visiting.index(target)
200
+ return unless start
201
+
202
+ cycle = @visiting.drop(start) + [target]
203
+ fail_include(include_node, "include cycle: #{cycle.join(' -> ')}")
204
+ end
205
+
206
+ # @rbs (String file) -> IR::source_provenance
207
+ def source_provenance(file)
208
+ { file: file, root: @root_directory, byte_span: nil }
209
+ end
210
+
211
+ # @rbs (AST::Include include_node, String message) -> bot
212
+ def fail_include(include_node, message)
213
+ raise Ibex::Error, "#{include_node.loc}: #{message}"
214
+ end
215
+ end
216
+ end
217
+ end