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
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Parses workspace sources, discovers safe include closures, and renders diagnostics.
6
+ class WorkspaceAnalyzer
7
+ GLOB_CHARACTERS = /[*?\[\]{}]/ #: Regexp
8
+ WINDOWS_ABSOLUTE = %r{\A(?:[A-Za-z]:[\\/]|\\\\)} #: Regexp
9
+
10
+ # @rbs!
11
+ # type lsp_diagnostic = Hash[String, untyped]
12
+ # type analyzed_document = {
13
+ # source: String,
14
+ # document: Frontend::SourceDocument?,
15
+ # diagnostics: Array[lsp_diagnostic]
16
+ # }
17
+
18
+ # @rbs (Workspace workspace, Frontend::SourceLoader loader) -> void
19
+ def initialize(workspace, loader)
20
+ @workspace = workspace
21
+ @loader = loader
22
+ end
23
+
24
+ # @rbs (String path) -> analyzed_document
25
+ def analyze(path)
26
+ source = @loader.read(path)
27
+ return analyze_fragment(path, source) if fragment_source?(source, path)
28
+
29
+ analyze_root(path, source)
30
+ rescue Ibex::Error, SystemCallError => e
31
+ source ||= readable_source(path)
32
+ { source: source, document: nil, diagnostics: [diagnostic(e.message, source, path)] }
33
+ end
34
+
35
+ # @rbs (String root) -> [Array[String], Hash[String, analyzed_document]]
36
+ def closure(root)
37
+ files = [] #: Array[String]
38
+ analyzed = {} #: Hash[String, analyzed_document]
39
+ visiting = [root]
40
+ until visiting.empty?
41
+ path = visiting.pop
42
+ next unless path
43
+ next if analyzed.key?(path)
44
+
45
+ current = analyze(path)
46
+ analyzed[path] = current
47
+ files << path
48
+ dependencies(current.fetch(:document)).reverse_each do |target|
49
+ visiting << target unless analyzed.key?(target)
50
+ end
51
+ end
52
+ [files, analyzed]
53
+ end
54
+
55
+ # @rbs (String root) -> [Frontend::Resolution?, Array[lsp_diagnostic]]
56
+ def resolve(root)
57
+ resolution = Frontend::Resolver.new(root, mode: :extended, loader: @loader).resolve
58
+ [resolution, []]
59
+ rescue Ibex::Error, SystemCallError => e
60
+ [nil, [diagnostic(e.message, source_for_error(e.message, root), file_for_error(e.message, root))]]
61
+ end
62
+
63
+ private
64
+
65
+ # @rbs (String path, String source) -> analyzed_document
66
+ def analyze_root(path, source)
67
+ result = Frontend::Parser.new(source, file: path, mode: :extended)
68
+ .parse_with_diagnostics(max_diagnostics: 20)
69
+ diagnostics = result.diagnostics.map { |entry| frontend_diagnostic(entry, source) }
70
+ { source: source, document: result.document, diagnostics: diagnostics }
71
+ end
72
+
73
+ # @rbs (String path, String source) -> analyzed_document
74
+ def analyze_fragment(path, source)
75
+ document = Frontend::Parser.new(source, file: path, mode: :extended).parse_source_document
76
+ { source: source, document: document, diagnostics: [] }
77
+ end
78
+
79
+ # @rbs (String source, String path) -> bool
80
+ def fragment_source?(source, path)
81
+ document, = Frontend::Lexer.new(source, file: path).tokenize_document_recovering(max_diagnostics: 1)
82
+ first = document.tokens.find { |token| token.type != :eof }
83
+ first&.value == "fragment"
84
+ rescue Ibex::Error
85
+ false
86
+ end
87
+
88
+ # @rbs (Frontend::SourceDocument? document) -> Array[String]
89
+ def dependencies(document)
90
+ ast = document&.ast
91
+ return [] unless ast
92
+
93
+ ast.declarations.filter_map do |declaration|
94
+ next unless declaration.is_a?(Frontend::AST::Include)
95
+
96
+ canonical_include(declaration)
97
+ rescue Ibex::Error, SystemCallError
98
+ nil
99
+ end
100
+ end
101
+
102
+ # @rbs (Frontend::AST::Include include_node) -> String
103
+ def canonical_include(include_node)
104
+ path = include_node.path
105
+ return invalid_include(include_node) if path.empty? || path.include?("\0")
106
+ return invalid_include(include_node) if path.start_with?("/") || path.match?(WINDOWS_ABSOLUTE)
107
+ return invalid_include(include_node) if path.split(%r{[\\/]}).include?("..")
108
+ return invalid_include(include_node) if path.match?(GLOB_CHARACTERS)
109
+
110
+ candidate = File.expand_path(path, File.dirname(include_node.loc.file))
111
+ canonical = @loader.canonical_path(candidate, allow_missing: true)
112
+ return canonical if @workspace.root_for(canonical)
113
+
114
+ invalid_include(include_node)
115
+ end
116
+
117
+ # @rbs (Frontend::AST::Include include_node) -> bot
118
+ def invalid_include(include_node)
119
+ raise Ibex::Error, "#{include_node.loc}: unsafe include path"
120
+ end
121
+
122
+ # @rbs (String message, String source, String fallback_path) -> lsp_diagnostic
123
+ def diagnostic(message, source, fallback_path)
124
+ file, line, column, text = diagnostic_parts(message, fallback_path)
125
+ {
126
+ "range" => diagnostic_range(source, line, column),
127
+ "severity" => 1,
128
+ "code" => "ibex.frontend",
129
+ "source" => "ibex",
130
+ "message" => text,
131
+ "data" => { "file" => file }
132
+ }
133
+ end
134
+
135
+ # @rbs (Frontend::Diagnostic diagnostic, String source) -> lsp_diagnostic
136
+ def frontend_diagnostic(diagnostic, source)
137
+ range = if diagnostic.span
138
+ PositionCodec.new(source).range(diagnostic.span)
139
+ else
140
+ diagnostic_range(source, diagnostic.location.line, diagnostic.location.column)
141
+ end
142
+ {
143
+ "range" => range,
144
+ "severity" => 1,
145
+ "code" => diagnostic.code,
146
+ "source" => "ibex",
147
+ "message" => diagnostic.message,
148
+ "data" => { "file" => diagnostic.location.file }
149
+ }
150
+ rescue ArgumentError, Ibex::Error
151
+ {
152
+ "range" => diagnostic_range("", 1, 1),
153
+ "severity" => 1,
154
+ "code" => diagnostic.code,
155
+ "source" => "ibex",
156
+ "message" => diagnostic.message,
157
+ "data" => { "file" => diagnostic.location.file }
158
+ }
159
+ end
160
+
161
+ # @rbs (String message, String fallback_path) -> [String, Integer, Integer, String]
162
+ def diagnostic_parts(message, fallback_path)
163
+ match = message.match(/\A(.*):(\d+):(\d+):\s*(.*)\z/m)
164
+ return [fallback_path, 1, 1, message] unless match
165
+
166
+ [match[1] || fallback_path, Integer(match[2]), Integer(match[3]), match[4] || message]
167
+ end
168
+
169
+ # @rbs (String source, Integer line, Integer column) -> Hash[String, Hash[String, Integer]]
170
+ def diagnostic_range(source, line, column)
171
+ line_text = source.lines.fetch(line - 1, "").delete_suffix("\n").delete_suffix("\r")
172
+ prefix = line_text.each_char.take([column - 1, 0].max).join
173
+ character = prefix.each_codepoint.sum { |codepoint| codepoint > 0xFFFF ? 2 : 1 }
174
+ point = { "line" => [line - 1, 0].max, "character" => character }
175
+ { "start" => point, "end" => point.dup }
176
+ end
177
+
178
+ # @rbs (String path) -> String
179
+ def readable_source(path)
180
+ @loader.read(path)
181
+ rescue SystemCallError
182
+ ""
183
+ end
184
+
185
+ # @rbs (String message, String fallback) -> String
186
+ def file_for_error(message, fallback)
187
+ match = message.match(/\A(.*):\d+:\d+:/)
188
+ return fallback unless match
189
+
190
+ match[1] || fallback
191
+ end
192
+
193
+ # @rbs (String message, String fallback) -> String
194
+ def source_for_error(message, fallback)
195
+ readable_source(file_for_error(message, fallback))
196
+ end
197
+ end
198
+ end
199
+ end
data/lib/ibex/lsp.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "uri"
5
+ require_relative "version"
6
+ require_relative "error"
7
+ require_relative "frontend"
8
+ require_relative "lsp/protocol_error"
9
+ require_relative "lsp/transport"
10
+ require_relative "lsp/position_codec"
11
+ require_relative "lsp/workspace"
12
+ require_relative "lsp/workspace_analyzer"
13
+ require_relative "lsp/document_store_validation"
14
+ require_relative "lsp/document_store_diagnostics"
15
+ require_relative "lsp/document_store"
16
+ require_relative "lsp/symbol_occurrence"
17
+ require_relative "lsp/symbol_index_source_queries"
18
+ require_relative "lsp/symbol_index_precedence_references"
19
+ require_relative "lsp/symbol_index_builder"
20
+ require_relative "lsp/symbol_index"
21
+ require_relative "lsp/request_support"
22
+ require_relative "lsp/initialization_handlers"
23
+ require_relative "lsp/document_handlers"
24
+ require_relative "lsp/navigation_handlers"
25
+ require_relative "lsp/request_handlers"
26
+ require_relative "lsp/server"
27
+
28
+ module Ibex
29
+ # Language Server Protocol support for grammar workspaces.
30
+ module LSP
31
+ end
32
+ end
@@ -11,27 +11,160 @@ module Ibex
11
11
  @declared_tokens = {} #: Hash[String, IR::location]
