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
@@ -48,9 +48,29 @@ module Ibex
48
48
  @declarations << AST::Expect.new(conflicts: conflicts, loc: next_location)
49
49
  end
50
50
 
51
- # @rbs (Object name) -> void
52
- def start(name)
53
- @declarations << AST::Start.new(name: name.to_s, loc: next_location)
51
+ # @rbs (*Object names) -> void
52
+ def start(*names)
53
+ @declarations << AST::Start.new(names: names.map(&:to_s), loc: next_location)
54
+ end
55
+
56
+ # @rbs (*Object names) -> void
57
+ def recover_sync(*names)
58
+ @declarations << AST::Recovery.new(sync_tokens: names.map(&:to_s), loc: next_location)
59
+ end
60
+
61
+ # @rbs (*Object names) -> void
62
+ def on_error_reduce(*names)
63
+ @declarations << AST::OnErrorReduce.new(names: names.map(&:to_s), loc: next_location)
64
+ end
65
+
66
+ # @rbs (Object source) -> void
67
+ def test_accept(source)
68
+ @declarations << AST::GrammarTest.new(expectation: :accept, source: source.to_s, loc: next_location)
69
+ end
70
+
71
+ # @rbs (Object source) -> void
72
+ def test_reject(source)
73
+ @declarations << AST::GrammarTest.new(expectation: :reject, source: source.to_s, loc: next_location)
54
74
  end
55
75
 
56
76
  # @rbs (Object name, Object expression) -> void
@@ -60,6 +80,29 @@ module Ibex
60
80
  @declarations << AST::Convert.new(pairs: [pair], loc: location)
61
81
  end
62
82
 
83
+ # @rbs (Object name, Object value) -> void
84
+ def display(name, value)
85
+ @declarations << AST::DisplayName.new(name: name.to_s, value: metadata_value(value, "display"),
86
+ loc: next_location)
87
+ end
88
+
89
+ # @rbs (Object name, Object value) -> void
90
+ def type(name, value)
91
+ @declarations << AST::SemanticType.new(name: name.to_s, value: metadata_value(value, "type"),
92
+ loc: next_location)
93
+ end
94
+
95
+ # @rbs (Object name, ?Object? semantic_type) -> void
96
+ def param(name, semantic_type = nil)
97
+ type = semantic_type && metadata_value(semantic_type, "%param")
98
+ @declarations << AST::Parameter.new(name: name.to_s, semantic_type: type, loc: next_location)
99
+ end
100
+
101
+ # @rbs (Object name, Object code) -> void
102
+ def printer(name, code)
103
+ @declarations << AST::Printer.new(name: name.to_s, code: code.to_s, loc: next_location)
104
+ end
105
+
63
106
  # @rbs (?direction: Symbol) { (PrecedenceBuilder) -> void } -> void
64
107
  def precedence(direction: :low_to_high)
65
108
  location = next_location
@@ -73,7 +116,9 @@ module Ibex
73
116
  location = next_location
74
117
  builder = RuleBuilder.new(self, location)
75
118
  yield builder
76
- @rules << AST::Rule.new(lhs: lhs.to_s, alternatives: builder.alternatives, loc: location)
119
+ @rules << AST::Rule.new(
120
+ lhs: lhs.to_s, parameters: [], alternatives: builder.alternatives, loc: location, inline: false
121
+ )
77
122
  end
78
123
 
79
124
  # @rbs (Object name, Object code) -> void
@@ -134,6 +179,19 @@ module Ibex
134
179
  @line += 1
135
180
  location
136
181
  end
182
+
183
+ private
184
+
185
+ # @rbs (Object value, String feature) -> String
186
+ def metadata_value(value, feature)
187
+ string = value.to_s
188
+ raise ArgumentError, "#{feature} value must not be empty" if string.strip.empty?
189
+ raise ArgumentError, "#{feature} value must be a single line" if string.match?(/[\r\n]/)
190
+ raise ArgumentError, "#{feature} value must not contain control characters" if
191
+ string.match?(/[[:cntrl:]]/)
192
+
193
+ string
194
+ end
137
195
  end
