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
@@ -27,6 +27,7 @@ module Ibex
27
27
  :type, #: Symbol
28
28
  :value, #: token_value
29
29
  :location, #: Location
30
+ :span, #: SourceSpan?
30
31
  keyword_init: true
31
32
  )
32
33
 
@@ -42,31 +43,53 @@ module Ibex
42
43
  attr_reader :source #: String
43
44
  attr_reader :file #: String
44
45
  attr_reader :index #: Integer
46
+ attr_reader :byte_offset #: Integer
45
47
  attr_reader :line #: Integer
46
48
  attr_reader :column #: Integer
47
49
 
48
50
  # @rbs (String source, String file) -> void
49
51
  def initialize(source, file)
50
- @source = source
51
- @file = file
52
+ @source = SourceEncoding.validated_utf8(source, file)
53
+ @file = file.dup.freeze
54
+ @ascii_only = @source.ascii_only?
55
+ @character_length = @ascii_only ? @source.bytesize : @source.length
52
56
  @index = 0
57
+ @byte_offset = 0
53
58
  @line = 1
54
59
  @column = 1
55
60
  end
56
61
 
57
62
  # @rbs () -> bool
58
63
  def eof?
59
- @index >= @source.length
64
+ @index >= @character_length
60
65
  end
61
66
 
62
67
  # @rbs (?Integer offset) -> String?
63
68
  def peek(offset = 0)
64
- @source[@index + offset]
69
+ character_index = @index + offset
70
+ if @ascii_only
71
+ return if character_index >= @character_length
72
+
73
+ return @source.byteslice(character_index, 1)
74
+ end
75
+
76
+ character_index += @character_length if character_index.negative?
77
+ return if character_index.negative? || character_index >= @character_length
78
+
79
+ start_byte = if character_index == @index
80
+ @byte_offset
81
+ elsif character_index == @index + 1
82
+ @byte_offset + utf8_character_width(@byte_offset)
83
+ else
84
+ byte_offset_for_character(character_index)
85
+ end
86
+ @source.byteslice(start_byte, utf8_character_width(start_byte))
65
87
  end
66
88
 
67
89
  # @rbs () -> String
68
90
  def rest
69
- @source[@index..] || ""
91
+ @source.byteslice(@byte_offset, @source.bytesize - @byte_offset) ||
92
+ String.new(encoding: Encoding::UTF_8)
70
93
  end
71
94
 
72
95
  # @rbs () -> Location
@@ -74,14 +97,34 @@ module Ibex
74
97
  Location.new(file: @file, line: @line, column: @column)
75
98
  end
76
99
 
100
+ # @rbs () -> SourcePosition
101
+ def position
102
+ SourcePosition.new(byte_offset: @byte_offset, line: @line, column: @column)
103
+ end
104
+
105
+ # @rbs (SourcePosition start) -> SourceSpan
106
+ def span_from(start)
107
+ SourceSpan.new(file: @file, start: start, finish: position)
108
+ end
109
+
77
110
  # @rbs (?Integer count) -> void
78
111
  def advance(count = 1)
112
+ return advance_utf8(count) unless @ascii_only
113
+
114
+ advance_ascii(count)
115
+ end
116
+
117
+ private
118
+
119
+ # @rbs (Integer count) -> void
120
+ def advance_ascii(count)
79
121
  count.times do
80
- character = @source[@index]
81
- break unless character
122
+ break if @index >= @character_length
82
123
 
124
+ newline = @source.getbyte(@index) == 10
83
125
  @index += 1
84
- if character == "\n"
126
+ @byte_offset = @index
127
+ if newline
85
128
  @line += 1
86
129
  @column = 1
87
130
  else
@@ -89,6 +132,87 @@ module Ibex
89
132
  end
90
133
  end
91
134
  end