12
12
  @precedence = {} #: Hash[String, IR::precedence]
13
13
  @precedence_locations = {} #: Hash[String, IR::location]
14
+ @display_names = {} #: Hash[String, String]
15
+ @display_name_locations = {} #: Hash[String, IR::location]
16
+ @semantic_types = {} #: Hash[String, String]
17
+ @semantic_type_locations = {} #: Hash[String, IR::location]
14
18
  @options = { result_var: true, omit_action_call: true }
19
+ @options[:cst] = true if @ast.cst
15
20
  @expected_conflicts = 0
21
+ @expected_rr_conflicts = nil
16
22
  @conversions = {} #: Hash[String, String]
23
+ @parser_parameters = [] #: Array[IR::parser_parameter]
24
+ @value_printers = {} #: Hash[String, IR::value_printer]
25
+ @recovery_sync_tokens = [] #: Array[String]
26
+ @recovery_sync_location = nil #: Frontend::Location?
27
+ @on_error_reduce_groups = [] #: Array[Array[String]]
28
+ @on_error_reduce_locations = {} #: Hash[String, Frontend::Location]
29
+ @grammar_tests = [] #: Array[IR::grammar_test]
30
+ @lexer_declaration = nil #: Frontend::AST::Lexer?
31
+ @node_shapes = {} #: Hash[String, Array[String]]
17
32
  @ast.declarations.each { |declaration| read_declaration(declaration) }
