ibex 0.1.0 → 0.2.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 (384) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +247 -104
  3. data/docs/architecture.md +322 -28
  4. data/docs/cst-migration.md +107 -0
  5. data/docs/cst.md +213 -0
  6. data/docs/development.md +129 -0
  7. data/docs/editor-setup.md +25 -0
  8. data/docs/error-ux.md +60 -0
  9. data/docs/grammar-reference.md +458 -18
  10. data/docs/lexer-migration.md +51 -0
  11. data/docs/racc-migration.md +34 -6
  12. data/docs/release-readiness.md +163 -0
  13. data/docs/stability.md +124 -0
  14. data/examples/README.md +60 -0
  15. data/examples/calculator.y +44 -0
  16. data/examples/csv.y +32 -0
  17. data/examples/ini.y +70 -0
  18. data/examples/json.y +48 -0
  19. data/examples/tiny_language.y +75 -0
  20. data/lib/ibex/analysis/sets.rb +5 -4
  21. data/lib/ibex/artifact_set.rb +63 -0
  22. data/lib/ibex/cli/ambiguity.rb +66 -0
  23. data/lib/ibex/cli/counterexample_options.rb +6 -2
  24. data/lib/ibex/cli/coverage.rb +173 -0
  25. data/lib/ibex/cli/debug.rb +106 -0
  26. data/lib/ibex/cli/diagnostics.rb +116 -0
  27. data/lib/ibex/cli/documentation.rb +67 -0
  28. data/lib/ibex/cli/error_messages.rb +177 -0
  29. data/lib/ibex/cli/explain.rb +76 -0
  30. data/lib/ibex/cli/formatting.rb +386 -0
  31. data/lib/ibex/cli/generation_artifacts.rb +138 -0
  32. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  33. data/lib/ibex/cli/grammar_tests.rb +122 -0
  34. data/lib/ibex/cli/ir_tools.rb +184 -0
  35. data/lib/ibex/cli/lsp.rb +33 -0
  36. data/lib/ibex/cli/outputs.rb +91 -8
  37. data/lib/ibex/cli/racc_migration.rb +87 -0
  38. data/lib/ibex/cli/samples.rb +92 -0
  39. data/lib/ibex/cli/watch.rb +103 -0
  40. data/lib/ibex/cli.rb +506 -37
  41. data/lib/ibex/codegen/action_locations.rb +103 -0
  42. data/lib/ibex/codegen/action_method_source.rb +210 -0
  43. data/lib/ibex/codegen/action_source.rb +118 -0
  44. data/lib/ibex/codegen/ambiguity.rb +173 -0
  45. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  46. data/lib/ibex/codegen/documentation.rb +170 -0
  47. data/lib/ibex/codegen/explain.rb +312 -0
  48. data/lib/ibex/codegen/generated_action_abi.rb +288 -0
  49. data/lib/ibex/codegen/html.rb +94 -10
  50. data/lib/ibex/codegen/mermaid.rb +43 -0
  51. data/lib/ibex/codegen/railroad.rb +197 -0
  52. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  53. data/lib/ibex/codegen/rbs.rb +323 -2
  54. data/lib/ibex/codegen/report.rb +46 -6
  55. data/lib/ibex/codegen/ruby.rb +334 -59
  56. data/lib/ibex/codegen/ruby_actions.rb +143 -0
  57. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  58. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  59. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  60. data/lib/ibex/codegen/ruby_syntax.rb +90 -0
  61. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  62. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  63. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  64. data/lib/ibex/coverage/collector.rb +188 -0
  65. data/lib/ibex/coverage/event_stream.rb +97 -0
  66. data/lib/ibex/coverage/report.rb +259 -0
  67. data/lib/ibex/coverage/runtime_event_validator.rb +160 -0
  68. data/lib/ibex/coverage.rb +13 -0
  69. data/lib/ibex/error_messages/parser.rb +159 -0
  70. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  71. data/lib/ibex/error_messages/renderer.rb +65 -0
  72. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  73. data/lib/ibex/error_messages/update.rb +169 -0
  74. data/lib/ibex/error_messages.rb +165 -0
  75. data/lib/ibex/frontend/ast.rb +145 -6
  76. data/lib/ibex/frontend/bootstrap_parser.rb +47 -4
  77. data/lib/ibex/frontend/diagnostic.rb +81 -0
  78. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  79. data/lib/ibex/frontend/dsl.rb +66 -7
  80. data/lib/ibex/frontend/formatter.rb +407 -0
  81. data/lib/ibex/frontend/generated_parser.rb +614 -190
  82. data/lib/ibex/frontend/generated_parser_base.rb +164 -30
  83. data/lib/ibex/frontend/generated_parser_includes.rb +61 -0
  84. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  85. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  86. data/lib/ibex/frontend/generation.rb +33 -0
  87. data/lib/ibex/frontend/lexer.rb +162 -10
  88. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  89. data/lib/ibex/frontend/parser/declarations.rb +247 -11
  90. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  91. data/lib/ibex/frontend/parser/rules.rb +43 -6
  92. data/lib/ibex/frontend/parser.rb +182 -4
  93. data/lib/ibex/frontend/regenerator.rb +26 -1
  94. data/lib/ibex/frontend/resolution.rb +69 -0
  95. data/lib/ibex/frontend/resolver.rb +217 -0
  96. data/lib/ibex/frontend/rule_documentation.rb +103 -0
  97. data/lib/ibex/frontend/source_cursor.rb +132 -8
  98. data/lib/ibex/frontend/source_document.rb +229 -0
  99. data/lib/ibex/frontend/source_loader.rb +150 -0
  100. data/lib/ibex/frontend/source_span.rb +81 -0
  101. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  102. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  103. data/lib/ibex/frontend/token_adapter/declaration_state.rb +216 -26
  104. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  105. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  106. data/lib/ibex/frontend/token_adapter.rb +8 -3
  107. data/lib/ibex/frontend.rb +13 -2
  108. data/lib/ibex/generation_input.rb +57 -0
  109. data/lib/ibex/generation_manifest.rb +200 -0
  110. data/lib/ibex/generation_transaction.rb +261 -0
  111. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  112. data/lib/ibex/generation_transaction_validation.rb +196 -0
  113. data/lib/ibex/grammar_tests.rb +206 -0
  114. data/lib/ibex/ir/automaton_ir.rb +38 -5
  115. data/lib/ibex/ir/grammar_ir.rb +137 -24
  116. data/lib/ibex/ir/lexer_ir.rb +76 -0
  117. data/lib/ibex/ir/migration.rb +120 -0
  118. data/lib/ibex/ir/serialize.rb +110 -19
  119. data/lib/ibex/ir/validator/automaton.rb +345 -0
  120. data/lib/ibex/ir/validator/base.rb +129 -0
  121. data/lib/ibex/ir/validator/grammar.rb +604 -0
  122. data/lib/ibex/ir/validator/lexer.rb +113 -0
  123. data/lib/ibex/ir/validator.rb +62 -0
  124. data/lib/ibex/ir.rb +57 -4
  125. data/lib/ibex/lalr/build_metrics.rb +23 -0
  126. data/lib/ibex/lalr/builder.rb +379 -52
  127. data/lib/ibex/lalr/conflict.rb +1 -0
  128. data/lib/ibex/lalr/conflict_search.rb +11 -5
  129. data/lib/ibex/lalr/counterexample.rb +20 -5
  130. data/lib/ibex/lalr/direct_lookaheads.rb +236 -0
  131. data/lib/ibex/lalr/ielr_partition.rb +152 -0
  132. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  133. data/lib/ibex/lalr.rb +7 -0
  134. data/lib/ibex/location.rb +129 -0
  135. data/lib/ibex/lsp/document_handlers.rb +66 -0
  136. data/lib/ibex/lsp/document_store.rb +264 -0
  137. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  138. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  139. data/lib/ibex/lsp/initialization_handlers.rb +78 -0
  140. data/lib/ibex/lsp/navigation_handlers.rb +56 -0
  141. data/lib/ibex/lsp/position_codec.rb +109 -0
  142. data/lib/ibex/lsp/protocol_error.rb +33 -0
  143. data/lib/ibex/lsp/request_handlers.rb +45 -0
  144. data/lib/ibex/lsp/request_support.rb +87 -0
  145. data/lib/ibex/lsp/server.rb +146 -0
  146. data/lib/ibex/lsp/symbol_index.rb +241 -0
  147. data/lib/ibex/lsp/symbol_index_builder.rb +267 -0
  148. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  149. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  150. data/lib/ibex/lsp/symbol_occurrence.rb +17 -0
  151. data/lib/ibex/lsp/transport.rb +118 -0
  152. data/lib/ibex/lsp/workspace.rb +127 -0
  153. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  154. data/lib/ibex/lsp.rb +32 -0
  155. data/lib/ibex/normalize/declarations.rb +140 -7
  156. data/lib/ibex/normalize/diagnostics.rb +50 -5
  157. data/lib/ibex/normalize/expander.rb +59 -55
  158. data/lib/ibex/normalize/expression.rb +60 -39
  159. data/lib/ibex/normalize/inline_expansion.rb +414 -0
  160. data/lib/ibex/normalize/inline_validation.rb +174 -0
  161. data/lib/ibex/normalize/lexer.rb +131 -0
  162. data/lib/ibex/normalize/named_references.rb +60 -0
  163. data/lib/ibex/normalize/nodes.rb +46 -0
  164. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +69 -0
  165. data/lib/ibex/normalize/parameter_lowering.rb +126 -0
  166. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  167. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  168. data/lib/ibex/normalize/parameters.rb +199 -0
  169. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  170. data/lib/ibex/normalize.rb +179 -14
  171. data/lib/ibex/racc_migration/checker.rb +122 -0
  172. data/lib/ibex/racc_migration/harness.rb +177 -0
  173. data/lib/ibex/racc_migration/report.rb +94 -0
  174. data/lib/ibex/racc_migration.rb +12 -0
  175. data/lib/ibex/rake_task.rb +116 -0
  176. data/lib/ibex/samples.rb +186 -0
  177. data/lib/ibex/table_simulation/result.rb +51 -0
  178. data/lib/ibex/table_simulation/simulator.rb +253 -0
  179. data/lib/ibex/table_simulation/step.rb +60 -0
  180. data/lib/ibex/table_simulation/text.rb +31 -0
  181. data/lib/ibex/table_simulation.rb +13 -0
  182. data/lib/ibex/tables.rb +9 -70
  183. data/lib/ibex/version.rb +1 -1
  184. data/lib/ibex/watch/runner.rb +172 -0
  185. data/lib/ibex/watch/source_snapshot.rb +93 -0
  186. data/lib/ibex/watch.rb +11 -0
  187. data/lib/ibex.rb +25 -1
  188. data/schema/automaton-ir-v1.schema.json +401 -0
  189. data/schema/automaton-ir-v2.schema.json +58 -0
  190. data/schema/benchmark-v1.schema.json +212 -0
  191. data/schema/benchmark-v2.schema.json +61 -0
  192. data/schema/cst-v1.json +170 -0
  193. data/schema/error-ux-v1.schema.json +258 -0
  194. data/schema/explain-v1.schema.json +433 -0
  195. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  196. data/schema/generation-manifest-v1.schema.json +115 -0
  197. data/schema/grammar-ir-v1.schema.json +426 -0
  198. data/schema/grammar-ir-v2.schema.json +779 -0
  199. data/schema/lexer-ir-v1.schema.json +215 -0
  200. data/schema/migration-check-v1.schema.json +60 -0
  201. data/schema/performance-comparison-v1.schema.json +395 -0
  202. data/schema/public-performance-comparison-v1.schema.json +506 -0
  203. data/schema/public-performance-profile-v1.schema.json +360 -0
  204. data/schema/runtime-coverage-v1.schema.json +86 -0
  205. data/schema/runtime-event-v1.schema.json +308 -0
  206. data/schema/table-simulation-v1.schema.json +70 -0
  207. data/sig/ibex/artifact_set.rbs +37 -0
  208. data/sig/ibex/cli/ambiguity.rbs +22 -0
  209. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  210. data/sig/ibex/cli/coverage.rbs +53 -0
  211. data/sig/ibex/cli/debug.rbs +28 -0
  212. data/sig/ibex/cli/diagnostics.rbs +38 -0
  213. data/sig/ibex/cli/documentation.rbs +25 -0
  214. data/sig/ibex/cli/error_messages.rbs +57 -0
  215. data/sig/ibex/cli/explain.rbs +25 -0
  216. data/sig/ibex/cli/formatting.rbs +103 -0
  217. data/sig/ibex/cli/generation_artifacts.rbs +50 -0
  218. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  219. data/sig/ibex/cli/grammar_tests.rbs +36 -0
  220. data/sig/ibex/cli/ir_tools.rbs +51 -0
  221. data/sig/ibex/cli/lsp.rbs +14 -0
  222. data/sig/ibex/cli/outputs.rbs +17 -0
  223. data/sig/ibex/cli/racc_migration.rbs +30 -0
  224. data/sig/ibex/cli/samples.rbs +30 -0
  225. data/sig/ibex/cli/watch.rbs +43 -0
  226. data/sig/ibex/cli.rbs +104 -5
  227. data/sig/ibex/codegen/action_locations.rbs +45 -0
  228. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  229. data/sig/ibex/codegen/action_source.rbs +50 -0
  230. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  231. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  232. data/sig/ibex/codegen/documentation.rbs +50 -0
  233. data/sig/ibex/codegen/explain.rbs +85 -0
  234. data/sig/ibex/codegen/generated_action_abi.rbs +101 -0
  235. data/sig/ibex/codegen/html.rbs +18 -2
  236. data/sig/ibex/codegen/mermaid.rbs +16 -0
  237. data/sig/ibex/codegen/railroad.rbs +82 -0
  238. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  239. data/sig/ibex/codegen/rbs.rbs +84 -4
  240. data/sig/ibex/codegen/report.rbs +8 -0
  241. data/sig/ibex/codegen/ruby.rbs +97 -23
  242. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  243. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  244. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  245. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  246. data/sig/ibex/codegen/ruby_syntax.rbs +25 -0
  247. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  248. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  249. data/sig/ibex/coverage/collector.rbs +76 -0
  250. data/sig/ibex/coverage/event_stream.rbs +42 -0
  251. data/sig/ibex/coverage/report.rbs +100 -0
  252. data/sig/ibex/coverage/runtime_event_validator.rbs +68 -0
  253. data/sig/ibex/coverage.rbs +7 -0
  254. data/sig/ibex/error_messages/parser.rbs +58 -0
  255. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  256. data/sig/ibex/error_messages/renderer.rbs +23 -0
  257. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  258. data/sig/ibex/error_messages/update.rbs +43 -0
  259. data/sig/ibex/error_messages.rbs +85 -0
  260. data/sig/ibex/frontend/ast.rbs +208 -19
  261. data/sig/ibex/frontend/bootstrap_parser.rbs +11 -0
  262. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  263. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  264. data/sig/ibex/frontend/dsl.rbs +33 -4
  265. data/sig/ibex/frontend/formatter.rbs +135 -0
  266. data/sig/ibex/frontend/generated_parser.rbs +218 -68
  267. data/sig/ibex/frontend/generated_parser_base.rbs +61 -10
  268. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  269. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  270. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  271. data/sig/ibex/frontend/generation.rbs +6 -0
  272. data/sig/ibex/frontend/lexer.rbs +49 -2
  273. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  274. data/sig/ibex/frontend/parser/declarations.rbs +54 -0
  275. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  276. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  277. data/sig/ibex/frontend/parser.rbs +66 -0
  278. data/sig/ibex/frontend/regenerator.rbs +11 -0
  279. data/sig/ibex/frontend/resolution.rbs +33 -0
  280. data/sig/ibex/frontend/resolver.rbs +91 -0
  281. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  282. data/sig/ibex/frontend/source_cursor.rbs +36 -3
  283. data/sig/ibex/frontend/source_document.rbs +119 -0
  284. data/sig/ibex/frontend/source_loader.rbs +66 -0
  285. data/sig/ibex/frontend/source_span.rbs +53 -0
  286. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  287. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  288. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +73 -5
  289. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  290. data/sig/ibex/frontend/token_adapter.rbs +5 -2
  291. data/sig/ibex/frontend.rbs +1 -1
  292. data/sig/ibex/generation_input.rbs +37 -0
  293. data/sig/ibex/generation_manifest.rbs +67 -0
  294. data/sig/ibex/generation_transaction.rbs +82 -0
  295. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  296. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  297. data/sig/ibex/grammar_tests.rbs +93 -0
  298. data/sig/ibex/ir/automaton_ir.rbs +8 -2
  299. data/sig/ibex/ir/grammar_ir.rbs +75 -15
  300. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  301. data/sig/ibex/ir/migration.rbs +34 -0
  302. data/sig/ibex/ir/serialize.rbs +23 -6
  303. data/sig/ibex/ir/validator/automaton.rbs +109 -0
  304. data/sig/ibex/ir/validator/base.rbs +65 -0
  305. data/sig/ibex/ir/validator/grammar.rbs +184 -0
  306. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  307. data/sig/ibex/ir/validator.rbs +16 -0
  308. data/sig/ibex/ir.rbs +38 -4
  309. data/sig/ibex/lalr/build_metrics.rbs +20 -0
  310. data/sig/ibex/lalr/builder.rbs +95 -15
  311. data/sig/ibex/lalr/conflict_search.rbs +7 -3
  312. data/sig/ibex/lalr/counterexample.rbs +5 -2
  313. data/sig/ibex/lalr/direct_lookaheads.rbs +86 -0
  314. data/sig/ibex/lalr/ielr_partition.rbs +59 -0
  315. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  316. data/sig/ibex/lalr.rbs +6 -0
  317. data/sig/ibex/location.rbs +67 -0
  318. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  319. data/sig/ibex/lsp/document_store.rbs +94 -0
  320. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  321. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  322. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  323. data/sig/ibex/lsp/navigation_handlers.rbs +30 -0
  324. data/sig/ibex/lsp/position_codec.rbs +40 -0
  325. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  326. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  327. data/sig/ibex/lsp/request_support.rbs +40 -0
  328. data/sig/ibex/lsp/server.rbs +55 -0
  329. data/sig/ibex/lsp/symbol_index.rbs +78 -0
  330. data/sig/ibex/lsp/symbol_index_builder.rbs +87 -0
  331. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  332. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  333. data/sig/ibex/lsp/symbol_occurrence.rbs +25 -0
  334. data/sig/ibex/lsp/transport.rbs +35 -0
  335. data/sig/ibex/lsp/workspace.rbs +41 -0
  336. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  337. data/sig/ibex/lsp.rbs +7 -0
  338. data/sig/ibex/normalize/declarations.rbs +31 -0
  339. data/sig/ibex/normalize/diagnostics.rbs +9 -0
  340. data/sig/ibex/normalize/expander.rbs +20 -14
  341. data/sig/ibex/normalize/expression.rbs +10 -10
  342. data/sig/ibex/normalize/inline_expansion.rbs +120 -0
  343. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  344. data/sig/ibex/normalize/lexer.rbs +37 -0
  345. data/sig/ibex/normalize/named_references.rbs +20 -0
  346. data/sig/ibex/normalize/nodes.rbs +14 -0
  347. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +32 -0
  348. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  349. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  350. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  351. data/sig/ibex/normalize/parameters.rbs +46 -0
  352. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  353. data/sig/ibex/normalize.rbs +123 -18
  354. data/sig/ibex/racc_migration/checker.rbs +36 -0
  355. data/sig/ibex/racc_migration/harness.rbs +16 -0
  356. data/sig/ibex/racc_migration/report.rbs +53 -0
  357. data/sig/ibex/racc_migration.rbs +8 -0
  358. data/sig/ibex/rake_task.rbs +51 -0
  359. data/sig/ibex/samples.rbs +51 -0
  360. data/sig/ibex/table_simulation/result.rbs +32 -0
  361. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  362. data/sig/ibex/table_simulation/step.rbs +40 -0
  363. data/sig/ibex/table_simulation/text.rbs +14 -0
  364. data/sig/ibex/table_simulation.rbs +7 -0
  365. data/sig/ibex/tables.rbs +0 -26
  366. data/sig/ibex/watch/runner.rbs +48 -0
  367. data/sig/ibex/watch/source_snapshot.rbs +42 -0
  368. data/sig/ibex/watch.rbs +7 -0
  369. data/sig/ibex.rbs +2 -0
  370. metadata +301 -16
  371. data/.rubocop.yml +0 -43
  372. data/CHANGELOG.md +0 -30
  373. data/Rakefile +0 -25
  374. data/Steepfile +0 -10
  375. data/docs/compat-notes.md +0 -37
  376. data/docs/lexer-coverage.md +0 -14
  377. data/docs/phase10-extensions.md +0 -27
  378. data/gemfiles/Gemfile +0 -7
  379. data/gemfiles/Gemfile.lock +0 -98
  380. data/lib/ibex/frontend/grammar.y +0 -156
  381. data/lib/ibex/runtime/parser.rb +0 -360
  382. data/lib/ibex/runtime.rb +0 -8
  383. data/sig/ibex/runtime/parser.rbs +0 -167
  384. 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,407 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # Deterministically formats grammar trivia and rejects any semantic change.