135
+
136
+ # @rbs (Integer count) -> void
137
+ def advance_utf8(count)
138
+ count.times do
139
+ break if @index >= @character_length
140
+
141
+ leading_byte = @source.getbyte(@byte_offset)
142
+ raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte
143
+
144
+ @index += 1
145
+ @byte_offset += if leading_byte < 0x80
146
+ 1
147
+ elsif leading_byte < 0xE0
148
+ 2
149
+ elsif leading_byte < 0xF0
150
+ 3
151
+ else
152
+ 4
153
+ end
154
+ if leading_byte == 10
155
+ @line += 1
156
+ @column = 1
157
+ else
158
+ @column += 1
159
+ end
160
+ end
161
+ end
162
+
163
+ # @rbs (Integer character_index) -> Integer
164
+ def byte_offset_for_character(character_index)
165
+ start_distance = character_index
166
+ current_distance = (character_index - @index).abs
167
+ finish_distance = @character_length - character_index
168
+
169
+ if current_distance <= start_distance && current_distance <= finish_distance
170
+ walk_to_character(@byte_offset, @index, character_index)
171
+ elsif start_distance <= finish_distance
172
+ walk_to_character(0, 0, character_index)
173
+ else
174
+ walk_to_character(@source.bytesize, @character_length, character_index)
175
+ end
176
+ end
177
+
178
+ # @rbs (Integer byte_offset, Integer from_index, Integer to_index) -> Integer
179
+ def walk_to_character(byte_offset, from_index, to_index)
180
+ while from_index < to_index
181
+ byte_offset += utf8_character_width(byte_offset)
182
+ from_index += 1
183
+ end
184
+ while from_index > to_index
185
+ byte_offset = previous_character_byte_offset(byte_offset)
186
+ from_index -= 1
187
+ end
188
+ byte_offset
189
+ end
190
+
191
+ # @rbs (Integer byte_offset) -> Integer
192
+ def previous_character_byte_offset(byte_offset)
193
+ byte_offset -= 1
194
+ while (byte = @source.getbyte(byte_offset)) && continuation_byte?(byte)
195
+ byte_offset -= 1
196
+ end
197
+ byte_offset
198
+ end
199
+
200
+ # @rbs (Integer byte_offset) -> Integer
201
+ def utf8_character_width(byte_offset)
202
+ leading_byte = @source.getbyte(byte_offset)
203
+ raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte
204
+
205
+ return 1 if leading_byte < 0x80
206
+ return 2 if leading_byte < 0xE0
207
+ return 3 if leading_byte < 0xF0
208
+
209
+ 4
210
+ end
211
+
212
+ # @rbs (Integer byte) -> bool
213
+ def continuation_byte?(byte)
214
+ byte >= 0x80 && byte < 0xC0
215
+ end
92
216
  end
93
217
  end
94
218
  end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # An immutable lexical unit or trivia slice in a concrete syntax tree.