138
196
 
139
197
  # Collects ordered associativity levels.
@@ -180,13 +238,14 @@ module Ibex
180
238
  @alternatives = [] #: Array[AST::Alternative]
181
239
  end
182
240
 
183
- # @rbs (*Object items, ?action: Object?, ?precedence: Object?) -> void
184
- def alt(*items, action: nil, precedence: nil)
241
+ # @rbs (*Object items, ?action: Object?, ?precedence: Object?, ?node: AST::NodeAnnotation?) -> void
242
+ def alt(*items, action: nil, precedence: nil, node: nil)
185
243
  location = @grammar.next_location || @default_location
186
244
  normalized = items.map { |item| @grammar.normalize_item(item) }
187
245
  action_node = action && AST::InlineAction.new(code: action.to_s, loc: location)
188
246
  alternative = AST::Alternative.new(
189
- items: normalized, action: action_node, precedence: precedence&.to_s, loc: location
247
+ items: normalized, action: action_node, precedence: precedence&.to_s,
248
+ node_annotation: node, loc: location
190
249
  )
191
250
  @alternatives << alternative
192
251
  end
@@ -0,0 +1,453 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Frontend
6
+ # Deterministically formats grammar trivia and rejects any semantic change.
7
+ # rubocop:disable Metrics/ClassLength -- token layout policy stays together so adjacency rules remain auditable.
8
+ class Formatter
9
+ DECLARATION_STARTS = %i[
10
+ PRAGMA INCLUDE IMPORT TOKEN PRECHIGH PRECLOW OPTIONS EXPECT EXPECT_RR START RECOVER ON_ERROR_REDUCE TEST LEXER
11
+ CONVERT DISPLAY TYPE PARAM PRINTER PARSER RULE
12
+ ].freeze #: Array[Symbol]
13
+ ASSOCIATIONS = %i[LEFT RIGHT NONASSOC].freeze #: Array[Symbol]
14
+ CALLABLES = %i[LHS PARAMETERIZED_REFERENCE SEPARATED_LIST SEPARATED_NONEMPTY_LIST].freeze #: Array[Symbol]
15
+ SUFFIXES = %w[? * +].freeze #: Array[String]
16
+ CLOSERS = %w[) , ;].freeze #: Array[String]
17
+ ADJACENT_AFTER = ["::", "("].freeze #: Array[String]
18
+
19
+ # @rbs!
20
+ # type formatter_element = {
21
+ # segment: Segment,
22
+ # external: external_token?,
23
+ # role: Symbol?,
24
+ # gap: Array[Segment]
25
+ # }
26
+ # type formatter_state = {
27
+ # section: :declarations | :rules,
28
+ # precedence_closer: external_token?,
29
+ # conversion: bool,
30
+ # conversion_name: bool,
31
+ # parser: bool,
32
+ # parser_key: bool,
33
+ # depth: Integer,
34
+ # rule_colon: bool
35
+ # }
36
+
37
+ # @rbs (String source, ?file: String, ?mode: Symbol) -> String
38
+ def self.format(source, file: "(grammar)", mode: :default)
39
+ new(mode: mode).format(source, file: file)
40
+ end
41
+
42
+ # @rbs (?mode: Symbol) -> void
43
+ def initialize(mode: :default)
44
+ raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
45
+
46
+ @mode = mode
47
+ end
48
+
49
+ # @rbs (String source, ?file: String) -> String
50
+ def format(source, file: "(grammar)")
51
+ document = Parser.new(source, file: file, mode: @mode).parse_source_document
52
+ formatted = render(document)
53
+ reparsed = Parser.new(formatted, file: file, mode: @mode).parse_source_document
54
+ unless same_semantic_projection?(document.ast, reparsed.ast)
55
+ raise Ibex::Error, "#{file}: formatting would change grammar semantics"
56
+ end
57
+
58
+ formatted
59
+ rescue Ibex::Error
60
+ raise
61
+ rescue StandardError => e
62
+ raise Ibex::Error, "#{file}: formatting failed: #{e.message}"
63
+ end
64
+
65
+ private
66
+
67
+ # @rbs (SourceDocument document) -> String
68
+ def render(document)
69
+ @default_newline = document.source.match(/\r\n|\n/)&.[](0) || "\n"
70
+ elements = build_elements(document)
71
+ output = +""
72
+ previous = nil #: formatter_element?
73
+ elements.each do |current|
74
+ output << separator(previous, current)
75
+ output << current.fetch(:segment).text
76
+ previous = current
77
+ end
78
+ output
79
+ end
80
+
81
+ # @rbs (SourceDocument document) -> Array[formatter_element]
82
+ def build_elements(document)
83
+ external_types = external_types(document.tokens)
84
+ pending = [] #: Array[Segment]
85
+ elements = document.cst.each_with_object([]) do |segment, result|
86
+ if %i[whitespace newline].include?(segment.kind)
87
+ pending << segment
88
+ next
89
+ end
90
+
91
+ result << {
92
+ segment: segment,
93
+ external: segment.token_index && external_types[segment.token_index],
94
+ role: nil,
95
+ gap: pending
96
+ }
97
+ pending = []
98
+ end
99
+ annotate_roles(elements)
100
+ elements
101
+ end
102
+
103
+ # @rbs (Array[Token] tokens) -> Hash[Integer, external_token]
104
+ def external_types(tokens)
105
+ adapter = TokenAdapter.new(tokens, extended: @mode == :extended)
106
+ types = {} #: Hash[Integer, external_token]
107
+ index = 0
108
+ while (classified = adapter.next_token)
109
+ types[index] = classified.fetch(0)
110
+ index += 1
111
+ end
112
+ types
113
+ end
114
+
115
+ # @rbs (Array[formatter_element] elements) -> void
116
+ def annotate_roles(elements)
117
+ state = {
118
+ section: :declarations, precedence_closer: nil, conversion: false,
119
+ conversion_name: true, parser: false, parser_key: true, depth: 0, rule_colon: false
120
+ } #: formatter_state
121
+ elements.each do |element|
122
+ external = element.fetch(:external)
123
+ next unless external
124
+
125
+ element[:role] = token_role(external, state)
126
+ advance_role_state(external, element.fetch(:role), state)
127
+ end
128
+ annotate_comment_indentation(elements)
129
+ end
130
+
131
+ # @rbs (Array[formatter_element] elements) -> void
132
+ def annotate_comment_indentation(elements)
133
+ following_role = nil #: Symbol?
134
+ elements.reverse_each do |element|
135
+ segment = element.fetch(:segment)
136
+ if comment?(segment)
137
+ element[:role] = :rule_comment if following_role == :rule_start
138
+ element[:role] = :alternative_comment if following_role == :alternative
139
+ elsif element.fetch(:external)
140
+ following_role = element.fetch(:role)
141
+ end
142
+ end
143
+ end
144
+
145
+ # @rbs (external_token external, formatter_state state) -> Symbol?
146
+ def token_role(external, state)
147
+ document_role = document_token_role(external, state)
148
+ return document_role if document_role
149
+
150
+ rule_token_role(external, state)
151
+ end
152
+
153
+ # @rbs (external_token external, formatter_state state) -> Symbol?
154
+ def document_token_role(external, state)
155
+ return :user_code if external == :USER_CODE
156
+
157
+ nested_role = nested_declaration_role(external, state)
158
+ return nested_role if nested_role
159
+ return :conversion_entry if state.fetch(:conversion) && state.fetch(:conversion_name)
160
+ return :rule_keyword if external == :RULE
161
+ return :declaration if DECLARATION_STARTS.include?(external)
162
+
163
+ nil
164
+ end
165
+
166
+ # @rbs (external_token external, formatter_state state) -> Symbol?
167
+ def nested_declaration_role(external, state)
168
+ return precedence_role(external, state) if %i[PRECHIGH PRECLOW].include?(external)
169
+ return :precedence_level if ASSOCIATIONS.include?(external) && state[:precedence_closer]
170
+ return :parser_end if external == :END && state.fetch(:parser)
171
+ return :parser_setting if state.fetch(:parser) && state.fetch(:parser_key)
172
+ return section_end_role(state) if external == :END
173
+
174
+ nil
175
+ end
176
+
177
+ # @rbs (formatter_state state) -> Symbol?
178
+ def section_end_role(state)
179
+ return :conversion_end if state.fetch(:conversion)
180
+ return :grammar_end if state.fetch(:section) == :rules
181
+
182
+ nil
183
+ end
184
+
185
+ # @rbs (external_token external, formatter_state state) -> Symbol?
186
+ def rule_token_role(external, state)
187
+ return :rule_start if external == :INLINE && state.fetch(:section) == :rules
188
+ return :rule_start if external == :LHS
189
+ return :alternative if external == "|" && state.fetch(:section) == :rules && state.fetch(:depth).zero?
190
+
191
+ if external == ":"
192
+ return :rule_colon if state.fetch(:rule_colon) && state.fetch(:depth).zero?
193
+
194
+ return :named_reference
195
+ end
196
+
197
+ nil
198
+ end
199
+
200
+ # @rbs (external_token external, formatter_state state) -> Symbol
201
+ def precedence_role(external, state)
202
+ return :precedence_end if state[:precedence_closer] == external
203
+
204
+ :declaration
205
+ end
206
+
207
+ # @rbs (external_token external, Symbol? role, formatter_state state) -> void
208
+ def advance_role_state(external, role, state)
209
+ advance_precedence_state(external, role, state)
210
+ advance_conversion_state(external, role, state)
211
+ advance_parser_state(external, role, state)
212
+ advance_rule_state(external, role, state)
213
+ end
214
+
215
+ # @rbs (external_token external, Symbol? role, formatter_state state) -> void
216
+ def advance_parser_state(external, role, state)
217
+ if external == :PARSER
218
+ state[:parser] = true
219
+ state[:parser_key] = true
220
+ elsif role == :parser_end
221
+ state[:parser] = false
222
+ elsif state.fetch(:parser)
223
+ state[:parser_key] = !state.fetch(:parser_key)
224
+ end
225
+ end
226
+
227
+ # @rbs (external_token external, Symbol? role, formatter_state state) -> void
228
+ def advance_precedence_state(external, role, state)
229
+ if role == :declaration && %i[PRECHIGH PRECLOW].include?(external)
230
+ state[:precedence_closer] = external == :PRECHIGH ? :PRECLOW : :PRECHIGH
231
+ elsif role == :precedence_end
232
+ state[:precedence_closer] = nil
233
+ end
234
+ end
235
+
236
+ # @rbs (external_token external, Symbol? role, formatter_state state) -> void
237
+ def advance_conversion_state(external, role, state)
238
+ if external == :CONVERT
239
+ state[:conversion] = true
240
+ state[:conversion_name] = true
241
+ elsif role == :conversion_end
242
+ state[:conversion] = false
243
+ elsif state.fetch(:conversion) && role == :conversion_entry
244
+ state[:conversion_name] = false
245
+ elsif state.fetch(:conversion) && !state.fetch(:conversion_name)
246
+ state[:conversion_name] = true
247
+ end
248
+ end
249
+
250
+ # @rbs (external_token external, Symbol? role, formatter_state state) -> void
251
+ def advance_rule_state(external, role, state)
252
+ state[:section] = :rules if external == :RULE
253
+ state[:rule_colon] = true if external == :LHS
254
+ state[:rule_colon] = false if role == :rule_colon
255
+ state[:depth] += 1 if external == "("
256
+ state[:depth] -= 1 if external == ")"
257
+ end
258
+
259
+ # @rbs (formatter_element? previous, formatter_element current) -> String
260
+ def separator(previous, current)
261
+ gap = current.fetch(:gap)
262
+ return leading_separator(current, gap) unless previous
263
+
264
+ previous_segment = previous.fetch(:segment)
265
+ current_segment = current.fetch(:segment)
266
+ return "" if direct_opaque_pair?(previous_segment, current_segment)
267
+ return trailing_separator(previous, gap) if current_segment.kind == :eof
268
+ return comment_separator(previous, current, gap) if comment?(previous_segment) || comment?(current_segment)
269
+
270
+ kind = separator_kind(previous, current)
271
+ render_separator(kind, gap, indentation(current))
272
+ end
273
+
274
+ # @rbs (formatter_element current, Array[Segment] gap) -> String
275
+ def leading_separator(current, gap)
276
+ return "" unless newline_in?(gap)
277
+
278
+ render_separator(:line, gap, indentation(current))
279
+ end
280
+
281
+ # @rbs (formatter_element previous, Array[Segment] gap) -> String
282
+ def trailing_separator(previous, gap)
283
+ segment = previous.fetch(:segment)
284
+ return "" if segment.kind == :user_code_body
285
+ return render_separator(:line, gap, 0) if previous.fetch(:role) == :grammar_end
286
+
287
+ render_separator(gap.any? { |item| item.kind == :newline } ? :line : :none, gap, 0)
288
+ end
289
+
290
+ # @rbs (Segment previous, Segment current) -> bool
291
+ def direct_opaque_pair?(previous, current)
292
+ (previous.kind == :user_code_marker && current.kind == :user_code_body) ||
293
+ (previous.kind == :user_code_body && current.kind == :user_code_marker)
294
+ end
295
+
296
+ # @rbs (Segment segment) -> bool
297
+ def comment?(segment)
298
+ %i[line_comment block_comment].include?(segment.kind)
299
+ end
300
+
301
+ # @rbs (formatter_element previous, formatter_element current, Array[Segment] gap) -> String
302
+ def comment_separator(previous, current, gap)
303
+ previous_segment = previous.fetch(:segment)
304
+ current_segment = current.fetch(:segment)
305
+ if text_ends_with_newline?(previous_segment.text)
306
+ newlines = gap.select { |segment| segment.kind == :newline }.map(&:text)
307
+ return newlines.join + (" " * indentation(current))
308
+ end
309
+ if previous_segment.kind == :line_comment || newline_in?(gap)
310
+ return render_separator(:line, gap, indentation(current))
311
+ end
312
+ return " " if current_segment.kind == :line_comment || previous_segment.kind == :block_comment ||
313
+ current_segment.kind == :block_comment
314
+
315
+ render_separator(:space, gap, indentation(current))
316
+ end
317
+
318
+ # @rbs (formatter_element previous, formatter_element current) -> Symbol
319
+ def separator_kind(previous, current)
320
+ previous_external = previous.fetch(:external)
321
+ current_external = current.fetch(:external)
322
+ previous_role = previous.fetch(:role)
323
+ current_role = current.fetch(:role)
324
+
325
+ return :line if line_boundary?(previous_role, current_role, previous, current)
326
+ return :none if adjacent_tokens?(previous_external, current_external, previous_role, current_role)
327
+
328
+ :space
329
+ end
330
+
331
+ # @rbs (external_token? previous_external, external_token? current_external,
332
+ # Symbol? previous_role, Symbol? current_role) -> bool
333
+ def adjacent_tokens?(previous_external, current_external, previous_role, current_role)
334
+ return true if current_role == :named_reference || previous_role == :named_reference
335
+ return true if current_external == "::"
336
+ return true if CLOSERS.include?(current_external) || SUFFIXES.include?(current_external)
337
+ return true if ADJACENT_AFTER.include?(previous_external)
338
+
339
+ current_external == "(" && CALLABLES.include?(previous_external)
340
+ end
341
+
342
+ # @rbs (Symbol? previous_role, Symbol? current_role, formatter_element previous,
343
+ # formatter_element current) -> bool
344
+ def line_boundary?(previous_role, current_role, previous, current)
345
+ return false if current_role == :rule_start && previous.fetch(:external) == :INLINE
346
+
347
+ return true if %i[declaration rule_keyword precedence_level precedence_end conversion_entry
348
+ conversion_end parser_setting parser_end rule_start alternative grammar_end
349
+ user_code].include?(current_role)
350
+ return true if previous_role == :rule_keyword
351
+ return false unless current.fetch(:segment).kind == :user_code_marker
352
+
353
+ previous.fetch(:segment).kind != :user_code_body
354
+ end
355
+
356
+ # @rbs (formatter_element element) -> Integer
357
+ def indentation(element)
358
+ case element.fetch(:role)
359
+ when :rule_start, :precedence_level, :conversion_entry, :parser_setting, :rule_comment then 2
360
+ when :alternative, :alternative_comment then 4
361
+ else 0
362
+ end
363
+ end
364
+
365
+ # @rbs (Symbol kind, Array[Segment] gap, Integer indentation) -> String
366
+ def render_separator(kind, gap, indentation)
367
+ return "" if kind == :none
368
+ return " " if kind == :space
369
+
370
+ newlines = gap.select { |segment| segment.kind == :newline }.map(&:text)
371
+ newlines = [@default_newline] if newlines.empty?
372
+ newlines.join + (" " * indentation)
373
+ end
374
+
375
+ # @rbs (Array[Segment] gap) -> bool
376
+ def newline_in?(gap)
377
+ gap.any? { |segment| segment.kind == :newline }
378
+ end
379
+
380
+ # @rbs (String text) -> bool
381
+ def text_ends_with_newline?(text)
382
+ text.end_with?("\n")
383
+ end
384
+
385
+ # @rbs (Object? left, Object? right) -> bool
386
+ def same_semantic_projection?(left, right)
387
+ pending = [[left, right]] #: Array[[Object?, Object?]]
388
+ until pending.empty?
389
+ pair = pending.pop
390
+ next unless pair
391
+
392
+ left_value, right_value = pair
393
+ next if left_value.equal?(right_value)
394
+ return false unless comparable_semantic_values?(left_value, right_value)
395
+
396
+ append_semantic_children(pending, left_value, right_value)
397
+ end
398
+ true
399
+ end
400
+
401
+ # @rbs (Object? left, Object? right) -> bool
402
+ def comparable_semantic_values?(left, right)
403
+ if left.is_a?(Struct)
404
+ return false unless right.is_a?(Struct) && left.instance_of?(right.class)
405
+
406
+ return left.members == right.members
407
+ end
408
+ return right.is_a?(Array) && left.length == right.length if left.is_a?(Array)
409
+ return comparable_semantic_hashes?(left, right) if left.is_a?(Hash)
410
+
411
+ left == right
412
+ end
413
+
414
+ # @rbs (Hash[Object?, Object?] left, Object? right) -> bool
415
+ def comparable_semantic_hashes?(left, right)
416
+ return false unless right.is_a?(Hash)
417
+
418
+ left_keys = semantic_keys(left)
419
+ right_keys = semantic_keys(right)
420
+ left_keys.length == right_keys.length && left_keys.all? { |key| right_keys.include?(key) }
421
+ end
422
+
423
+ # @rbs (Array[[Object?, Object?]] pending, Object? left, Object? right) -> void
424
+ def append_semantic_children(pending, left, right)
425
+ case left
426
+ when Struct
427
+ return unless right.is_a?(Struct)
428
+
429
+ semantic_members(left).reverse_each { |member| pending << [left[member], right[member]] }
430
+ when Array
431
+ return unless right.is_a?(Array)
432
+
433
+ left.each_index.reverse_each { |index| pending << [left[index], right[index]] }
434
+ when Hash
435
+ return unless right.is_a?(Hash)
436
+
437
+ semantic_keys(left).reverse_each { |key| pending << [left[key], right[key]] }
438
+ end
439
+ end
440
+
441
+ # @rbs (Struct[Object?] value) -> Array[Symbol]
442
+ def semantic_members(value)
443
+ value.members.reject { |member| %i[loc span extended_loc].include?(member) }
444
+ end
445
+
446
+ # @rbs (Hash[Object?, Object?] value) -> Array[Object?]
447
+ def semantic_keys(value)
448
+ value.keys.reject { |key| %i[loc span].include?(key) }
449
+ end
450
+ end
451
+ # rubocop:enable Metrics/ClassLength
452
+ end
453
+ end