18
33
  end
19
34
 
20
35
  # @rbs (Frontend::AST::declaration declaration) -> void
21
- def read_declaration(declaration)
36
+ def read_declaration(declaration) # rubocop:disable Metrics/CyclomaticComplexity
22
37
  # @type self: Normalizer
23
38
  case declaration
24
- when Frontend::AST::Tokens then declaration.names.each { |name| @declared_tokens[name] = declaration.loc.to_h }
39
+ when Frontend::AST::Tokens then read_tokens(declaration)
25
40
  when Frontend::AST::Precedence then read_precedence(declaration)
26
- when Frontend::AST::Options then declaration.names.each { |name| read_option(name, declaration.loc) }
41
+ when Frontend::AST::Options then read_options(declaration)
27
42
  when Frontend::AST::Expect then @expected_conflicts = declaration.conflicts
28
- when Frontend::AST::Start
29
- @explicit_start = declaration.name
30
- @start_location = declaration.loc
31
- when Frontend::AST::Convert then declaration.pairs.each { |pair| @conversions[pair.name] = pair.expression }
43
+ when Frontend::AST::ExpectRR then @expected_rr_conflicts = declaration.conflicts
44
+ when Frontend::AST::Start, Frontend::AST::Recovery, Frontend::AST::OnErrorReduce, Frontend::AST::GrammarTest
45
+ read_parser_control_declaration(declaration)
46
+ when Frontend::AST::Lexer
47
+ fail_at(declaration.loc, "duplicate lexer declaration") if @lexer_declaration
48
+ @lexer_declaration = declaration
49
+ when Frontend::AST::Convert then read_conversions(declaration)
50
+ when Frontend::AST::DisplayName, Frontend::AST::SemanticType, Frontend::AST::Parameter, Frontend::AST::Printer
51
+ read_extended_declaration(declaration)
52
+ when Frontend::AST::Include then fail_at(declaration.loc, "includes must be resolved before normalization")
32
53
  end
