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,327 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "semantic_analyzer/type_declaration"
4
+ require_relative "semantic_analyzer/attributes"
5
+ require_relative "semantic_analyzer/function_binding"
6
+ require_relative "semantic_analyzer/top_level"
7
+ require_relative "semantic_analyzer/interface_conformance"
8
+ require_relative "semantic_analyzer/nullability"
9
+ require_relative "semantic_analyzer/statements"
10
+ require_relative "semantic_analyzer/expressions"
11
+ require_relative "semantic_analyzer/calls"
12
+ require_relative "semantic_analyzer/analysis_context"
13
+ require_relative "semantic_analyzer/type_compatibility"
14
+ require_relative "semantic_analyzer/flow_refinement"
15
+ require_relative "semantic_analyzer/name_resolution"
16
+ require_relative "semantic_analyzer/generics"
17
+ require_relative "semantic_analyzer/foreign_functions"
18
+ require_relative "semantic_analyzer/module_context"
19
+
20
+ module MilkTea
21
+ class SemanticError < StandardError
22
+ attr_reader :line, :column, :length, :path, :suggestion
23
+
24
+ def initialize(msg = nil, line: nil, column: nil, length: nil, path: nil, suggestion: nil)
25
+ super(msg)
26
+ @line = line
27
+ @column = column
28
+ @length = length
29
+ @path = path
30
+ @suggestion = suggestion
31
+ end
32
+
33
+ def code
34
+ "sema/error"
35
+ end
36
+
37
+ def to_diagnostic(path: nil)
38
+ Diagnostic.new(
39
+ path:,
40
+ line: @line,
41
+ column: @column,
42
+ length: @length,
43
+ code: "sema/error",
44
+ message: message,
45
+ severity: :error,
46
+ )
47
+ end
48
+ end
49
+
50
+ class SemanticAnalyzer
51
+ Analysis = Data.define(:ast, :module_name, :module_kind, :directives, :imports, :types, :interfaces, :attributes, :attribute_applications, :values, :functions, :methods, :implemented_interfaces, :local_completion_frames, :binding_resolution, :callable_value_identifier_sites, :callable_value_member_access_sites, :required_unsafe_lines, :uses_parallel_for, :resolved_expr_types, :resolved_call_kinds, :const_values) do
52
+ def initialize(ast:, module_name:, module_kind:, directives:, imports:, types:, interfaces:, attributes:, attribute_applications:, values:, functions:, methods:, implemented_interfaces:, local_completion_frames:, binding_resolution:, callable_value_identifier_sites:, callable_value_member_access_sites:, required_unsafe_lines:, uses_parallel_for:, resolved_expr_types: {}, resolved_call_kinds: {}, const_values: {}) = super
53
+ end
54
+ ToolingSnapshot = Data.define(:facts, :diagnostics)
55
+ LocalCompletionFrame = Data.define(:start_line, :end_line, :function_name, :receiver_type, :snapshots)
56
+ LocalCompletionSnapshot = Data.define(:line, :column, :bindings)
57
+ BindingResolution = Data.define(
58
+ :identifier_binding_ids,
59
+ :declaration_binding_ids,
60
+ :mutating_argument_identifier_ids,
61
+ :editable_receiver_expression_ids,
62
+ :mutable_lvalue_argument_identifier_ids,
63
+ :binding_types,
64
+ )
65
+ InterfaceMethodBinding = Data.define(:name, :params, :return_type, :kind, :async, :ast)
66
+ InterfaceBinding = Data.define(:name, :methods, :ast, :module_name, :type_arguments) do
67
+ def initialize(name:, methods:, ast:, module_name:, type_arguments: nil) = super
68
+ end
69
+
70
+ GenericInterfaceBinding = Data.define(:name, :type_params, :type_param_constraints, :methods, :ast, :module_name) do
71
+ def instantiate(arguments)
72
+ raise ArgumentError, "#{name} expects #{type_params.length} type arguments, got #{arguments.length}" unless arguments.length == type_params.length
73
+
74
+ substitutions = type_params.zip(arguments).to_h
75
+ substituted_methods = methods.transform_values do |method|
76
+ InterfaceMethodBinding.new(
77
+ name: method.name,
78
+ params: method.params.map { |p| Types::Registry.parameter(p.name, Types.substitute_type_variables(p.type, substitutions)) },
79
+ return_type: Types.substitute_type_variables(method.return_type, substitutions),
80
+ kind: method.kind,
81
+ async: method.async,
82
+ ast: method.ast,
83
+ )
84
+ end
85
+
86
+ InterfaceBinding.new(
87
+ name:,
88
+ methods: substituted_methods.freeze,
89
+ ast:,
90
+ module_name:,
91
+ type_arguments: arguments.freeze,
92
+ )
93
+ end
94
+ end
95
+ ResolvedAttributeApplication = Data.define(:binding, :argument_values)
96
+ AttributePresenceKey = Data.define(:target, :attribute_module_name, :attribute_name)
97
+ TypeParamConstraintBinding = Data.define(:interfaces) do
98
+ def initialize(interfaces: []) = super
99
+ end
100
+ DefaultResolution = Data.define(:target_type, :binding)
101
+ HashResolution = Data.define(:target_type, :binding)
102
+ EqualResolution = Data.define(:target_type, :binding)
103
+ OrderResolution = Data.define(:target_type, :binding)
104
+
105
+ INSTALLABLE_BUILTIN_TYPE_NAMES = (MilkTea::BUILTIN_PRIMITIVE_NAMES + %w[
106
+ Subscription EventError
107
+ struct_handle field_handle callable_handle attribute_handle member_handle type
108
+ ]).freeze
109
+
110
+ def self.check(ast, imported_modules: {}, allow_missing_imports: false, path: nil, global_import_index: {})
111
+ Checker.new(ast, imported_modules:, allow_missing_imports:, path:, global_import_index:).check
112
+ end
113
+
114
+ # LSP-oriented entry point: runs all sema phases and collects every error
115
+ # instead of stopping at the first one. Structural phases collect per-
116
+ # declaration, and function-body phases collect per function/method.
117
+ # Returns { analysis: Analysis|nil, errors: [SemanticError] }.
118
+ def self.check_collecting_errors(ast, imported_modules: {}, allow_missing_imports: false, path: nil)
119
+ Checker.new(ast, imported_modules:, allow_missing_imports:, path:).check_collecting_errors
120
+ rescue SemanticError => e
121
+ { analysis: nil, errors: [e] }
122
+ end
123
+
124
+ def self.tooling_snapshot(ast, imported_modules: {}, allow_missing_imports: false, path: nil)
125
+ result = check_collecting_errors(ast, imported_modules:, allow_missing_imports:, path:)
126
+ diagnostics = Array(result[:errors]).map { |error| error.to_diagnostic(path:) }.freeze
127
+
128
+ ToolingSnapshot.new(facts: result[:analysis], diagnostics:)
129
+ end
130
+
131
+ class Checker
132
+ include CompatibilityHelpers
133
+
134
+ attr_reader :ctx
135
+
136
+ def module_name
137
+ @ctx.module_name
138
+ end
139
+
140
+ def initialize(ast, imported_modules: {}, allow_missing_imports: false, path: nil, global_import_index: {})
141
+ @path = path
142
+ @allow_missing_imports = allow_missing_imports
143
+ @ctx = ModuleContext.new(
144
+ ast:,
145
+ module_name: ast.module_name&.to_s,
146
+ module_kind: ast.module_kind,
147
+ imported_modules:,
148
+ global_import_index:,
149
+ const_declarations: ast.declarations.grep(AST::ConstDecl).each_with_object({}) { |decl, result| result[decl.name] = decl },
150
+ )
151
+ @null_type = Types::Null.new
152
+ @error_type = Types::Error.new
153
+ @loop_depth = 0
154
+ @unsafe_depth = 0
155
+ @compile_time_depth = 0
156
+ @foreign_mapping_depth = 0
157
+ @async_function_depth = 0
158
+ @checked_function_bindings = {}
159
+ @checking_function_bindings = {}
160
+ @evaluating_const_values = []
161
+ @evaluated_const_values = {}
162
+ @error_node_stack = []
163
+ @local_completion_frames = []
164
+ @active_local_completion_stack = []
165
+ @resolved_expr_types = {}
166
+ @resolved_call_kinds = {}
167
+ @const_values = {}
168
+ @next_binding_id = 1
169
+ @binding_name_by_id = {}
170
+ @binding_type_by_id = {}
171
+ @identifier_binding_ids = {}
172
+ @declaration_binding_ids = {}
173
+ @mutating_argument_identifier_ids = {}
174
+ @mutable_lvalue_argument_identifier_ids = {}
175
+ @editable_receiver_expression_ids = {}
176
+ @preassigned_local_binding_ids = {}
177
+ @nullability_flow_result = nil
178
+ @unsafe_statement_lines = []
179
+ @callable_value_identifier_sites = {}
180
+ @callable_value_member_access_sites = {}
181
+ @required_unsafe_lines = []
182
+ @uses_parallel_for = false
183
+ @current_specialization_owner = nil
184
+ @return_context_stack = []
185
+ end
186
+
187
+ STRUCTURAL_PHASES = %i[
188
+ install_builtin_types
189
+ install_builtin_attributes
190
+ install_imports
191
+ install_prelude_types
192
+ declare_named_types
193
+ resolve_generic_type_param_constraints
194
+ resolve_type_aliases
195
+ declare_attributes
196
+ resolve_aggregate_fields
197
+ resolve_enum_members
198
+ resolve_variant_arms
199
+ collect_emit_declarations
200
+ declare_top_level_values
201
+ check_attribute_applications
202
+ declare_functions
203
+ check_interface_conformances
204
+ ].freeze
205
+
206
+ def check
207
+ STRUCTURAL_PHASES.each { |phase| send(phase) }
208
+ check_top_level_values
209
+ finalize_top_level_const_values
210
+ check_top_level_static_asserts
211
+ check_functions
212
+
213
+ build_analysis
214
+ end
215
+
216
+ def collect_emit_declarations
217
+ collect_emit_from_declarations(expanded_declarations)
218
+ @ctx.ast.declarations.grep(AST::ConstDecl).each { |decl| @ctx.const_declarations[decl.name] ||= decl }
219
+ end
220
+
221
+ def collect_emit_from_declarations(declarations)
222
+ declarations.each do |decl|
223
+ case decl
224
+ when AST::FunctionDef
225
+ next unless decl.const && decl.body
226
+
227
+ collect_emit_from_statements(decl.body)
228
+ when AST::WhenStmt
229
+ body = when_chosen_body(decl)
230
+ collect_emit_from_declarations(body) if body
231
+ when AST::StructDecl, AST::ExtendingBlock
232
+ # no emit in structs/extending blocks
233
+ end
234
+ end
235
+ end
236
+
237
+ def collect_emit_from_statements(statements)
238
+ statements.each do |stmt|
239
+ case stmt
240
+ when AST::EmitStmt
241
+ emit_decl = stmt.declaration
242
+ next if emit_decl.is_a?(AST::ErrorExpr)
243
+
244
+ @ctx.ast.declarations << emit_decl
245
+ collect_emit_from_node(emit_decl)
246
+ when AST::WhenStmt
247
+ body = when_chosen_body(stmt) || []
248
+ body.each { |nested| collect_emit_from_statements([nested]) }
249
+ when AST::ForStmt, AST::WhileStmt, AST::IfStmt, AST::MatchStmt
250
+ next unless stmt.inline
251
+ stmt.body&.each { |s| collect_emit_from_statements([s]) }
252
+ if stmt.is_a?(AST::IfStmt)
253
+ stmt.else_body&.each { |s| collect_emit_from_statements([s]) }
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ def collect_emit_from_node(node)
260
+ case node
261
+ when AST::FunctionDef
262
+ node.body&.each do |stmt|
263
+ if stmt.is_a?(AST::EmitStmt)
264
+ nested = stmt.declaration
265
+ next if nested.is_a?(AST::ErrorExpr)
266
+
267
+ @ctx.ast.declarations << nested
268
+ collect_emit_from_node(nested)
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ # Like check, but collects per-function errors instead of raising at first.
275
+ # Structural phases (imports, type resolution, declaration) collect errors per
276
+ # declaration so that the maximum number of diagnostics are surfaced.
277
+ # Returns { analysis: Analysis, errors: [SemanticError] }.
278
+ def check_collecting_errors
279
+ @collecting_errors = true
280
+ @structural_errors = []
281
+
282
+ STRUCTURAL_PHASES.each { |phase| catch_structural { send(phase) } }
283
+
284
+ errors = @structural_errors.dup
285
+
286
+ begin
287
+ check_top_level_values
288
+ rescue SemanticError => e
289
+ errors << e
290
+ end
291
+ errors.concat(@structural_errors.drop(errors.length))
292
+
293
+ begin
294
+ finalize_top_level_const_values
295
+ rescue SemanticError => e
296
+ errors << e
297
+ end
298
+
299
+ begin
300
+ check_top_level_static_asserts
301
+ rescue SemanticError => e
302
+ errors << e
303
+ end
304
+
305
+ check_functions_collecting(errors)
306
+
307
+ analysis = build_analysis
308
+
309
+ { analysis: analysis, errors: errors.uniq { |e| [e.message, e.line, e.column, e.length] } }
310
+ end
311
+
312
+ def catch_structural
313
+ yield
314
+ rescue SemanticError => e
315
+ @structural_errors << e
316
+ end
317
+
318
+ def collect_structural_error(error)
319
+ raise error unless @collecting_errors
320
+
321
+ @structural_errors << error
322
+ end
323
+
324
+
325
+ end
326
+ end
327
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ TriviaToken = Data.define(:kind, :text, :line, :column, :start_offset, :end_offset)
5
+
6
+ class Token < Data.define(:type, :lexeme, :literal, :line, :column, :start_offset, :end_offset, :leading_trivia, :trailing_trivia)
7
+ KEYWORDS = MilkTea::KEYWORDS
8
+
9
+ ASSIGNMENT_TYPES = %i[
10
+ equal plus_equal minus_equal star_equal slash_equal percent_equal
11
+ amp_equal pipe_equal caret_equal shift_left_equal shift_right_equal
12
+ ].freeze
13
+
14
+ def assignment?
15
+ ASSIGNMENT_TYPES.include?(type)
16
+ end
17
+
18
+ def eof?
19
+ type == :eof
20
+ end
21
+
22
+ def with_appended_trailing_trivia(trivia)
23
+ with(trailing_trivia: trailing_trivia + [trivia])
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SyntaxTokenStream
5
+ def initialize(tokens)
6
+ @tokens = tokens
7
+ end
8
+
9
+ def [](index)
10
+ @tokens[index]
11
+ end
12
+
13
+ def length
14
+ @tokens.length
15
+ end
16
+
17
+ def to_a
18
+ @tokens
19
+ end
20
+ end
21
+
22
+ class TriviaTokenStream
23
+ attr_reader :tokens, :trivia
24
+
25
+ def initialize(tokens, trivia)
26
+ @tokens = tokens
27
+ @trivia = trivia
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiddle"
4
+ require_relative "types"
5
+
6
+ module MilkTea
7
+ module Layout
8
+ module_function
9
+
10
+ POINTER_SIZE = Fiddle::SIZEOF_VOIDP
11
+
12
+ def size_of(type)
13
+ size_and_alignment(type, {})&.first
14
+ end
15
+
16
+ def alignment_of(type)
17
+ size_and_alignment(type, {})&.last
18
+ end
19
+
20
+ def offset_of(type, field_name)
21
+ case type
22
+ when Types::Struct, Types::StructInstance, Types::Span, Types::StringView, Types::Task
23
+ fields = ordered_fields(type)
24
+ return unless fields.key?(field_name)
25
+
26
+ struct_layout(fields, packed: packed_layout?(type), alignment: explicit_alignment(type), stack: {})[:offsets][field_name]
27
+ when Types::Union
28
+ type.field(field_name) ? 0 : nil
29
+ when Types::GenericInstance
30
+ return unless str_buffer_type?(type)
31
+
32
+ fields = str_buffer_fields(type)
33
+ return unless fields.key?(field_name)
34
+
35
+ struct_layout(fields, packed: false, alignment: nil, stack: {})[:offsets][field_name]
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def size_and_alignment(type, stack)
42
+ case type
43
+ when Types::Primitive
44
+ primitive_layout(type)
45
+ when Types::EnumBase
46
+ size_and_alignment(type.backing_type, stack)
47
+ when Types::Nullable, Types::Function
48
+ if type.is_a?(Types::Function) || layout_pointer_like_nullable_base?(type.base)
49
+ [POINTER_SIZE, POINTER_SIZE]
50
+ else
51
+ with_stack(type, stack) do
52
+ layout = struct_layout(nullable_opt_layout_fields(type.base), packed: false, alignment: nil, stack:)
53
+ [layout[:size], layout[:alignment]]
54
+ end
55
+ end
56
+ when Types::StringView, Types::Span, Types::Task, Types::Struct, Types::StructInstance
57
+ with_stack(type, stack) do
58
+ layout = struct_layout(ordered_fields(type), packed: packed_layout?(type), alignment: explicit_alignment(type), stack:)
59
+ [layout[:size], layout[:alignment]]
60
+ end
61
+ when Types::Union
62
+ with_stack(type, stack) do
63
+ layout = union_layout(ordered_fields(type), packed: packed_layout?(type), alignment: explicit_alignment(type), stack:)
64
+ [layout[:size], layout[:alignment]]
65
+ end
66
+ when Types::Variant, Types::VariantInstance
67
+ with_stack(type, stack) do
68
+ layout = variant_layout(type, stack:)
69
+ [layout[:size], layout[:alignment]]
70
+ end
71
+ when Types::GenericInstance
72
+ generic_layout(type, stack)
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ def primitive_layout(type)
79
+ case type.name
80
+ when "bool", "byte", "ubyte", "char"
81
+ [1, 1]
82
+ when "short", "ushort"
83
+ [2, 2]
84
+ when "int", "uint", "float"
85
+ [4, 4]
86
+ when "long", "ulong", "double"
87
+ [8, 8]
88
+ when "ptr_int", "ptr_uint", "cstr"
89
+ [POINTER_SIZE, POINTER_SIZE]
90
+ else
91
+ nil
92
+ end
93
+ end
94
+
95
+ def generic_layout(type, stack)
96
+ case type.name
97
+ when "ptr", "const_ptr", "own", "ref"
98
+ [POINTER_SIZE, POINTER_SIZE]
99
+ when "array"
100
+ return unless array_type?(type)
101
+
102
+ element_layout = size_and_alignment(type.arguments.first, stack)
103
+ return unless element_layout
104
+
105
+ [element_layout.first * type.arguments[1].value, element_layout.last]
106
+ when "str_buffer"
107
+ return unless str_buffer_type?(type)
108
+
109
+ with_stack(type, stack) do
110
+ layout = struct_layout(str_buffer_fields(type), packed: false, alignment: nil, stack:)
111
+ [layout[:size], layout[:alignment]]
112
+ end
113
+ end
114
+ end
115
+
116
+ def variant_layout(type, stack:)
117
+ payload_layouts = type.arm_names.filter_map do |arm_name|
118
+ fields = type.arm(arm_name)
119
+ next if fields.nil? || fields.empty?
120
+
121
+ struct_layout(fields, packed: false, alignment: nil, stack:)
122
+ end
123
+
124
+ data_layout = union_layout_from_layouts(payload_layouts, packed: false, alignment: nil)
125
+ field_infos = [["kind", 4, 4]]
126
+ field_infos << ["data", data_layout[:size], data_layout[:alignment]] if data_layout[:size].positive?
127
+ struct_layout_from_infos(field_infos, packed: false, alignment: nil)
128
+ end
129
+
130
+ def struct_layout(fields, packed:, alignment:, stack:)
131
+ field_infos = fields.map do |field_name, field_type|
132
+ field_layout = size_and_alignment(field_type, stack)
133
+ return nil unless field_layout
134
+
135
+ [field_name, field_layout.first, field_layout.last]
136
+ end
137
+
138
+ struct_layout_from_infos(field_infos, packed:, alignment:)
139
+ end
140
+
141
+ def union_layout(fields, packed:, alignment:, stack:)
142
+ field_infos = fields.map do |_field_name, field_type|
143
+ field_layout = size_and_alignment(field_type, stack)
144
+ return nil unless field_layout
145
+
146
+ field_layout
147
+ end
148
+
149
+ union_layout_from_layouts(field_infos.map { |size, field_alignment| { size:, alignment: field_alignment } }, packed:, alignment:)
150
+ end
151
+
152
+ def struct_layout_from_infos(field_infos, packed:, alignment:)
153
+ offsets = {}
154
+ offset = 0
155
+ natural_alignment = 1
156
+
157
+ field_infos.each do |field_name, field_size, field_alignment|
158
+ effective_alignment = packed ? 1 : field_alignment
159
+ natural_alignment = [natural_alignment, effective_alignment].max
160
+ offset = align_up(offset, effective_alignment) unless packed
161
+ offsets[field_name] = offset
162
+ offset += field_size
163
+ end
164
+
165
+ overall_alignment = [natural_alignment, alignment || 1].max
166
+ {
167
+ size: align_up(offset, overall_alignment),
168
+ alignment: overall_alignment,
169
+ offsets:,
170
+ }
171
+ end
172
+
173
+ def union_layout_from_layouts(layouts, packed:, alignment:)
174
+ natural_alignment = packed ? 1 : (layouts.map { |layout| layout[:alignment] }.max || 1)
175
+ overall_alignment = [natural_alignment, alignment || 1].max
176
+ size = layouts.map { |layout| layout[:size] }.max || 0
177
+
178
+ {
179
+ size: align_up(size, overall_alignment),
180
+ alignment: overall_alignment,
181
+ }
182
+ end
183
+
184
+ def ordered_fields(type)
185
+ type.fields
186
+ end
187
+
188
+ def packed_layout?(type)
189
+ type.respond_to?(:packed) && type.packed
190
+ end
191
+
192
+ def explicit_alignment(type)
193
+ type.respond_to?(:alignment) ? type.alignment : nil
194
+ end
195
+
196
+ def array_type?(type)
197
+ type.arguments.length == 2 && type.arguments[1].is_a?(Types::LiteralTypeArg) && type.arguments[1].value.is_a?(Integer)
198
+ end
199
+
200
+ def str_buffer_type?(type)
201
+ type.arguments.length == 1 && type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
202
+ end
203
+
204
+ def str_buffer_fields(type)
205
+ storage_capacity = type.arguments.first.value + 1
206
+ {
207
+ "data" => Types::Registry.generic_instance("array", [Types::Registry.primitive("char"), Types::LiteralTypeArg.new(storage_capacity)]),
208
+ "len" => Types::Registry.primitive("ptr_uint"),
209
+ "dirty" => Types::Registry.primitive("bool"),
210
+ }
211
+ end
212
+
213
+ def layout_pointer_like_nullable_base?(base)
214
+ return true if base.is_a?(Types::Function) || base.is_a?(Types::Proc) || base.is_a?(Types::Opaque)
215
+ return true if base.is_a?(Types::Primitive) && base.name == "cstr"
216
+
217
+ base.is_a?(Types::GenericInstance) && %w[ptr const_ptr own ref].include?(base.name)
218
+ end
219
+
220
+ def nullable_opt_layout_fields(base)
221
+ {
222
+ "has_value" => Types::Registry.primitive("bool"),
223
+ "value" => base,
224
+ }
225
+ end
226
+
227
+ def align_up(value, alignment)
228
+ return value if alignment <= 1
229
+
230
+ remainder = value % alignment
231
+ remainder.zero? ? value : value + alignment - remainder
232
+ end
233
+
234
+ def with_stack(type, stack)
235
+ return if stack[type]
236
+
237
+ stack[type] = true
238
+ yield
239
+ ensure
240
+ stack.delete(type)
241
+ end
242
+ end
243
+ end