6
+ # rubocop:disable Metrics/ClassLength -- token layout policy stays together so adjacency rules remain auditable.
7
+ class Formatter
8
+ DECLARATION_STARTS = %i[
9
+ PRAGMA INCLUDE IMPORT TOKEN PRECHIGH PRECLOW OPTIONS EXPECT EXPECT_RR START RECOVER ON_ERROR_REDUCE TEST LEXER
10
+ CONVERT DISPLAY TYPE PARAM PRINTER RULE
11
+ ].freeze #: Array[Symbol]
12
+ ASSOCIATIONS = %i[LEFT RIGHT NONASSOC].freeze #: Array[Symbol]
13
+ CALLABLES = %i[LHS PARAMETERIZED_REFERENCE SEPARATED_LIST SEPARATED_NONEMPTY_LIST].freeze #: Array[Symbol]
14
+ SUFFIXES = %w[? * +].freeze #: Array[String]
15
+ CLOSERS = %w[) , ;].freeze #: Array[String]
16
+ ADJACENT_AFTER = ["::", "("].freeze #: Array[String]
17
+
18
+ # @rbs!
19
+ # type formatter_element = {
20
+ # segment: Segment,
21
+ # external: external_token?,
22
+ # role: Symbol?,
23
+ # gap: Array[Segment]
24
+ # }
25
+
26
+ # @rbs (String source, ?file: String, ?mode: Symbol) -> String
27
+ def self.format(source, file: "(grammar)", mode: :default)
28
+ new(mode: mode).format(source, file: file)
29
+ end
30
+
31
+ # @rbs (?mode: Symbol) -> void
32
+ def initialize(mode: :default)
33
+ raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)
34
+
35
+ @mode = mode
36
+ end
37
+
38
+ # @rbs (String source, ?file: String) -> String
39
+ def format(source, file: "(grammar)")
40
+ document = Parser.new(source, file: file, mode: @mode).parse_source_document
41
+ formatted = render(document)
42
+ reparsed = Parser.new(formatted, file: file, mode: @mode).parse_source_document
43
+ unless same_semantic_projection?(document.ast, reparsed.ast)
44
+ raise Ibex::Error, "#{file}: formatting would change grammar semantics"
45
+ end
46
+
47
+ formatted
48
+ rescue Ibex::Error
49
+ raise
50
+ rescue StandardError => e
51
+ raise Ibex::Error, "#{file}: formatting failed: #{e.message}"
52
+ end
53
+
54
+ private
55
+
56
+ # @rbs (SourceDocument document) -> String
57
+ def render(document)
58
+ @default_newline = document.source.match(/\r\n|\n/)&.[](0) || "\n"
59
+ elements = build_elements(document)
60
+ output = +""
61
+ previous = nil #: formatter_element?
62
+ elements.each do |current|
63
+ output << separator(previous, current)
64
+ output << current.fetch(:segment).text
65
+ previous = current
66
+ end
67
+ output
68
+ end
69
+
70
+ # @rbs (SourceDocument document) -> Array[formatter_element]
71
+ def build_elements(document)
72
+ external_types = external_types(document.tokens)
73
+ pending = [] #: Array[Segment]
74
+ elements = document.cst.each_with_object([]) do |segment, result|
75
+ if %i[whitespace newline].include?(segment.kind)
76
+ pending << segment
77
+ next
78
+ end
79
+
80
+ result << {
81
+ segment: segment,
82
+ external: segment.token_index && external_types[segment.token_index],
83
+ role: nil,
84
+ gap: pending
85
+ }
86
+ pending = []
87
+ end
88
+ annotate_roles(elements)
89
+ elements
90
+ end
91
+
92
+ # @rbs (Array[Token] tokens) -> Hash[Integer, external_token]
93
+ def external_types(tokens)
94
+ adapter = TokenAdapter.new(tokens, extended: @mode == :extended)
95
+ types = {} #: Hash[Integer, external_token]
96
+ index = 0
97
+ while (classified = adapter.next_token)
98
+ types[index] = classified.fetch(0)
99
+ index += 1
100
+ end
101
+ types
102
+ end
103
+
104
+ # @rbs (Array[formatter_element] elements) -> void
105
+ def annotate_roles(elements)
106
+ state = {
107
+ section: :declarations, precedence_closer: nil, conversion: false,
108
+ conversion_name: true, depth: 0, rule_colon: false
109
+ } #: Hash[Symbol, untyped]
110
+ elements.each do |element|
111
+ external = element.fetch(:external)
112
+ next unless external
113
+
114
+ element[:role] = token_role(external, state)
115
+ advance_role_state(external, element.fetch(:role), state)
116
+ end
117
+ annotate_comment_indentation(elements)
118
+ end
119
+
120
+ # @rbs (Array[formatter_element] elements) -> void
121
+ def annotate_comment_indentation(elements)
122
+ following_role = nil #: Symbol?
123
+ elements.reverse_each do |element|
124
+ segment = element.fetch(:segment)
125
+ if comment?(segment)
126
+ element[:role] = :rule_comment if following_role == :rule_start
127
+ element[:role] = :alternative_comment if following_role == :alternative
128
+ elsif element.fetch(:external)
129
+ following_role = element.fetch(:role)
130
+ end
131
+ end
132
+ end
133
+
134
+ # @rbs (external_token external, Hash[Symbol, untyped] state) -> Symbol?
135
+ def token_role(external, state)
136
+ document_role = document_token_role(external, state)
137
+ return document_role if document_role
138
+
139
+ rule_token_role(external, state)
140
+ end
141
+
142
+ # @rbs (external_token external, Hash[Symbol, untyped] state) -> Symbol?
143
+ def document_token_role(external, state)
144
+ return :user_code if external == :USER_CODE
145
+ return precedence_role(external, state) if %i[PRECHIGH PRECLOW].include?(external)
146
+ return :precedence_level if ASSOCIATIONS.include?(external) && state[:precedence_closer]
147
+ return section_end_role(state) if external == :END
148
+ return :conversion_entry if state.fetch(:conversion) && state.fetch(:conversion_name)
149
+ return :rule_keyword if external == :RULE
150
+ return :declaration if DECLARATION_STARTS.include?(external)
151
+
152
+ nil
153
+ end
154
+
155
+ # @rbs (Hash[Symbol, untyped] state) -> Symbol?
156
+ def section_end_role(state)
157
+ return :conversion_end if state.fetch(:conversion)
158
+ return :grammar_end if state.fetch(:section) == :rules
159
+
160
+ nil
161
+ end
162
+
163
+ # @rbs (external_token external, Hash[Symbol, untyped] state) -> Symbol?
164
+ def rule_token_role(external, state)
165
+ return :rule_start if external == :INLINE && state.fetch(:section) == :rules
166
+ return :rule_start if external == :LHS
167
+ return :alternative if external == "|" && state.fetch(:section) == :rules && state.fetch(:depth).zero?
168
+
169
+ if external == ":"
170
+ return :rule_colon if state.fetch(:rule_colon) && state.fetch(:depth).zero?
171
+
172
+ return :named_reference
173
+ end
174
+
175
+ nil
176
+ end
177
+
178
+ # @rbs (external_token external, Hash[Symbol, untyped] state) -> Symbol
179
+ def precedence_role(external, state)
180
+ return :precedence_end if state[:precedence_closer] == external
181
+
182
+ :declaration
183
+ end
184
+
185
+ # @rbs (external_token external, Symbol? role, Hash[Symbol, untyped] state) -> void
186
+ def advance_role_state(external, role, state)
187
+ advance_precedence_state(external, role, state)
188
+ advance_conversion_state(external, role, state)
189
+ advance_rule_state(external, role, state)
190
+ end
191
+
192
+ # @rbs (external_token external, Symbol? role, Hash[Symbol, untyped] state) -> void
193
+ def advance_precedence_state(external, role, state)
194
+ if role == :declaration && %i[PRECHIGH PRECLOW].include?(external)
195
+ state[:precedence_closer] = external == :PRECHIGH ? :PRECLOW : :PRECHIGH
196
+ elsif role == :precedence_end
197
+ state[:precedence_closer] = nil
198
+ end
199
+ end
200
+
201
+ # @rbs (external_token external, Symbol? role, Hash[Symbol, untyped] state) -> void
202
+ def advance_conversion_state(external, role, state)
203
+ if external == :CONVERT
204
+ state[:conversion] = true
205
+ state[:conversion_name] = true
206
+ elsif role == :conversion_end
207
+ state[:conversion] = false
208
+ elsif state.fetch(:conversion) && role == :conversion_entry
209
+ state[:conversion_name] = false
210
+ elsif state.fetch(:conversion) && !state.fetch(:conversion_name)
211
+ state[:conversion_name] = true
212
+ end
213
+ end
214
+
215
+ # @rbs (external_token external, Symbol? role, Hash[Symbol, untyped] state) -> void
216
+ def advance_rule_state(external, role, state)
217
+ state[:section] = :rules if external == :RULE
218
+ state[:rule_colon] = true if external == :LHS
219
+ state[:rule_colon] = false if role == :rule_colon
220
+ state[:depth] += 1 if external == "("
221
+ state[:depth] -= 1 if external == ")"
222
+ end
223
+
224
+ # @rbs (formatter_element? previous, formatter_element current) -> String
225
+ def separator(previous, current)
226
+ gap = current.fetch(:gap)
227
+ return leading_separator(current, gap) unless previous
228
+
229
+ previous_segment = previous.fetch(:segment)
230
+ current_segment = current.fetch(:segment)
231
+ return "" if direct_opaque_pair?(previous_segment, current_segment)
232
+ return trailing_separator(previous, gap) if current_segment.kind == :eof
233
+ return comment_separator(previous, current, gap) if comment?(previous_segment) || comment?(current_segment)
234
+
235
+ kind = separator_kind(previous, current)
236
+ render_separator(kind, gap, indentation(current))
237
+ end
238
+
239
+ # @rbs (formatter_element current, Array[Segment] gap) -> String
240
+ def leading_separator(current, gap)
241
+ return "" unless newline_in?(gap)
242
+
243
+ render_separator(:line, gap, indentation(current))
244
+ end
245
+
246
+ # @rbs (formatter_element previous, Array[Segment] gap) -> String
247
+ def trailing_separator(previous, gap)
248
+ segment = previous.fetch(:segment)
249
+ return "" if segment.kind == :user_code_body
250
+ return render_separator(:line, gap, 0) if previous.fetch(:role) == :grammar_end
251
+
252
+ render_separator(gap.any? { |item| item.kind == :newline } ? :line : :none, gap, 0)
253
+ end
254
+
255
+ # @rbs (Segment previous, Segment current) -> bool
256
+ def direct_opaque_pair?(previous, current)
257
+ (previous.kind == :user_code_marker && current.kind == :user_code_body) ||
258
+ (previous.kind == :user_code_body && current.kind == :user_code_marker)
259
+ end
260
+
261
+ # @rbs (Segment segment) -> bool
262
+ def comment?(segment)
263
+ %i[line_comment block_comment].include?(segment.kind)
264
+ end
265
+
266
+ # @rbs (formatter_element previous, formatter_element current, Array[Segment] gap) -> String
267
+ def comment_separator(previous, current, gap)
268
+ previous_segment = previous.fetch(:segment)
269
+ current_segment = current.fetch(:segment)
270
+ if text_ends_with_newline?(previous_segment.text)
271
+ newlines = gap.select { |segment| segment.kind == :newline }.map(&:text)
272
+ return newlines.join + (" " * indentation(current))
273
+ end
274
+ if previous_segment.kind == :line_comment || newline_in?(gap)
275
+ return render_separator(:line, gap, indentation(current))
276
+ end
277
+ return " " if current_segment.kind == :line_comment || previous_segment.kind == :block_comment ||
278
+ current_segment.kind == :block_comment
279
+
280
+ render_separator(:space, gap, indentation(current))
281
+ end
282
+
283
+ # @rbs (formatter_element previous, formatter_element current) -> Symbol
284
+ def separator_kind(previous, current)
285
+ previous_external = previous.fetch(:external)
286
+ current_external = current.fetch(:external)
287
+ previous_role = previous.fetch(:role)
288
+ current_role = current.fetch(:role)
289
+
290
+ return :line if line_boundary?(previous_role, current_role, previous, current)
291
+ return :none if adjacent_tokens?(previous_external, current_external, previous_role, current_role)
292
+
293
+ :space
294
+ end
295
+
296
+ # @rbs (external_token? previous_external, external_token? current_external,
297
+ # Symbol? previous_role, Symbol? current_role) -> bool
298
+ def adjacent_tokens?(previous_external, current_external, previous_role, current_role)
299
+ return true if current_role == :named_reference || previous_role == :named_reference
300
+ return true if current_external == "::"
301
+ return true if CLOSERS.include?(current_external) || SUFFIXES.include?(current_external)
302
+ return true if ADJACENT_AFTER.include?(previous_external)
303
+
304
+ current_external == "(" && CALLABLES.include?(previous_external)
305
+ end
306
+
307
+ # @rbs (Symbol? previous_role, Symbol? current_role, formatter_element previous,
308
+ # formatter_element current) -> bool
309
+ def line_boundary?(previous_role, current_role, previous, current)
310
+ return false if current_role == :rule_start && previous.fetch(:external) == :INLINE
311
+
312
+ return true if %i[declaration rule_keyword precedence_level precedence_end conversion_entry
313
+ conversion_end rule_start alternative grammar_end user_code].include?(current_role)
314
+ return true if previous_role == :rule_keyword
315
+ return false unless current.fetch(:segment).kind == :user_code_marker
316
+
317
+ previous.fetch(:segment).kind != :user_code_body
318
+ end
319
+
320
+ # @rbs (formatter_element element) -> Integer
321
+ def indentation(element)
322
+ case element.fetch(:role)
323
+ when :rule_start, :precedence_level, :conversion_entry, :rule_comment then 2
324
+ when :alternative, :alternative_comment then 4
325
+ else 0
326
+ end
327
+ end
328
+
329
+ # @rbs (Symbol kind, Array[Segment] gap, Integer indentation) -> String
330
+ def render_separator(kind, gap, indentation)
331
+ return "" if kind == :none
332
+ return " " if kind == :space
333
+
334
+ newlines = gap.select { |segment| segment.kind == :newline }.map(&:text)
335
+ newlines = [@default_newline] if newlines.empty?
336
+ newlines.join + (" " * indentation)
337
+ end
338
+
339
+ # @rbs (Array[Segment] gap) -> bool
340
+ def newline_in?(gap)
341
+ gap.any? { |segment| segment.kind == :newline }
342
+ end
343
+
344
+ # @rbs (String text) -> bool
345
+ def text_ends_with_newline?(text)
346
+ text.end_with?("\n")
347
+ end
348
+
349
+ # @rbs (untyped left, untyped right) -> bool
350
+ def same_semantic_projection?(left, right)
351
+ pending = [[left, right]] #: Array[[untyped, untyped]]
352
+ until pending.empty?
353
+ pair = pending.pop
354
+ next unless pair
355
+
356
+ left_value, right_value = pair
357
+ next if left_value.equal?(right_value)
358
+ return false unless comparable_semantic_values?(left_value, right_value)
359
+
360
+ append_semantic_children(pending, left_value, right_value)
361
+ end
362
+ true
363
+ end
364
+
365
+ # @rbs (untyped left, untyped right) -> bool
366
+ def comparable_semantic_values?(left, right)
367
+ return left.instance_of?(right.class) && left.members == right.members if left.is_a?(Struct)
368
+ return right.is_a?(Array) && left.length == right.length if left.is_a?(Array)
369
+ return comparable_semantic_hashes?(left, right) if left.is_a?(Hash)
370
+
371
+ left == right
372
+ end
373
+
374
+ # @rbs (Hash[untyped, untyped] left, untyped right) -> bool
375
+ def comparable_semantic_hashes?(left, right)
376
+ return false unless right.is_a?(Hash)
377
+
378
+ left_keys = semantic_keys(left)
379
+ right_keys = semantic_keys(right)
380
+ left_keys.length == right_keys.length && left_keys.all? { |key| right_keys.include?(key) }
381
+ end
382
+
383
+ # @rbs (Array[[untyped, untyped]] pending, untyped left, untyped right) -> void
384
+ def append_semantic_children(pending, left, right)
385
+ case left
386
+ when Struct
387
+ semantic_members(left).reverse_each { |member| pending << [left[member], right[member]] }
388
+ when Array
389
+ left.each_index.reverse_each { |index| pending << [left[index], right[index]] }
390
+ when Hash
391
+ semantic_keys(left).reverse_each { |key| pending << [left[key], right[key]] }
392
+ end
393
+ end
394
+
395
+ # @rbs (Struct[untyped] value) -> Array[Symbol]
396
+ def semantic_members(value)
397
+ value.members.reject { |member| %i[loc span].include?(member) }
398
+ end
399
+
400
+ # @rbs (Hash[untyped, untyped] value) -> Array[untyped]
401
+ def semantic_keys(value)
402
+ value.keys.reject { |key| %i[loc span].include?(key) }
403
+ end
404
+ end
405
+ # rubocop:enable Metrics/ClassLength
406
+ end
407
+ end