33
54
  end
34
55
 
56
+ # @rbs (Frontend::AST::Start declaration) -> void
57
+ def read_start_declaration(declaration)
58
+ # @type self: Normalizer
59
+ fail_at(declaration.loc, "duplicate start declaration") if @explicit_starts
60
+ if declaration.names.length > 1 && @mode != :extended
61
+ fail_at(declaration.loc, "multiple start symbols require extended mode")
62
+ end
63
+ fail_at(declaration.loc, "start declaration requires at least one symbol") if declaration.names.empty?
64
+ unless declaration.names.uniq.length == declaration.names.length
65
+ fail_at(declaration.loc, "start symbols must be unique")
66
+ end
67
+
68
+ @explicit_starts = declaration.names
69
+ @start_location = declaration.loc
70
+ end
71
+
72
+ # @rbs (Frontend::AST::symbol_metadata | Frontend::AST::Parameter | Frontend::AST::Printer declaration) -> void
73
+ def read_extended_declaration(declaration)
74
+ if declaration.is_a?(Frontend::AST::Parameter)
75
+ read_parser_parameter(declaration)
76
+ elsif declaration.is_a?(Frontend::AST::Printer)
77
+ read_value_printer(declaration)
78
+ else
79
+ read_symbol_metadata_declaration(declaration)
80
+ end
81
+ end
82
+
83
+ # @rbs (Frontend::AST::Printer declaration) -> void
84
+ def read_value_printer(declaration)
85
+ # @type self: Normalizer
86
+ if @value_printers.key?(declaration.name)
87
+ fail_at(declaration.loc, "duplicate %printer declaration for #{declaration.name}")
88
+ end
89
+
90
+ @value_printers[declaration.name] = {
91
+ symbol: declaration.name, code: declaration.code, loc: declaration.loc.to_h
92
+ }
93
+ end
94
+
95
+ # @rbs () -> void
96
+ def validate_value_printers
97
+ @value_printers.each_value do |printer|
98
+ next if @symbols_by_name.key?(printer[:symbol])
99
+
100
+ location = printer[:loc]
101
+ raise Ibex::Error,
102
+ "#{location[:file]}:#{location[:line]}:#{location[:column]}: " \
103
+ "%printer references missing symbol #{printer[:symbol]}"
104
+ end
105
+ end
106
+
107
+ # @rbs (Frontend::AST::Parameter declaration) -> void
108
+ def read_parser_parameter(declaration)
109
+ # @type self: Normalizer
110
+ if @parser_parameters.any? { |parameter| parameter[:name] == declaration.name }
111
+ fail_at(declaration.loc, "duplicate %param declaration for #{declaration.name}")
112
+ end
113
+ unless declaration.name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
114
+ fail_at(declaration.loc, "%param name #{declaration.name.inspect} must be a Ruby local identifier")
115
+ end
116
+ if Normalizer::RUBY_KEYWORDS.include?(declaration.name)
117
+ fail_at(declaration.loc, "%param name #{declaration.name.inspect} is a Ruby keyword")
118
+ end
119
+
120
+ @parser_parameters << { name: declaration.name, semantic_type: declaration.semantic_type }
121
+ end
122
+
123
+ # @rbs (Frontend::AST::Tokens declaration) -> void
124
+ def read_tokens(declaration)
125
+ # @type self: Normalizer
126
+ declaration.names.each { |name| @declared_tokens[name] = declaration.loc.to_h }
127
+ (declaration.aliases || {}).each do |name, value|
128
+ fail_at(declaration.loc, "duplicate display declaration for #{name}") if @display_names.key?(name)
129
+
130
+ @display_names[name] = value
131
+ @display_name_locations[name] = declaration.loc.to_h
132
+ end
133
+ end
134
+
135
+ # @rbs (Frontend::AST::Options declaration) -> void
136
+ def read_options(declaration)
137
+ declaration.names.each { |name| read_option(name, declaration.loc) }
138
+ end
139
+
140
+ # @rbs (Frontend::AST::Convert declaration) -> void
141
+ def read_conversions(declaration)
142
+ # @type self: Normalizer
143
+ declaration.pairs.each { |pair| @conversions[pair.name] = pair.expression }
144
+ end
145
+
146
+ # @rbs (Frontend::AST::symbol_metadata declaration) -> void
147
+ def read_symbol_metadata_declaration(declaration)
148
+ # @type self: Normalizer
149
+ if declaration.is_a?(Frontend::AST::DisplayName)
150
+ read_symbol_metadata(declaration, @display_names, @display_name_locations, "display")
151
+ else
152
+ read_symbol_metadata(declaration, @semantic_types, @semantic_type_locations, "type")
153
+ end
154
+ end
155
+
156
+ # @rbs (Frontend::AST::symbol_metadata declaration, Hash[String, String] values,
157
+ # Hash[String, IR::location] locations, String label) -> void
158
+ def read_symbol_metadata(declaration, values, locations, label)
159
+ # @type self: Normalizer
160
+ if values.key?(declaration.name)
161
+ fail_at(declaration.loc, "duplicate #{label} declaration for #{declaration.name}")
162
+ end
163
+
164
+ values[declaration.name] = declaration.value
165
+ locations[declaration.name] = declaration.loc.to_h
166
+ end
167
+
35
168
  # @rbs (Frontend::AST::Precedence declaration) -> void