6
+ class Segment
7
+ TRIVIA_KINDS = %i[whitespace newline line_comment block_comment].freeze #: Array[Symbol]
8
+ OPAQUE_KINDS = %i[action user_code_body].freeze #: Array[Symbol]
9
+
10
+ attr_reader :kind #: Symbol
11
+ attr_reader :span #: SourceSpan
12
+ attr_reader :text #: String
13
+ attr_reader :token_type #: Symbol?
14
+ attr_reader :token_index #: Integer?
15
+
16
+ # @rbs (kind: Symbol, span: SourceSpan, text: String, ?token_type: Symbol?,
17
+ # ?token_index: Integer?) -> void
18
+ def initialize(kind:, span:, text:, token_type: nil, token_index: nil)
19
+ raise ArgumentError, "segment text does not match its span" unless text.bytesize == span.length
20
+
21
+ @kind = kind
22
+ @span = span
23
+ @text = text.dup.freeze
24
+ @token_type = token_type
25
+ @token_index = token_index
26
+ freeze
27
+ end
28
+
29
+ # @rbs () -> bool
30
+ def trivia?
31
+ TRIVIA_KINDS.include?(kind)
32
+ end
33
+
34
+ # @rbs () -> bool
35
+ def opaque?
36
+ OPAQUE_KINDS.include?(kind)
37
+ end
38
+
39
+ # @rbs () -> bool
40
+ def token?
41
+ !token_index.nil?
42
+ end
43
+
44
+ # @rbs () -> String
45
+ def render
46
+ text
47
+ end
48
+ end
49
+
50
+ module CST
51
+ # The immutable lexical root of a grammar source document.
52
+ class Document
53
+ # @rbs! include Enumerable[Segment]
54
+ # @rbs skip
55
+ include Enumerable
56
+
57
+ attr_reader :segments #: Array[Segment]
58
+
59
+ # @rbs (Array[Segment] segments) -> void
60
+ def initialize(segments)
61
+ validate_contiguous(segments)
62
+ @segments = segments.dup.freeze
63
+ freeze
64
+ end
65
+
66
+ # @rbs!
67
+ # def each: () -> Enumerator[Segment, Document]
68
+ # | () { (Segment) -> void } -> Document
69
+ # @rbs skip
70
+ def each(&block)
71
+ return enum_for(:each) unless block
72
+
73
+ segments.each(&block)
74
+ self
75
+ end
76
+
77
+ # @rbs () -> String
78
+ def render
79
+ segments.map(&:render).join
80
+ end
81
+
82
+ private
83
+
84
+ # @rbs (Array[Segment] segments) -> void
85
+ def validate_contiguous(segments)
86
+ segments.each_cons(2) do |left, right|
87
+ next unless left && right
88
+ next if left.span.end_byte == right.span.start_byte
89
+
90
+ raise ArgumentError, "concrete syntax tree segments must be contiguous"
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # Lossless grammar source plus its semantic parse and lexical concrete syntax tree.
97
+ class SourceDocument
98
+ attr_reader :source #: String
99
+ attr_reader :file #: String
100
+ attr_reader :tokens #: Array[Token]
101
+ attr_reader :cst #: CST::Document
102
+ attr_reader :ast #: AST::Root | AST::Fragment | nil
103
+
104
+ # @rbs (source: String, file: String, tokens: Array[Token], cst: CST::Document,
105
+ # ?ast: AST::Root | AST::Fragment | nil) -> void
106
+ def initialize(source:, file:, tokens:, cst:, ast: nil)
107
+ @source = SourceEncoding.validated_utf8(source, file)
108
+ @file = file.dup.freeze
109
+ @tokens = tokens.dup.freeze
110
+ @cst = cst
111
+ @ast = ast
112
+ @line_starts = build_line_starts.freeze #: Array[Integer]
113
+ validate_render
114
+ freeze
115
+ end
116
+
117
+ # @rbs () -> String
118
+ def render
119
+ cst.render
120
+ end
121
+
122
+ # @rbs (SourceSpan span) -> String
123
+ def slice(span)
124
+ validate_span(span)
125
+ source.byteslice(span.start_byte, span.length) || ""
126
+ end
127
+
128
+ # @rbs (Integer byte_offset) -> SourcePosition
129
+ def position_at(byte_offset)
130
+ validate_byte_offset(byte_offset)
131
+ prefix = source.byteslice(0, byte_offset) || ""
132
+ raise ArgumentError, "byte offset must be on a UTF-8 character boundary" unless prefix.valid_encoding?
133
+
134
+ line_index = (@line_starts.bsearch_index { |start| start > byte_offset } || @line_starts.length) - 1
135
+ line_start = @line_starts.fetch(line_index)
136
+ line_prefix = source.byteslice(line_start, byte_offset - line_start) || ""
137
+ SourcePosition.new(byte_offset: byte_offset, line: line_index + 1, column: line_prefix.length + 1)
138
+ end
139
+
140
+ # @rbs (Integer line, Integer column) -> Integer
141
+ def byte_offset_at(line, column)
142
+ raise ArgumentError, "line and column must be positive" unless line.positive? && column.positive?
143
+
144
+ line_start = @line_starts[line - 1]
145
+ raise ArgumentError, "line is outside the source" unless line_start
146
+
147
+ line_end = @line_starts[line] || source.bytesize
148
+ line_text = source.byteslice(line_start, line_end - line_start) || ""
149
+ line_text = line_text.delete_suffix("\n")
150
+ character_count = column - 1
151
+ raise ArgumentError, "column is outside the source line" if character_count > line_text.length
152
+
153
+ line_start + line_text.each_char.take(character_count).join.bytesize
154
+ end
155
+
156
+ # @rbs (Integer start_byte, Integer end_byte) -> SourceSpan
157
+ def span(start_byte, end_byte)
158
+ SourceSpan.new(file: file, start: position_at(start_byte), finish: position_at(end_byte))
159
+ end
160
+
161
+ # @rbs (Segment segment) -> Token?
162
+ def token_for(segment)
163
+ index = segment.token_index
164
+ index && tokens[index]
165
+ end
166
+
167
+ # @rbs (AST::Root | AST::Fragment ast) -> SourceDocument
168
+ def with_ast(ast)
169
+ self.class.new(source: source, file: file, tokens: tokens, cst: cst, ast: ast)
170
+ end
171
+
172
+ private
173
+
174
+ # @rbs () -> Array[Integer]
175
+ def build_line_starts
176
+ starts = [0]
177
+ offset = 0
178
+ binary_source = source.b
179
+ while (newline = binary_source.index("\n", offset))
180
+ offset = newline + 1
181
+ starts << offset
182
+ end
183
+ starts
184
+ end
185
+
186
+ # @rbs () -> void
187
+ def validate_render
188
+ return if full_source_coverage? && segment_slices_match? && render == source
189
+
190
+ raise ArgumentError, "concrete syntax tree does not reproduce its source"
191
+ end
192
+
193
+ # @rbs () -> bool
194
+ def full_source_coverage?
195
+ segments = cst.segments
196
+ return source.empty? if segments.empty?
197
+
198
+ first = segments.first
199
+ last = segments.last
200
+ return false unless first && last
201
+
202
+ first.span.start_byte.zero? && last.span.end_byte == source.bytesize
203
+ end
204
+
205
+ # @rbs () -> bool
206
+ def segment_slices_match?
207
+ cst.segments.all? do |segment|
208
+ segment.span.file == file &&
209
+ source.byteslice(segment.span.start_byte, segment.span.length) == segment.text
210
+ end
211
+ end
212
+
213
+ # @rbs (Integer byte_offset) -> void
214
+ def validate_byte_offset(byte_offset)
215
+ return if byte_offset.between?(0, source.bytesize)
216
+
217
+ raise ArgumentError, "byte offset is outside the source"
218
+ end
219
+
220
+ # @rbs (SourceSpan span) -> void
221
+ def validate_span(span)
222
+ raise ArgumentError, "span belongs to another source" unless span.file == file
223
+
224
+ validate_byte_offset(span.start_byte)
225
+ validate_byte_offset(span.end_byte)
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../generation_input"
4
+
5
+ module Ibex
6
+ module Frontend
7
+ # Resolves canonical grammar paths and reads either open-buffer overlays or disk.
8
+ class SourceLoader
9
+ # @rbs @overlays: Hash[String, String]
10
+ # @rbs @aliases: Hash[String, String]
11
+ # @rbs @read_records: Hash[String, GenerationInput]
12
+ # @rbs @record_reads: bool
13
+ # @rbs @access_paths: Hash[String, Array[String]]
14
+
15
+ # @rbs () -> Array[GenerationInput]
16
+ def read_records
17
+ @read_records.values
18
+ end
19
+
20
+ # Associate the lexical path used by a resolver with its canonical source.
21
+ # @rbs (String canonical, String access_path) -> void
22
+ def record_access(canonical, access_path)
23
+ paths = (@access_paths[canonical] ||= [])
24
+ expanded = File.expand_path(access_path)
25
+ paths << expanded unless paths.include?(expanded)
26
+ @read_records[canonical]&.add_access_path(expanded)
27
+ end
28
+
29
+ # @rbs (?overlays: Hash[String, String], ?record_reads: bool) -> void
30
+ def initialize(overlays: {}, record_reads: false)
31
+ @overlays = {} #: Hash[String, String]
32
+ @aliases = {} #: Hash[String, String]
33
+ @read_records = {} #: Hash[String, GenerationInput]
34
+ @record_reads = record_reads
35
+ @access_paths = {} #: Hash[String, Array[String]]
36
+ overlays.each { |path, source| set_overlay(path, source) }
37
+ end
38
+
39
+ # Canonicalize an existing file, or a missing path explicitly allowed by an open overlay.
40
+ # @rbs (String path, ?base: String, ?allow_missing: bool) -> String
41
+ def canonical_path(path, base: Dir.pwd, allow_missing: false)
42
+ expanded = File.expand_path(path, base)
43
+ aliased = @aliases[expanded]
44
+ return aliased if aliased
45
+
46
+ begin
47
+ File.realpath(expanded)
48
+ rescue Errno::ENOENT
49
+ canonical = canonical_missing_path(expanded)
50
+ return canonical if allow_missing || @overlays.key?(canonical)
51
+
52
+ raise
53
+ end
54
+ end
55
+
56
+ # @rbs (String path) -> String
57
+ def read(path)
58
+ canonical = canonical_path(path, allow_missing: true)
59
+ overlay = @overlays[canonical]
60
+ source = overlay || File.binread(canonical)
61
+ if @record_reads
62
+ record = GenerationInput.new(canonical, source)
63
+ (@access_paths[canonical] || []).each { |access| record.add_access_path(access) }
64
+ @read_records[canonical] = record
65
+ end
66
+
67
+ source
68
+ end
69
+
70
+ # @rbs (String path) -> bool
71
+ def file?(path)
72
+ canonical = canonical_path(path, allow_missing: true)
73
+ @overlays.key?(canonical) || File.file?(canonical)
74
+ rescue SystemCallError
75
+ false
76
+ end
77
+
78
+ # @rbs (String path) -> bool
79
+ def overlay?(path)
80
+ canonical = canonical_path(path, allow_missing: true)
81
+ @overlays.key?(canonical)
82
+ rescue SystemCallError
83
+ false
84
+ end
85
+
86
+ # Install or replace an editor buffer without writing it to disk.
87
+ # @rbs (String path, String source) -> String
88
+ def set_overlay(path, source)
89
+ expanded = File.expand_path(path)
90
+ canonical = canonical_path(expanded, allow_missing: true)
91
+ raise ArgumentError, "overlay path must not be a directory" if File.directory?(canonical)
92
+
93
+ @aliases[expanded] = canonical
94
+ @overlays[canonical] = source.dup.freeze
95
+ canonical
96
+ end
97
+
98
+ # Remove an editor buffer and return its canonical path.
99
+ # @rbs (String path) -> String
100
+ def delete_overlay(path)
101
+ expanded = File.expand_path(path)
102
+ canonical = @aliases.delete(expanded) || canonical_path(expanded, allow_missing: true)
103
+ @aliases.delete_if { |_alias_path, target| target == canonical }
104
+ @overlays.delete(canonical)
105
+ canonical
106
+ end
107
+
108
+ # Read disk while deliberately bypassing an open overlay.
109
+ # @rbs (String path) -> String
110
+ def disk_source(path)
111
+ File.binread(disk_path(path))
112
+ end
113
+
114
+ # @rbs (String path) -> bool
115
+ def disk_file?(path)
116
+ File.file?(disk_path(path))
117
+ rescue SystemCallError
118
+ false
119
+ end
120
+
121
+ # Resolve the current disk target without consulting overlay aliases.
122
+ # @rbs (String path) -> String
123
+ def disk_path(path)
124
+ File.realpath(File.expand_path(path))
125
+ end
126
+
127
+ private
128
+
129
+ # Resolve the nearest existing ancestor so symlink escapes remain visible for new files.
130
+ # @rbs (String expanded) -> String
131
+ def canonical_missing_path(expanded)
132
+ raise Errno::ENOENT, expanded if File.symlink?(expanded)
133
+
134
+ cursor = expanded
135
+ suffix = [] #: Array[String]
136
+ loop do
137
+ parent = File.dirname(cursor)
138
+ raise Errno::ENOENT, expanded if parent == cursor
139
+
140
+ suffix.unshift(File.basename(cursor))
141
+ cursor = parent
142
+ break if File.exist?(cursor) || File.symlink?(cursor)
143
+ end
144
+ raise Errno::ENOTDIR, cursor unless File.directory?(File.realpath(cursor))
145
+
146
+ File.join(File.realpath(cursor), *suffix)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # Normalizes grammar source without changing its bytes.
6
+ module SourceEncoding
7
+ # @rbs (String source, String file) -> String
8
+ def self.validated_utf8(source, file)
9
+ validated = source.dup.force_encoding(Encoding::UTF_8)
10
+ raise Ibex::Error, "#{file}: input must be valid UTF-8" unless validated.valid_encoding?
11
+
12
+ validated.freeze
13
+ end
14
+ end
15
+
16
+ # An immutable position in grammar source.
17
+ class SourcePosition
18
+ attr_reader :byte_offset #: Integer
19
+ attr_reader :line #: Integer
20
+ attr_reader :column #: Integer
21
+
22
+ # @rbs (byte_offset: Integer, line: Integer, column: Integer) -> void
23
+ def initialize(byte_offset:, line:, column:)
24
+ raise ArgumentError, "byte_offset must be non-negative" if byte_offset.negative?
25
+ raise ArgumentError, "line and column must be positive" unless line.positive? && column.positive?
26
+
27
+ @byte_offset = byte_offset
28
+ @line = line
29
+ @column = column
30
+ freeze
31
+ end
32
+
33
+ # @rbs () -> Hash[Symbol, Integer]
34
+ def to_h
35
+ { byte_offset: byte_offset, line: line, column: column }
36
+ end
37
+ end
38
+
39
+ # An immutable half-open byte span in one grammar source.
40
+ class SourceSpan
41
+ attr_reader :file #: String
42
+ attr_reader :start #: SourcePosition
43
+ attr_reader :finish #: SourcePosition
44
+
45
+ # @rbs (file: String, start: SourcePosition, finish: SourcePosition) -> void
46
+ def initialize(file:, start:, finish:)
47
+ raise ArgumentError, "span end precedes its start" if finish.byte_offset < start.byte_offset
48
+
49
+ @file = file.dup.freeze
50
+ @start = start
51
+ @finish = finish
52
+ freeze
53
+ end
54
+
55
+ # @rbs () -> Integer
56
+ def start_byte
57
+ start.byte_offset
58
+ end
59
+
60
+ # @rbs () -> Integer
61
+ def end_byte
62
+ finish.byte_offset
63
+ end
64
+
65
+ # @rbs () -> Integer
66
+ def length
67
+ end_byte - start_byte
68
+ end
69
+
70
+ # @rbs () -> bool
71
+ def empty?
72
+ length.zero?
73
+ end
74
+
75
+ # @rbs () -> Hash[Symbol, untyped]
76
+ def to_h
77
+ { file: file, start: start.to_h, end: finish.to_h }
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ class TokenAdapter
6
+ # Root/fragment header and include-path transitions for declaration classification.
7
+ module DeclarationDocumentState
8
+ private
9
+
10
+ # @rbs (Token token) -> external_token
11
+ def class_keyword(token)
12
+ # @type self: DeclarationState
13
+ value = string_value(token)
14
+ if value == "fragment"
15
+ raise Ibex::Error, "#{token.location}: fragments require extended mode" unless @extended_mode
16
+
17
+ @fragment = true
18
+ @state = :declaration
19
+ return :FRAGMENT
20
+ end
21
+ return :IDENTIFIER unless value == "class"
22
+
23
+ @state = :class_name
24
+ :CLASS
25
+ end
26
+
27
+ # @rbs (external_token type) -> external_token?
28
+ def classify_include(type)
29
+ # @type self: DeclarationState
30
+ return unless @state == :include_path && type == :LITERAL
31
+
32
+ @state = :declaration
33
+ @declaration = nil
34
+ type
35
+ end
36
+
37
+ # @rbs (Token token, String value) -> void
38
+ def reject_fragment_pragma(token, value)
39
+ # @type self: DeclarationState
40
+ return unless @fragment && value == "pragma"
41
+
42
+ raise Ibex::Error, "#{token.location}: pragma declarations are not allowed in fragments"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end