mt-lang 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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,749 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module Parse
5
+ module Declarations
6
+ private
7
+
8
+ def parse_import
9
+ line = previous.line
10
+ path = parse_qualified_name
11
+ local_name = path.parts.last
12
+ local_column = previous.column
13
+ alias_name = if match(:as)
14
+ alias_token = consume_name("expected import alias")
15
+ local_name = alias_token.lexeme
16
+ local_column = alias_token.column
17
+ alias_token.lexeme
18
+ end
19
+ consume_end_of_statement
20
+ AST::Import.new(path:, alias_name:, line:, column: local_column, length: local_name.length)
21
+ end
22
+
23
+ def parse_declaration
24
+ attributes = parse_attribute_applications
25
+ visibility, visibility_token = parse_visibility
26
+
27
+ if legacy_layout_modifier_start?(peek)
28
+ raise error(peek, "layout modifiers must use attributes like @[packed] or @[align(...)]")
29
+ elsif match(:attribute)
30
+ reject_attributes!(attributes, "attribute")
31
+ parse_attribute_decl(visibility:)
32
+ elsif match(:const)
33
+ if match(:function)
34
+ parse_function_def(visibility:, const: true, attributes:)
35
+ else
36
+ parse_const_decl(visibility:, attributes:)
37
+ end
38
+ elsif match(:var)
39
+ reject_attributes!(attributes, "var")
40
+ parse_var_decl(visibility:)
41
+ elsif match(:event)
42
+ parse_event_decl(visibility:, attributes:)
43
+ elsif match(:type)
44
+ reject_attributes!(attributes, "type")
45
+ parse_type_alias_decl(visibility:)
46
+ elsif match(:struct)
47
+ parse_struct_decl(visibility:, attributes:)
48
+ elsif match(:union)
49
+ parse_union_decl(visibility:, attributes:)
50
+ elsif match(:enum)
51
+ parse_enum_decl(AST::EnumDecl, visibility:, attributes:)
52
+ elsif match(:flags)
53
+ parse_enum_decl(AST::FlagsDecl, visibility:, attributes:)
54
+ elsif match(:variant)
55
+ parse_variant_decl(visibility:, attributes:)
56
+ elsif match(:interface)
57
+ reject_attributes!(attributes, "interface")
58
+ parse_interface_decl(visibility:)
59
+ elsif match(:opaque)
60
+ reject_attributes!(attributes, "opaque")
61
+ parse_opaque_decl(visibility:)
62
+ elsif match(:extending)
63
+ reject_attributes!(attributes, "extending")
64
+ raise error(visibility_token, "public is not allowed on extending blocks") if visibility == :public
65
+
66
+ parse_extending_block
67
+ elsif check(:editable) || check(:static)
68
+ reject_attributes!(attributes, "standalone method")
69
+ raise error(peek, "#{peek.lexeme} function is only allowed inside extending blocks")
70
+ elsif match(:foreign)
71
+ parse_foreign_decl(visibility:, attributes:)
72
+ elsif match(:async)
73
+ consume(:function, "expected function after async")
74
+ parse_function_def(visibility:, async: true, attributes:)
75
+ elsif match(:function)
76
+ parse_function_def(visibility:, attributes:)
77
+ elsif match(:external)
78
+ raise error(visibility_token, "public is not allowed on external declarations") if visibility == :public
79
+
80
+ parse_extern_decl(attributes:)
81
+ elsif match(:static_assert)
82
+ reject_attributes!(attributes, "static_assert")
83
+ raise error(visibility_token, "public is not allowed on static_assert") if visibility == :public
84
+
85
+ parse_static_assert
86
+ elsif check_when_start?
87
+ reject_attributes!(attributes, "when")
88
+ raise error(visibility_token, "public is not allowed on when") if visibility == :public
89
+
90
+ advance
91
+ parse_when_decl
92
+ else
93
+ message = visibility == :public ? "expected exportable declaration after public" : "expected declaration"
94
+ raise error(peek, message)
95
+ end
96
+ end
97
+
98
+ def parse_raw_module_body(errors: nil)
99
+ imports = []
100
+ directives = []
101
+ declarations = []
102
+
103
+ while match(:import)
104
+ if errors
105
+ begin
106
+ imports << parse_import
107
+ rescue ParseError => e
108
+ errors << e
109
+ synchronize_to_top_level_boundary
110
+ end
111
+ else
112
+ imports << parse_import
113
+ end
114
+ skip_newlines
115
+ end
116
+
117
+ while raw_module_directive_start?
118
+ if errors
119
+ begin
120
+ directives << parse_raw_module_directive
121
+ rescue ParseError => e
122
+ errors << e
123
+ synchronize_to_top_level_boundary
124
+ end
125
+ else
126
+ directives << parse_raw_module_directive
127
+ end
128
+ skip_newlines
129
+ end
130
+
131
+ until eof?
132
+ if errors
133
+ begin
134
+ declarations << parse_raw_module_declaration
135
+ rescue ParseError => e
136
+ errors << e
137
+ synchronize_to_top_level_boundary
138
+ end
139
+ else
140
+ declarations << parse_raw_module_declaration
141
+ end
142
+ skip_newlines
143
+ end
144
+
145
+ [imports, directives, declarations]
146
+ end
147
+
148
+ def raw_module_directive_start?
149
+ check(:link) || check(:include) || check(:compiler_flag)
150
+ end
151
+
152
+ def parse_raw_module_directive
153
+ if match(:link)
154
+ parse_link_directive
155
+ elsif match(:include)
156
+ parse_include_directive
157
+ elsif match(:compiler_flag)
158
+ parse_compiler_flag_directive
159
+ else
160
+ raise error(peek, "expected external directive")
161
+ end
162
+ end
163
+
164
+ def parse_link_directive
165
+ value = consume(:string, "expected string literal after link").literal
166
+ consume_end_of_statement
167
+ AST::LinkDirective.new(value:)
168
+ end
169
+
170
+ def parse_include_directive
171
+ value = consume(:string, "expected string literal after include").literal
172
+ consume_end_of_statement
173
+ AST::IncludeDirective.new(value:)
174
+ end
175
+
176
+ def parse_compiler_flag_directive
177
+ value = consume(:string, "expected string literal after compiler_flag").literal
178
+ consume_end_of_statement
179
+ AST::CompilerFlagDirective.new(value:)
180
+ end
181
+
182
+ def parse_raw_module_declaration
183
+ attributes = parse_attribute_applications
184
+
185
+ if match(:public)
186
+ reject_attributes!(attributes)
187
+ raise error(previous, "public is not allowed in external files")
188
+ elsif legacy_layout_modifier_start?(peek)
189
+ raise error(peek, "layout modifiers must use attributes like @[packed] or @[align(...)]")
190
+ elsif match(:attribute)
191
+ reject_attributes!(attributes)
192
+ raise error(previous, "attribute is not allowed in external files")
193
+ elsif match(:const)
194
+ reject_attributes!(attributes)
195
+ if match(:function)
196
+ parse_function_def(visibility:, const: true, attributes:)
197
+ else
198
+ parse_const_decl(visibility: nil)
199
+ end
200
+ elsif match(:event)
201
+ reject_attributes!(attributes)
202
+ raise error(previous, "event is not allowed in external files")
203
+ elsif match(:type)
204
+ reject_attributes!(attributes)
205
+ parse_type_alias_decl(visibility: nil)
206
+ elsif match(:struct)
207
+ parse_struct_decl(visibility: nil, attributes:)
208
+ elsif match(:union)
209
+ reject_attributes!(attributes)
210
+ parse_union_decl(visibility: nil)
211
+ elsif match(:enum)
212
+ reject_attributes!(attributes)
213
+ parse_enum_decl(AST::EnumDecl, visibility: nil)
214
+ elsif match(:flags)
215
+ reject_attributes!(attributes)
216
+ parse_enum_decl(AST::FlagsDecl, visibility: nil)
217
+ elsif match(:opaque)
218
+ reject_attributes!(attributes)
219
+ parse_opaque_decl(visibility: nil)
220
+ elsif match(:external)
221
+ reject_attributes!(attributes)
222
+ parse_extern_decl(attributes:)
223
+ elsif check_when_start?
224
+ reject_attributes!(attributes)
225
+ advance
226
+ parse_when_stmt
227
+ else
228
+ raise error(peek, raw_module_declaration_error_message(peek))
229
+ end
230
+ end
231
+
232
+ def raw_module_declaration_error_message(token)
233
+ case token.type
234
+ when :import
235
+ "imports must appear before external directives and declarations"
236
+ when :link, :include, :compiler_flag
237
+ "#{token.lexeme} directives must appear before external declarations"
238
+ when :attribute, :event, :var, :variant, :interface, :extending, :foreign, :function, :static_assert
239
+ "#{token.lexeme} is not allowed in external files"
240
+ when :async
241
+ "async function is not allowed in external files"
242
+ when :module
243
+ "module headers are not allowed in external files"
244
+ else
245
+ "expected external declaration"
246
+ end
247
+ end
248
+
249
+ def parse_const_decl(visibility: :private, attributes: [])
250
+ line = previous.line
251
+ name = nil
252
+ type = nil
253
+ name_token = consume_name("expected constant name")
254
+ name = name_token.lexeme
255
+ if match(:arrow)
256
+ type = parse_type_ref
257
+ body = parse_block
258
+ return AST::ConstDecl.new(name:, type:, value: nil, block_body: body, visibility:, attributes:, line:, column: name_token.column)
259
+ end
260
+
261
+ consume(:colon, "expected ':' after constant name")
262
+ type = parse_type_ref
263
+ consume(:equal, "expected '=' after constant type")
264
+ value = parse_expression
265
+ consume_end_of_statement
266
+ AST::ConstDecl.new(name:, type:, value:, visibility:, attributes:, line:, column: name_token.column)
267
+ rescue ParseError => e
268
+ raise unless @recovery_errors && name
269
+
270
+ @recovery_errors << e
271
+ synchronize_to_statement_boundary
272
+ AST::ConstDecl.new(name:, type: type || recovery_error_expr(e), value: recovery_error_expr(e), visibility:, attributes:, line:, column: name_token.column)
273
+ end
274
+
275
+ def parse_var_decl(visibility: :private)
276
+ line = previous.line
277
+ name = nil
278
+ var_type = nil
279
+ name_token = consume_name("expected variable name")
280
+ name = name_token.lexeme
281
+ var_type = match(:colon) ? parse_type_ref : nil
282
+ value = if match(:equal)
283
+ parse_expression
284
+ else
285
+ raise error(name_token, "module variable without initializer requires a type") unless var_type
286
+
287
+ nil
288
+ end
289
+ consume_end_of_statement
290
+ AST::VarDecl.new(name:, type: var_type, value:, visibility:, line:, column: name_token.column)
291
+ rescue ParseError => e
292
+ raise unless @recovery_errors && name
293
+
294
+ @recovery_errors << e
295
+ synchronize_to_statement_boundary
296
+ AST::VarDecl.new(name:, type: var_type || recovery_error_expr(e), value: recovery_error_expr(e), visibility:, line:, column: name_token.column)
297
+ end
298
+
299
+ def parse_event_decl(visibility: :private, attributes: [])
300
+ line = previous.line
301
+ name_token = consume_name("expected event name")
302
+ consume(:lbracket, "expected '[' after event name")
303
+ capacity = consume(:integer, "expected positive integer capacity").literal
304
+ consume(:rbracket, "expected ']' after event capacity")
305
+ payload_type = nil
306
+ if match(:lparen)
307
+ payload_type = parse_type_ref
308
+ consume(:rparen, "expected ')' after event payload type")
309
+ end
310
+ consume_end_of_statement
311
+ AST::EventDecl.new(name: name_token.lexeme, capacity:, payload_type:, visibility:, attributes:, line:, column: name_token.column)
312
+ end
313
+
314
+ def parse_type_alias_decl(visibility: :private)
315
+ line = previous.line
316
+ name_token = consume_name("expected type alias name")
317
+ name = name_token.lexeme
318
+ consume(:equal, "expected '=' after type alias name")
319
+ target = parse_type_ref
320
+ consume_end_of_statement
321
+ AST::TypeAliasDecl.new(name:, target:, visibility:, line:, column: name_token.column)
322
+ end
323
+
324
+ def parse_attribute_decl(visibility: :private)
325
+ line = previous.line
326
+ consume(:lbracket, "expected '[' after attribute")
327
+ targets = parse_comma_separated_until(:rbracket) do
328
+ target_token = consume_path_component("expected attribute target")
329
+ case target_token.lexeme
330
+ when "struct" then :struct
331
+ when "field" then :field
332
+ when "callable" then :callable
333
+ when "const" then :const
334
+ when "event" then :event
335
+ when "enum" then :enum
336
+ when "flags" then :flags
337
+ when "union" then :union
338
+ when "variant" then :variant
339
+ else
340
+ raise error(target_token, "unknown attribute target #{target_token.lexeme}")
341
+ end
342
+ end
343
+ consume(:rbracket, "expected ']' after attribute targets")
344
+ name_token = consume_name_allowing_keywords("expected attribute name")
345
+ params = check(:lparen) ? parse_signature_params : []
346
+ consume_end_of_statement
347
+ AST::AttributeDecl.new(name: name_token.lexeme, targets:, params:, visibility:, line:, column: name_token.column)
348
+ end
349
+
350
+ def parse_struct_decl(packed: false, alignment: nil, visibility: :private, attributes: [])
351
+ line = previous.line
352
+ name_token = consume_name("expected struct name")
353
+ name = name_token.lexeme
354
+ lifetime_params, type_params = parse_struct_decl_params
355
+ implements = parse_implements_clause
356
+ c_name = parse_optional_explicit_c_name
357
+ packed, alignment = parse_struct_layout_attributes(attributes) if attributes.any?
358
+ members = parse_named_block do
359
+ parse_struct_member
360
+ end
361
+ fields = members.filter_map { |kind, member| member if kind == :field }
362
+ events = members.filter_map { |kind, member| member if kind == :event }
363
+ nested_types = members.filter_map { |kind, member| member if kind == :nested_type }
364
+ AST::StructDecl.new(name:, type_params:, implements:, c_name:, fields:, events:, nested_types:, attributes:, packed:, alignment:, visibility:, lifetime_params:, line:, column: name_token.column)
365
+ end
366
+
367
+ def parse_struct_decl_params
368
+ return [[], []] unless match(:lbracket)
369
+
370
+ lifetime_params = []
371
+ type_params = []
372
+
373
+ loop do
374
+ break if check(:rbracket)
375
+
376
+ if match(:at)
377
+ name_token = consume_name("expected lifetime name after @")
378
+ lifetime_params << "@#{name_token.lexeme}"
379
+ else
380
+ name_token = consume_name("expected type parameter name")
381
+ if match(:colon)
382
+ value_type = parse_type_ref
383
+ type_params << AST::ValueTypeParam.new(
384
+ name: name_token.lexeme,
385
+ type: value_type,
386
+ line: name_token.line,
387
+ column: name_token.column,
388
+ length: name_token.lexeme.length,
389
+ )
390
+ else
391
+ constraints = parse_type_param_constraints
392
+ type_params << AST::TypeParam.new(
393
+ name: name_token.lexeme,
394
+ constraints:,
395
+ line: name_token.line,
396
+ column: name_token.column,
397
+ length: name_token.lexeme.length,
398
+ )
399
+ end
400
+ end
401
+
402
+ break unless match(:comma)
403
+ next if check(:rbracket)
404
+ end
405
+
406
+ consume(:rbracket, "expected ']' after struct parameters")
407
+ [lifetime_params, type_params]
408
+ end
409
+
410
+ def parse_struct_member
411
+ field_attributes = parse_attribute_applications
412
+ visibility, visibility_token = parse_visibility
413
+
414
+ if match(:event)
415
+ return [:event, parse_event_decl(visibility:, attributes: field_attributes)]
416
+ end
417
+
418
+ if match(:struct)
419
+ return [:nested_type, parse_struct_decl(visibility:, attributes: field_attributes)]
420
+ end
421
+
422
+ raise error(visibility_token, "public is only allowed on struct events") if visibility == :public
423
+
424
+ field_token = consume_name("expected field name")
425
+ field_name = field_token.lexeme
426
+ consume(:colon, "expected ':' after field name")
427
+ field_type = parse_type_ref
428
+ consume_end_of_statement
429
+ [:field, AST::Field.new(name: field_name, type: field_type, attributes: field_attributes, line: field_token.line, column: field_token.column)]
430
+ rescue ParseError => e
431
+ raise unless @recovery_errors
432
+
433
+ @recovery_errors << e
434
+ synchronize_to_statement_boundary
435
+ field_name_error = (field_name if defined?(field_name)) || "error"
436
+ [:field, AST::Field.new(name: field_name_error, type: recovery_error_expr(e), attributes: field_attributes, line: e.token&.line || 1, column: e.token&.column || 1)]
437
+ end
438
+
439
+ def parse_union_decl(visibility: :private, attributes: [])
440
+ line = previous.line
441
+ name_token = consume_name("expected union name")
442
+ name = name_token.lexeme
443
+ c_name = parse_optional_explicit_c_name
444
+ fields = parse_named_block do
445
+ field_name = consume_name("expected field name").lexeme
446
+ consume(:colon, "expected ':' after field name")
447
+ field_type = parse_type_ref
448
+ consume_end_of_statement
449
+ AST::Field.new(name: field_name, type: field_type)
450
+ end
451
+ AST::UnionDecl.new(name:, c_name:, fields:, visibility:, attributes:, line:, column: name_token.column)
452
+ end
453
+
454
+ def parse_optional_explicit_c_name
455
+ return nil unless match(:equal)
456
+
457
+ consume(:cstring, "expected C string literal after '='").literal
458
+ end
459
+
460
+ def parse_enum_decl(node_class, visibility: :private, attributes: [])
461
+ line = previous.line
462
+ name_token = consume_name("expected declaration name")
463
+ name = name_token.lexeme
464
+ consume(:colon, "expected ':' after declaration name")
465
+ backing_type = if check(:newline) || check(:indent)
466
+ AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["int"], type_arguments: []), arguments: [], nullable: false, lifetime: nil, line: line, column: name.length + 1)
467
+ else
468
+ parse_type_ref
469
+ end
470
+ skip_newlines
471
+ consume(:indent, "expected indented declaration body")
472
+
473
+ members = []
474
+ skip_newlines
475
+ auto_value = 0
476
+ until check(:dedent) || eof?
477
+ member_token = consume_name("expected member name")
478
+ member_name = member_token.lexeme
479
+ if match(:equal)
480
+ value = parse_expression
481
+ if value.is_a?(AST::IntegerLiteral)
482
+ auto_value = value.value + 1
483
+ elsif value.is_a?(AST::UnaryOp) && value.operator == "-" && value.operand.is_a?(AST::IntegerLiteral)
484
+ auto_value = -value.operand.value + 1
485
+ end
486
+ else
487
+ value = AST::IntegerLiteral.new(lexeme: auto_value.to_s, value: auto_value)
488
+ auto_value += 1
489
+ end
490
+ consume_end_of_statement
491
+ members << AST::EnumMember.new(name: member_name, value:, line: member_token.line, column: member_token.column)
492
+ skip_newlines
493
+ end
494
+
495
+ consume(:dedent, "expected end of declaration body")
496
+ node_class.new(name:, backing_type:, members:, visibility:, attributes:, line:, column: name_token.column)
497
+ end
498
+
499
+ def parse_variant_decl(visibility: :private, attributes: [])
500
+ line = previous.line
501
+ name_token = consume_name("expected variant name")
502
+ name = name_token.lexeme
503
+ type_params = parse_declaration_type_params
504
+ arms = parse_named_block do
505
+ arm_name = consume_name("expected variant arm name").lexeme
506
+ fields = if match(:lparen)
507
+ parsed = parse_comma_separated_until(:rparen) do
508
+ field_name = consume_name("expected field name").lexeme
509
+ consume(:colon, "expected ':' after field name")
510
+ field_type = parse_type_ref
511
+ AST::Field.new(name: field_name, type: field_type)
512
+ end
513
+ consume(:rparen, "expected ')' after variant arm fields")
514
+ parsed
515
+ else
516
+ []
517
+ end
518
+ consume_end_of_statement
519
+ AST::VariantArm.new(name: arm_name, fields:)
520
+ end
521
+ AST::VariantDecl.new(name:, type_params:, arms:, visibility:, attributes:, line:, column: name_token.column)
522
+ end
523
+
524
+ def parse_opaque_decl(visibility: :private)
525
+ line = previous.line
526
+ name_token = consume_name("expected opaque type name")
527
+ name = name_token.lexeme
528
+ implements = parse_implements_clause
529
+ c_name = nil
530
+ if match(:equal)
531
+ c_name = consume(:cstring, "expected C string literal after '='").literal
532
+ end
533
+ consume_end_of_statement
534
+ AST::OpaqueDecl.new(name:, implements:, c_name:, visibility:, line:, column: name_token.column)
535
+ end
536
+
537
+ def parse_interface_decl(visibility: :private)
538
+ line = previous.line
539
+ name_token = consume_name("expected interface name")
540
+ name = name_token.lexeme
541
+ type_params = parse_declaration_type_params
542
+ methods = parse_named_block do
543
+ method_attributes = parse_attribute_applications
544
+ parse_interface_method_decl(attributes: method_attributes)
545
+ end
546
+ AST::InterfaceDecl.new(name:, type_params:, methods:, visibility:, line:, column: name_token.column)
547
+ end
548
+
549
+ def parse_extending_block
550
+ line = previous.line
551
+ type_name = parse_type_ref
552
+ receiver_type_param_names = extending_target_type_param_names(type_name)
553
+ methods = with_type_param_names(receiver_type_param_names) do
554
+ parse_named_block do
555
+ method_attributes = parse_attribute_applications
556
+ parse_method_def(attributes: method_attributes)
557
+ end
558
+ end
559
+ AST::ExtendingBlock.new(type_name:, methods:, line:, column: type_name.column)
560
+ end
561
+
562
+ def extending_target_type_param_names(type_name)
563
+ type_name.arguments.flat_map do |argument|
564
+ extending_target_type_param_names_from_argument(argument.value)
565
+ end.uniq
566
+ end
567
+
568
+ def extending_target_type_param_names_from_argument(value)
569
+ case value
570
+ when AST::TypeRef
571
+ nested_names = value.arguments.flat_map do |argument|
572
+ extending_target_type_param_names_from_argument(argument.value)
573
+ end
574
+ if value.name.parts.length == 1 && value.arguments.empty? && !value.nullable
575
+ name = value.name.parts.first
576
+ nested_names << name unless known_type_like_name?(name)
577
+ end
578
+ nested_names
579
+ when AST::FunctionType, AST::ProcType
580
+ value.params.flat_map { |param| extending_target_type_param_names_from_argument(param.type) } +
581
+ extending_target_type_param_names_from_argument(value.return_type)
582
+ else
583
+ []
584
+ end
585
+ end
586
+
587
+ def parse_function_def(visibility: :private, async: false, const: false, attributes: [])
588
+ line = previous.line
589
+ name_token = consume_name("expected function name")
590
+ name = name_token.lexeme
591
+ type_params, params, return_type, body = parse_callable_signature
592
+ AST::FunctionDef.new(name:, type_params:, params:, return_type:, body:, visibility:, async:, const:, attributes:, line:, column: name_token.column)
593
+ rescue ParseError => e
594
+ raise unless @recovery_errors
595
+
596
+ @recovery_errors << e
597
+ AST::FunctionDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, visibility:, async:, const:, attributes:, line:, column: name_token.column)
598
+ end
599
+
600
+ def parse_method_def(attributes: [])
601
+ visibility, _visibility_token, async, kind, line, name_token = parse_method_like_decl_head
602
+ name = name_token.lexeme
603
+ type_params, params, return_type, body = parse_callable_signature
604
+ AST::MethodDef.new(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, attributes:, line:, column: name_token.column)
605
+ rescue ParseError => e
606
+ raise unless @recovery_errors
607
+
608
+ @recovery_errors << e
609
+ name = name_token&.lexeme || "unknown"
610
+ col = name_token&.column || 1
611
+ advance until eof? || check(:function) || check(:dedent)
612
+ AST::MethodDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, kind: kind || :plain, visibility: visibility || :private, async: async || false, attributes:, line:, column: col)
613
+ end
614
+
615
+ def parse_interface_method_decl(attributes: [])
616
+ visibility, visibility_token, async, kind, line, name_token = parse_method_like_decl_head
617
+ raise error(visibility_token, "public is not allowed on interface methods") if visibility == :public
618
+
619
+ name = name_token.lexeme
620
+ _type_params, params, return_type, _body = parse_callable_signature(
621
+ allow_type_params: false,
622
+ generic_error_token: name_token,
623
+ generic_error_message: "interface method #{name} cannot be generic",
624
+ allow_body: false
625
+ )
626
+ AST::InterfaceMethodDecl.new(name:, params:, return_type:, kind:, async:, attributes:, line:, column: name_token.column)
627
+ end
628
+
629
+ def parse_method_like_decl_head
630
+ visibility, visibility_token = parse_visibility
631
+ async = match(:async)
632
+ kind = parse_method_kind
633
+ if check(:function)
634
+ consume(:function, "expected function declaration")
635
+ elsif check(:identifier)
636
+ bad_token = advance
637
+ @recovery_errors << ParseError.new("unknown keyword '#{bad_token.lexeme}'; expected 'function' (did you mean 'editable', 'static', or 'async'?)", token: bad_token, path: @path) if @recovery_errors
638
+ else
639
+ raise error(peek, "expected function declaration")
640
+ end
641
+ line = previous.line
642
+ name_token = consume_name("expected function name")
643
+ [visibility, visibility_token, async, kind, line, name_token]
644
+ end
645
+
646
+ def parse_method_kind
647
+ return :editable if match(:editable)
648
+ return :static if match(:static)
649
+
650
+ :plain
651
+ end
652
+
653
+ def parse_callable_signature(allow_type_params: true, generic_error_token: nil, generic_error_message: nil, allow_body: true)
654
+ type_params = parse_declaration_type_params
655
+ if !allow_type_params && type_params.any?
656
+ raise error(generic_error_token || previous, generic_error_message || "generic callable declarations are not allowed here")
657
+ end
658
+
659
+ params = nil
660
+ return_type = nil
661
+ body = nil
662
+
663
+ with_type_param_names(type_params.map(&:name)) do
664
+ params = parse_params
665
+ return_type = match(:arrow) ? parse_type_ref : nil
666
+ if allow_body
667
+ body = parse_block_body_safe
668
+ else
669
+ consume_end_of_statement
670
+ end
671
+ end
672
+
673
+ [type_params, params, return_type, body]
674
+ end
675
+
676
+ def parse_block_body_safe
677
+ parse_block
678
+ rescue ParseError => e
679
+ raise unless @recovery_errors
680
+
681
+ @recovery_errors << e
682
+ match(:newline)
683
+ if match(:indent)
684
+ begin
685
+ parse_and_dedent_block_body || []
686
+ rescue ParseError => e2
687
+ @recovery_errors << e2
688
+ []
689
+ end
690
+ end
691
+ end
692
+
693
+ def parse_visibility
694
+ return [:public, previous] if match(:public)
695
+
696
+ [:private, nil]
697
+ end
698
+
699
+ def parse_extern_decl(attributes: [])
700
+ consume(:function, "expected function after external")
701
+ parse_extern_function_decl(attributes:)
702
+ end
703
+
704
+ def parse_foreign_decl(visibility: :private, attributes: [])
705
+ consume(:function, "expected function after foreign")
706
+ parse_foreign_function_decl(visibility:, attributes:)
707
+ end
708
+
709
+ def parse_extern_function_decl(attributes: [])
710
+ line = previous.line
711
+ name = consume_name("expected function name").lexeme
712
+ type_params = parse_declaration_type_params
713
+ params = nil
714
+ variadic = false
715
+ return_type = nil
716
+ mapping = nil
717
+ with_type_param_names(type_params.map(&:name)) do
718
+ params, variadic = parse_foreign_params(allow_variadic: true)
719
+ consume(:arrow, "expected '->' before external function return type")
720
+ return_type = parse_type_ref
721
+ if match(:equal)
722
+ mapping = parse_expression
723
+ end
724
+ end
725
+ consume_end_of_statement
726
+ AST::ExternFunctionDecl.new(name:, type_params:, params:, return_type:, variadic:, attributes:, line:, mapping:)
727
+ end
728
+
729
+ def parse_foreign_function_decl(visibility: :private, attributes: [])
730
+ line = previous.line
731
+ name = consume_name("expected function name").lexeme
732
+ type_params = parse_declaration_type_params
733
+ params = nil
734
+ variadic = false
735
+ return_type = nil
736
+ mapping = nil
737
+ with_type_param_names(type_params.map(&:name)) do
738
+ params, variadic = parse_foreign_params(allow_variadic: true)
739
+ consume(:arrow, "expected '->' before foreign function return type")
740
+ return_type = parse_type_ref
741
+ consume(:equal, "expected '=' before foreign function mapping")
742
+ mapping = parse_expression
743
+ end
744
+ consume_end_of_statement
745
+ AST::ForeignFunctionDecl.new(name:, type_params:, params:, return_type:, variadic:, mapping:, visibility:, attributes:, line:)
746
+ end
747
+ end
748
+ end
749
+ end