36
169
  def read_precedence(declaration)
37
170
  # @type self: Normalizer
@@ -10,12 +10,26 @@ module Ibex
10
10
  # @rbs () -> void
11
11
  def validate_grammar
12
12
  # @type self: Normalizer
13
+ validate_symbol_metadata
13
14
  warn_duplicate_productions
14
15
  warn_unreachable_nonterminals
16
+ warn_unreachable_terminals
15
17
  warn_unused_terminals
18
+ warn_unused_precedence
16
19
  warn_empty_language
17
20
  end
18
21
 
22
+ # @rbs () -> void
23
+ def validate_symbol_metadata
24
+ # @type self: Normalizer
25
+ { display: @display_name_locations, type: @semantic_type_locations }.each do |label, locations|
26
+ locations.each do |name, location|
27
+ fail_hash(location, "#{label} declaration references undefined symbol #{name}") unless
28
+ symbol(name) || parameter_template?(name) || (label == :type && @inline_rule_names.include?(name))
29
+ end
30
+ end
31
+ end
32
+
19
33
  # @rbs () -> void
20
34
  def warn_duplicate_productions
21
35
  # @type self: Normalizer
@@ -45,8 +59,8 @@ module Ibex
45
59
  # @rbs () -> Set[Integer]
46
60
  def reachable_symbol_ids
47
61
  # @type self: Normalizer
48
- start = required_symbol(@start_name).id
49
- reachable = Set[start]
62
+ starts = @start_names.map { |name| required_symbol(name).id }
63
+ reachable = Set.new(starts)
50
64
  loop do
51
65
  before = reachable.length
52
66
  @productions.select { |production| reachable.include?(production.lhs) }.each do |production|
@@ -67,6 +81,35 @@ module Ibex
67
81
  end
68
82
  end
69
83
 
84
+ # @rbs () -> void
85
+ def warn_unreachable_terminals
86
+ # @type self: Normalizer
87
+ reachable = reachable_symbol_ids
88
+ used = @productions.flat_map(&:rhs).to_set
89
+ @declared_tokens.each_key do |name|
90
+ grammar_symbol = required_symbol(name)
91
+ next unless used.include?(grammar_symbol.id)
92
+ next if reachable.include?(grammar_symbol.id)
93
+
94
+ @warnings << { type: :unreachable_terminal, symbol: name, loc: @declared_tokens[name] }
95
+ end
96
+ end
97
+
98
+ # @rbs () -> void
99
+ def warn_unused_precedence
100
+ # @type self: Normalizer
101
+ referenced = @productions.each_with_object(Set.new) do |production, symbol_ids|
102
+ production.rhs.each { |id| symbol_ids << id }
103
+ symbol_ids << production.precedence_override if production.precedence_override
104
+ end
105
+ @precedence.each_key do |name|
106
+ grammar_symbol = required_symbol(name)
107
+ next if referenced.include?(grammar_symbol.id)
108
+
109
+ @warnings << { type: :unused_precedence, symbol: name, loc: @precedence_locations[name] }
110
+ end
111
+ end
112
+
70
113
  # @rbs () -> void
71
114
  def warn_empty_language
72
115
  # @type self: Normalizer
@@ -78,10 +121,12 @@ module Ibex
78
121
  end
79
122
  break if productive.length == before
80
123
  end
81
- return if productive.include?(required_symbol(@start_name).id)
124
+ @start_names.each do |name|
125
+ next if productive.include?(required_symbol(name).id)
82
126
 
83
- start_symbol = required_symbol(@start_name)
84
- @warnings << { type: :empty_language, symbol: @start_name, loc: start_symbol.location }
127
+ start_symbol = required_symbol(name)
128
+ @warnings << { type: :empty_language, symbol: name, loc: start_symbol.location }
129
+ end
85
130
  end
86
131
 
87
132
  # @rbs () -> Set[Integer]
@@ -9,6 +9,9 @@ module Ibex
9
9
  def normalize_user_productions
10
10
  # @type self: Normalizer
11
11
  @ast.rules.each do |rule|
12
+ next if parameterized_rule?(rule)
13
+
14
+ @current_include_chain = @resolution&.include_chain_for(rule) || []
12
15
  rule.alternatives.each { |alternative| normalize_alternative(rule, alternative) }
13
16
  end
14
17
  end
@@ -18,7 +21,8 @@ module Ibex
18
21
  # @type self: Normalizer
19
22
  rhs = [] #: Array[String]
20
23
  named_refs = [] #: Array[IR::named_ref]
21
- alternative.items.each do |item|
24
+ items = normalized_alternative_items(alternative)
25
+ items.each do |item|
22
26
  if item.is_a?(Frontend::AST::InlineAction)
23
27
  rhs << expand_inline_action(item, rhs.length, named_refs)
24
28
  next
@@ -28,14 +32,31 @@ module Ibex
28
32
  add_named_reference(item, named_refs, rhs.length - 1)
29
33
  end
30
34
  action = normalize_action(alternative.action, named_refs)
35
+ node = normalize_node_annotation(alternative, rhs)
31
36
  add_production(rule.lhs, rhs, action, alternative.precedence,
32
- { kind: :user, loc: alternative.loc.to_h })
37
+ { kind: :user, loc: alternative.loc.to_h }, rule.documentation, node)
38
+ end
39
+
40
+ # @rbs (Frontend::AST::Alternative alternative) -> Array[Frontend::AST::item]
41
+ def normalized_alternative_items(alternative)
42
+ # @type self: Normalizer
43
+ explicit = alternative.items.grep(Frontend::AST::Empty)
44
+ if explicit.any?
45
+ fail_at(explicit.first.loc, "%empty must be the only item in an alternative") unless alternative.items.one?
46
+ return []
47
+ end
48
+
49
+ if alternative.items.empty? && (@mode.to_sym == :extended || @ast.extended)
50
+ @warnings << { type: :implicit_empty, loc: alternative.loc.to_h }
51
+ end
52
+ alternative.items
33
53
  end
34
54
 
35
55
  # @rbs (Frontend::AST::item item) -> String
36
56
  def normalize_item(item)
37
57
  # @type self: Normalizer
38
58
  return symbol_for_reference(item).name if item.is_a?(Frontend::AST::SymbolReference)
59
+ return specialize_parameterized_reference(item) if item.is_a?(Frontend::AST::ParameterizedReference)
39
60
  return expand_group(item) if item.is_a?(Frontend::AST::Group)
40
61
  return expand_optional(item) if item.is_a?(Frontend::AST::Optional)
41
62
  return expand_star(item) if item.is_a?(Frontend::AST::Star)
@@ -59,6 +80,12 @@ module Ibex
59
80
  def expand_optional(item)
60
81
  # @type self: Normalizer
61
82
  base = normalize_item(item.item)
83
+ build_optional(item, base)
84
+ end
85
+
86
+ # @rbs (Frontend::AST::Optional item, String base) -> String
87
+ def build_optional(item, base)
88
+ # @type self: Normalizer
62
89
  helper = new_helper("optional", item.loc)
63
90
  add_production(helper, [], synthetic_action("nil", item.loc), nil, synthetic_origin(:optional, item))
64
91
  add_production(helper, [base], synthetic_action("val[0]", item.loc), nil, synthetic_origin(:optional, item))
@@ -69,6 +96,12 @@ module Ibex
69
96
  def expand_star(item)
70
97
  # @type self: Normalizer
71
98
  base = normalize_item(item.item)
99
+ build_star(item, base)
100
+ end
101
+
102
+ # @rbs (Frontend::AST::Star item, String base) -> String
103
+ def build_star(item, base)
104
+ # @type self: Normalizer
72
105
  helper = new_helper("star", item.loc)
73
106
  add_production(helper, [], synthetic_action("[]", item.loc), nil, synthetic_origin(:star, item))
74
107
  add_production(helper, [helper, base], synthetic_action("val[0] + [val[1]]", item.loc), nil,
@@ -80,6 +113,12 @@ module Ibex
80
113
  def expand_plus(item)
81
114
  # @type self: Normalizer
82
115
  base = normalize_item(item.item)
116
+ build_plus(item, base)
117
+ end
118
+
119
+ # @rbs (Frontend::AST::Plus item, String base) -> String
120
+ def build_plus(item, base)
121
+ # @type self: Normalizer
83
122
  helper = new_helper("plus", item.loc)
84
123
  add_production(helper, [base], synthetic_action("[val[0]]", item.loc), nil, synthetic_origin(:plus, item))
85
124
  add_production(helper, [helper, base], synthetic_action("val[0] + [val[1]]", item.loc), nil,
@@ -92,6 +131,12 @@ module Ibex
92
131
  # @type self: Normalizer
93
132
  base = normalize_item(item.item)
94
133
  separator = normalize_item(item.separator)
134
+ build_separated_list(item, base, separator)
135
+ end
136
+
137
+ # @rbs (Frontend::AST::SeparatedList item, String base, String separator) -> String
138
+ def build_separated_list(item, base, separator)
139
+ # @type self: Normalizer
95
140
  helper = new_helper("separated_list", item.loc)
96
141
  unless item.nonempty
97
142
  add_production(helper, [], synthetic_action("[]", item.loc), nil, synthetic_origin(:separated_list, item))
@@ -110,12 +155,18 @@ module Ibex
110
155
  helper = new_helper("group", item.loc)
111
156
  item.alternatives.each do |alternative|
112
157
  rhs = alternative.map { |child| normalize_item(child) }
113
- expression = group_value_expression(rhs.length)
114
- add_production(helper, rhs, synthetic_action(expression, item.loc), nil, synthetic_origin(:group, item))
158
+ add_group_production(helper, rhs, item)
115
159
  end
116
160
  helper
117
161
  end
118
162
 
163
+ # @rbs (String helper, Array[String] rhs, Frontend::AST::Group item) -> void
164
+ def add_group_production(helper, rhs, item)
165
+ # @type self: Normalizer
166
+ expression = group_value_expression(rhs.length)
167
+ add_production(helper, rhs, synthetic_action(expression, item.loc), nil, synthetic_origin(:group, item))
168
+ end
169
+
119
170
  # @rbs (Integer length) -> String
120
171
  def group_value_expression(length)
121
172
  # @type self: Normalizer
@@ -125,28 +176,6 @@ module Ibex
125
176
  "val"
126
177
  end
127
178
 
128
- # @rbs (Frontend::AST::Group group) -> void
129
- def reject_group_named_references(group)
130
- # @type self: Normalizer
131
- reference = group.alternatives.flatten.filter_map { |item| named_reference_in(item) }.first
132
- fail_at(reference.loc, "named references inside EBNF groups are not supported") if reference
133
- end
134
-
135
- # @rbs (Frontend::AST::item item) -> Frontend::AST::SymbolReference?
136
- def named_reference_in(item)
137
- # @type self: Normalizer
138
- return item if item.is_a?(Frontend::AST::SymbolReference) && item.named_reference
139
- if item.is_a?(Frontend::AST::Group)
140
- return item.alternatives.flatten.filter_map { |child| named_reference_in(child) }.first
141
- end
142
- if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
143
- item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
144
- return named_reference_in(item.item)
145
- end
146
-
147
- nil
148
- end
149
-
150
179
  # @rbs (String kind, Frontend::Location location) -> String
151
180
  def new_helper(kind, location)
152
181
  # @type self: Normalizer
@@ -177,42 +206,17 @@ module Ibex
177
206
  IR::Action.new(code: action.code, location: action.loc.to_h, named_refs: named_refs)
178
207
  end
179
208
 
180
- # @rbs (Frontend::AST::item item, Array[IR::named_ref] refs, Integer index) -> void
181
- def add_named_reference(item, refs, index)
182
- # @type self: Normalizer
183
- reference = unwrap_reference(item)
184
- return unless reference
185
-
186
- name = reference.named_reference
187
- return unless name
188
-
189
- fail_at(reference.loc, "reserved named reference #{name}") if Normalizer::RESERVED_NAMES.include?(name)
190
- fail_at(reference.loc, "duplicate named reference #{name}") if refs.any? { |entry| entry[:name] == name }
191
- refs << { name: name, index: index }
192
- end
193
-
194
- # @rbs (Frontend::AST::item item) -> Frontend::AST::SymbolReference?
195
- def unwrap_reference(item)
196
- # @type self: Normalizer
197
- return item if item.is_a?(Frontend::AST::SymbolReference)
198
- if item.is_a?(Frontend::AST::Optional) || item.is_a?(Frontend::AST::Star) ||
199
- item.is_a?(Frontend::AST::Plus) || item.is_a?(Frontend::AST::SeparatedList)
200
- return unwrap_reference(item.item)
201
- end
202
-
203
- nil
204
- end
205
-
206
209
  # @rbs (String lhs_name, Array[String] rhs_names, IR::Action? action, String? precedence_name,
207
- # Hash[Symbol, untyped] origin) -> void
208
- def add_production(lhs_name, rhs_names, action, precedence_name, origin)
210
+ # Hash[Symbol, untyped] origin, ?String? documentation, ?IR::node_annotation? node) -> void
211
+ def add_production(lhs_name, rhs_names, action, precedence_name, origin, documentation = nil, node = nil)
209
212
  # @type self: Normalizer
210
213
  lhs = symbol(lhs_name) || intern(lhs_name, :nonterminal, location: origin[:loc])
211
214
  rhs = rhs_names.map { |name| symbol(name)&.id || fail_hash(origin[:loc], "undefined symbol #{name}") }
212
215
  precedence = precedence_name && symbol(precedence_name)
213
216
  fail_hash(origin[:loc], "undefined precedence symbol #{precedence_name}") if precedence_name && !precedence
214
217
  @productions << IR::Production.new(id: @productions.length, lhs: lhs.id, rhs: rhs, action: action,
215
- precedence_override: precedence&.id, origin: origin)
218
+ precedence_override: precedence&.id, origin: origin,
219
+ documentation: documentation, expansion: resolved_expansion, node: node)
216
220
  end
217
221
  end
218
222
  end