mt-lang 0.3.34 → 0.3.38
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/docs/index.html +5 -5
- data/docs/language-design.md +3 -3
- data/docs/language-manual.md +1 -1
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +12 -2
- data/lib/milk_tea/bindings/bindgen.rb +5 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +4 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +25 -3
- data/lib/milk_tea/core/c_backend/reinterpret.rb +2 -2
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +15 -2
- data/lib/milk_tea/core/c_backend/type_collectors.rb +39 -2
- data/lib/milk_tea/core/c_backend.rb +12 -0
- data/lib/milk_tea/core/compile_time.rb +109 -74
- data/lib/milk_tea/core/lexer.rb +12 -0
- data/lib/milk_tea/core/lowering/block.rb +6 -7
- data/lib/milk_tea/core/lowering/declarations.rb +40 -1
- data/lib/milk_tea/core/lowering/loops.rb +25 -1
- data/lib/milk_tea/core/lowering/resolve.rb +13 -6
- data/lib/milk_tea/core/lowering/utils.rb +6 -2
- data/lib/milk_tea/core/lowering.rb +12 -0
- data/lib/milk_tea/core/module_loader.rb +59 -37
- data/lib/milk_tea/core/parser/statements.rb +35 -7
- data/lib/milk_tea/core/parser.rb +12 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +4 -1
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +2 -1
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +8 -20
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +2 -1
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +12 -2
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +10 -10
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +14 -5
- data/lib/milk_tea/core/semantic_analyzer.rb +6 -31
- data/lib/milk_tea/lsp/server/code_actions.rb +12 -2
- data/lib/milk_tea/lsp/server/formatting.rb +172 -58
- data/lib/milk_tea/lsp/server/inlay_hints.rb +33 -11
- data/lib/milk_tea/lsp/server/selection_range.rb +4 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +9 -5
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +5 -5
- data/lib/milk_tea/packages/manifest.rb +9 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +4 -1
- data/std/box2d.mt +14 -14
- data/std/c/box2d.mt +19 -19
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 952280dd2ad14bc3b5751fc7e8a3d29b39363293b1436cc1b3b546c31c938427
|
|
4
|
+
data.tar.gz: e0bdf3468681b07914143c32b0b9cc2470cdbeaf55cf81994186003dc406261b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9dcf6f6c955f1428b5374315782fda121669ae3c12a8c84d683205cb44bb4ca7903465e70447860e89c0753695f01af513903e93b82987838fa724be19fc7b2f
|
|
7
|
+
data.tar.gz: 67c259de17bdd81a4f5785d7dbe05cf39ab959ea51c1f27ed2b05a516ed97ce91a4b2291aa415e0abaa695337f1fc1825ead4acdfceb480a9be4db3cd3a24dd4
|
data/README.md
CHANGED
|
@@ -939,7 +939,7 @@ See module source for full method surface. Iterator forms:
|
|
|
939
939
|
|
|
940
940
|
Text categories:
|
|
941
941
|
|
|
942
|
-
- `str` -> string view. The `+` operator concatenates two `str` values
|
|
942
|
+
- `str` -> string view. The `+` operator concatenates two `str` values into a per-thread scratch buffer (no heap allocation), returning a borrowed `str`; the result stays valid while cumulative concatenations on that thread remain within the scratch buffer budget. For loops or repeated concatenation, prefer `string.String` for amortized building.
|
|
943
943
|
- `cstr` -> C ABI string
|
|
944
944
|
- `str_buffer[N]` -> fixed-capacity mutable UTF-8 text buffer
|
|
945
945
|
|
|
@@ -959,7 +959,7 @@ Format strings:
|
|
|
959
959
|
|
|
960
960
|
- `f"count=#{count}"` has type `str`.
|
|
961
961
|
- Allowed interpolations: `str`, `cstr`, `bool`, numeric primitives, integer-backed enums and flags, plus types implementing `format_len() -> ptr_uint` and `append_format(output: ref[std.string.String]) -> void`.
|
|
962
|
-
- `f"..."`
|
|
962
|
+
- Dynamic `f"..."` expressions build into a heap-backed temporary that the compiler releases after use. A dynamic `f"..."` may be returned from a function (or stored in a `str` local): ownership of that buffer transfers to the caller. `std.fmt.format(f"...")` returns an owned `string.String` when you want explicit owned-text lifetime management.
|
|
963
963
|
- Float and double interpolations support `:.N` precision.
|
|
964
964
|
- Integer primitive and integer-backed enum/flags interpolations support `:x` (lowercase hex) and `:X` (uppercase hex).
|
|
965
965
|
- Integer primitive and integer-backed enum/flags interpolations support `:o` / `:O` (octal) and `:b` / `:B` (binary).
|
data/docs/index.html
CHANGED
|
@@ -1262,7 +1262,7 @@ let d: dyn[Drawable] = adapt[Drawable](ref_of(entity))</code></pre>
|
|
|
1262
1262
|
<tr><td><code>ptr_int</code> <code>ptr_uint</code></td><td>Pointer-sized integers</td></tr>
|
|
1263
1263
|
<tr><td><code>float</code> <code>double</code></td><td>Floating-point</td></tr>
|
|
1264
1264
|
<tr><td><code>void</code></td><td>No value</td></tr>
|
|
1265
|
-
<tr><td><code>str</code></td><td>UTF-8 string view (borrowed). The <code>+</code> operator concatenates two <code>str</code> values
|
|
1265
|
+
<tr><td><code>str</code></td><td>UTF-8 string view (borrowed). The <code>+</code> operator concatenates two <code>str</code> values into a per-thread scratch buffer (no allocation); the result is valid while the scratch budget lasts.</td></tr>
|
|
1266
1266
|
<tr><td><code>cstr</code></td><td>NUL-terminated C string</td></tr>
|
|
1267
1267
|
<tr><td><code>vec2</code> <code>vec3</code> <code>vec4</code></td><td>Float vectors with <code>.x .y .z .w</code></td></tr>
|
|
1268
1268
|
<tr><td><code>ivec2</code> <code>ivec3</code> <code>ivec4</code></td><td>Integer vectors</td></tr>
|
|
@@ -1871,7 +1871,7 @@ function field_equal[T](a: const_ptr[T], b: const_ptr[T]) -> bool:
|
|
|
1871
1871
|
<div class="table-wrap">
|
|
1872
1872
|
<table class="attr-table">
|
|
1873
1873
|
<tr><th>Type</th><th>Ownership</th><th>Use Case</th></tr>
|
|
1874
|
-
<tr><td><code>str</code></td><td>Borrowed</td><td>Read-only UTF-8 view (literals, format strings, slicing)</td></tr>
|
|
1874
|
+
<tr><td><code>str</code></td><td>Borrowed</td><td>Read-only UTF-8 view (literals, format strings, slicing); a stored or returned dynamic format string carries its own heap buffer</td></tr>
|
|
1875
1875
|
<tr><td><code>cstr</code></td><td>Borrowed</td><td>NUL-terminated C ABI string (<code>c"hello"</code>)</td></tr>
|
|
1876
1876
|
<tr><td><code>str_buffer[N]</code></td><td>Owned (stack)</td><td>Fixed-capacity mutable UTF-8 builder</td></tr>
|
|
1877
1877
|
<tr><td><code>std.string.String</code></td><td>Owned (heap)</td><td>Growable owned text via <code>fmt.format(f"...")</code></td></tr>
|
|
@@ -1889,9 +1889,9 @@ let info = f"value=#{pi:.4}"
|
|
|
1889
1889
|
## Hex format for ints
|
|
1890
1890
|
let hex = f"address=#{ptr_value:x}"
|
|
1891
1891
|
|
|
1892
|
-
## Owned text
|
|
1892
|
+
## Owned text with explicit release
|
|
1893
1893
|
import std.fmt
|
|
1894
|
-
let owned = fmt.format(f"count=#{count}") ## -> string.String</code></pre>
|
|
1894
|
+
let owned = fmt.format(f"count=#{count}") ## -> string.String (call .release())</code></pre>
|
|
1895
1895
|
</div>
|
|
1896
1896
|
<p>Interpolated expressions must be <code>str</code>, <code>cstr</code>, <code>bool</code>, a numeric primitive, an integer-backed enum or flags type, or a type implementing <code>format_len()</code> and <code>append_format()</code> (custom formatting hooks). The compiler lowers <code>fmt.format(f"...")</code>, <code>str_buffer.append_format(f"...")</code>, and <code>string.String.append_format(f"...")</code> directly to the formatted output without an intermediate allocation.</p>
|
|
1897
1897
|
|
|
@@ -2242,7 +2242,7 @@ function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
|
2242
2242
|
<tr><td><code>std.gzip</code> <code>std.tar</code></td><td>Compression</td></tr>
|
|
2243
2243
|
<tr><td><code>std.sync</code> <code>std.thread</code> <code>std.jobs</code></td><td>Concurrency</td></tr>
|
|
2244
2244
|
<tr><td><code>std.fsm</code> <code>std.goap</code> <code>std.behavior_tree</code></td><td>AI / State machines</td></tr>
|
|
2245
|
-
<tr><td><code>std.
|
|
2245
|
+
<tr><td><code>std.box</code></td><td>Explicit single-value heap storage for shared mutable state</td></tr>
|
|
2246
2246
|
</table>
|
|
2247
2247
|
</div>
|
|
2248
2248
|
</section>
|
data/docs/language-design.md
CHANGED
|
@@ -28,7 +28,7 @@ The output target is beautiful C. The generated C should be readable enough that
|
|
|
28
28
|
- No macro system that rewrites arbitrary ASTs
|
|
29
29
|
- No garbage collector
|
|
30
30
|
- No implicit conversions between unrelated primitive types
|
|
31
|
-
- No user-invisible allocation for
|
|
31
|
+
- No user-invisible allocation for collections, ordinary values, or method calls. Capturing a `proc` may allocate a ref-counted closure environment as part of proc value semantics. Dynamic format strings are the one text case that builds a heap-backed temporary, released after use (see the text-construction rule in §1); other owned text and storage use explicit allocating surfaces.
|
|
32
32
|
|
|
33
33
|
## Design rules
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ If code allocates, takes an address, dereferences a raw pointer, performs an FFI
|
|
|
38
38
|
|
|
39
39
|
FFI visibility belongs at the declaration site. Raw `external` files expose exact ABI types. Imported foreign declarations may project those raw types into ordinary Milk Tea types, but the projection rule, temporary-storage rule, and ownership rule must be declared there instead of repeated at every call site.
|
|
40
40
|
|
|
41
|
-
The same rule applies to text construction. Plain string literals
|
|
41
|
+
The same rule applies to text construction. Plain string literals are borrowed `str` values. A dynamic format string `f"..."` builds into a heap-backed temporary that the compiler releases after the surrounding statement; returning it (or storing it in a `str` local) transfers ownership of that buffer to the caller. The `+` operator on `str` concatenates into a per-thread scratch buffer without allocating — the result is a borrowed `str` valid until that buffer's budget is exhausted. For loops or amortized building, and whenever ownership should stay explicit and controllable, `string.String` and `str_buffer[N]` remain the explicit surfaces with visible cost; `std.fmt.format(f"...")` is the explicit owned-text path returning a `string.String`.
|
|
42
42
|
|
|
43
43
|
### 2. C is the ABI ground truth
|
|
44
44
|
|
|
@@ -762,7 +762,7 @@ Built-in operators should match familiar C behavior where possible:
|
|
|
762
762
|
|
|
763
763
|
No user-defined operator overloading.
|
|
764
764
|
|
|
765
|
-
The `+` operator also concatenates two `str` values
|
|
765
|
+
The `+` operator also concatenates two `str` values into a per-thread scratch buffer, returning a borrowed `str`; `cstr` values are not concatenable. This is the one operator exception to the arithmetic rules, and it is described in the text-construction rule under Design rules §1.
|
|
766
766
|
|
|
767
767
|
Built-in vector, matrix, and quaternion types support component-wise arithmetic with the standard operators:
|
|
768
768
|
|
data/docs/language-manual.md
CHANGED
|
@@ -1072,7 +1072,7 @@ Rules:
|
|
|
1072
1072
|
11. `+`, `-`
|
|
1073
1073
|
12. `*`, `/`, `%`
|
|
1074
1074
|
|
|
1075
|
-
The `+` operator also concatenates two `str` operands, producing a
|
|
1075
|
+
The `+` operator also concatenates two `str` operands into a per-thread scratch buffer, producing a borrowed `str` (no heap allocation) whose validity is bounded by the scratch buffer budget (§2.3). It does not concatenate `cstr` or mixed `str`/`cstr` operands.
|
|
1076
1076
|
|
|
1077
1077
|
### 5.4 Assignment operators
|
|
1078
1078
|
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -22,8 +22,14 @@ module MilkTea
|
|
|
22
22
|
"cstr"
|
|
23
23
|
else
|
|
24
24
|
pointee = pointer_candidate.sub(/\s*\*\z/, "")
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if function_type_typedef?(pointee)
|
|
26
|
+
# `Name *` where Name is a function-type typedef is a function
|
|
27
|
+
# pointer, which is exactly what the `fn` type already is.
|
|
28
|
+
map_c_type(pointee, context:)
|
|
29
|
+
else
|
|
30
|
+
pointer_name = top_level_const_qualified?(pointee) ? "const_ptr" : "ptr"
|
|
31
|
+
"#{pointer_name}[#{map_c_type(pointee, context:)}]"
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
else
|
|
29
35
|
unqualified = strip_qualifiers(normalized)
|
|
@@ -515,6 +521,10 @@ module MilkTea
|
|
|
515
521
|
qual_type.end_with?("*")
|
|
516
522
|
end
|
|
517
523
|
|
|
524
|
+
def function_type_typedef?(qual_type)
|
|
525
|
+
@function_type_typedef_names.include?(strip_qualifiers(qual_type))
|
|
526
|
+
end
|
|
527
|
+
|
|
518
528
|
def c_string_pointer?(qual_type)
|
|
519
529
|
pointee = qual_type.sub(/\s*\*\z/, "")
|
|
520
530
|
unqualified = strip_qualifiers(pointee)
|
|
@@ -147,6 +147,11 @@ module MilkTea
|
|
|
147
147
|
@visible_typedef_names = top_level_nodes.filter_map do |node|
|
|
148
148
|
node["name"] if node["kind"] == "TypedefDecl" && allowed_declaration_name?(node["name"])
|
|
149
149
|
end
|
|
150
|
+
@function_type_typedef_names = top_level_nodes.filter_map do |node|
|
|
151
|
+
if node["kind"] == "TypedefDecl" && allowed_declaration_name?(node["name"]) && extract_function_proto(node) && !function_pointer_type?(type_qual_type(node))
|
|
152
|
+
node["name"]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
150
155
|
build_alias_maps(top_level_nodes)
|
|
151
156
|
|
|
152
157
|
declarations = []
|
|
@@ -139,6 +139,8 @@ module MilkTea
|
|
|
139
139
|
end
|
|
140
140
|
when Types::Task
|
|
141
141
|
[task_type_name(type)]
|
|
142
|
+
when Types::Dyn
|
|
143
|
+
[dyn_type_name(type)]
|
|
142
144
|
when Types::Proc
|
|
143
145
|
[proc_type_name(type)]
|
|
144
146
|
when Types::GenericInstance
|
|
@@ -153,6 +155,8 @@ module MilkTea
|
|
|
153
155
|
end
|
|
154
156
|
when Types::Function
|
|
155
157
|
[]
|
|
158
|
+
when Types::Tuple
|
|
159
|
+
[tuple_type_name(type)]
|
|
156
160
|
when Types::Struct, Types::StructInstance, Types::Union, Types::Variant, Types::VariantInstance, Types::Event, Types::Subscription
|
|
157
161
|
[named_type_c_name(type)]
|
|
158
162
|
when Types::VariantArmPayload
|
|
@@ -347,7 +347,22 @@ module MilkTea
|
|
|
347
347
|
def emit_address_of_operand(expression)
|
|
348
348
|
return emit_expression(expression.operand) if expression.is_a?(IR::Unary) && expression.operator == "*"
|
|
349
349
|
|
|
350
|
-
"&#{wrap_expression(expression)}"
|
|
350
|
+
return "&#{wrap_expression(expression)}" if c_expression_lvalue?(expression)
|
|
351
|
+
|
|
352
|
+
"&(#{c_type(expression.type)}[1]){ #{emit_expression(expression)} }[0]"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def c_expression_lvalue?(expression)
|
|
356
|
+
case expression
|
|
357
|
+
when IR::Name, IR::CheckedIndex, IR::CheckedSpanIndex, IR::AggregateLiteral, IR::ArrayLiteral, IR::VariantLiteral, IR::ZeroInit
|
|
358
|
+
true
|
|
359
|
+
when IR::Member, IR::Index
|
|
360
|
+
c_expression_lvalue?(expression.receiver)
|
|
361
|
+
when IR::Unary
|
|
362
|
+
expression.operator == "*"
|
|
363
|
+
else
|
|
364
|
+
false
|
|
365
|
+
end
|
|
351
366
|
end
|
|
352
367
|
|
|
353
368
|
def emit_cast_operand(expression)
|
|
@@ -464,7 +479,12 @@ module MilkTea
|
|
|
464
479
|
def emit_addressof_field_initializer(field_type, value)
|
|
465
480
|
c_type_name = named_type_c_name(field_type)
|
|
466
481
|
inner = value.expression
|
|
467
|
-
|
|
482
|
+
source = if c_expression_lvalue?(inner)
|
|
483
|
+
"&(#{emit_expression(inner)})"
|
|
484
|
+
else
|
|
485
|
+
"&(#{c_type(field_type)}[1]){ #{emit_expression(inner)} }[0]"
|
|
486
|
+
end
|
|
487
|
+
"((#{c_type_name}*)memcpy(malloc(sizeof(#{c_type_name})), #{source}, sizeof(#{c_type_name})))"
|
|
468
488
|
end
|
|
469
489
|
|
|
470
490
|
def emit_cyclic_array_initializer(field_type, value)
|
|
@@ -479,8 +499,10 @@ module MilkTea
|
|
|
479
499
|
init = emit_initializer(value)
|
|
480
500
|
source_expr = if init.start_with?("{")
|
|
481
501
|
"&(#{c_type(field_type)})#{init}"
|
|
482
|
-
|
|
502
|
+
elsif c_expression_lvalue?(value)
|
|
483
503
|
"&(#{init})"
|
|
504
|
+
else
|
|
505
|
+
"&(#{c_type(field_type)}[1]){ #{init} }[0]"
|
|
484
506
|
end
|
|
485
507
|
"((#{field_c_name}*)memcpy(malloc(sizeof(#{field_c_name})), #{source_expr}, sizeof(#{field_c_name})))"
|
|
486
508
|
end
|
|
@@ -116,13 +116,13 @@ module MilkTea
|
|
|
116
116
|
return false if expression.expression.type.is_a?(Types::Null)
|
|
117
117
|
|
|
118
118
|
c_type(expression.target_type) == c_type(expression.expression.type)
|
|
119
|
-
rescue
|
|
119
|
+
rescue CBackendError
|
|
120
120
|
false
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def identity_reinterpret?(target_type, source_type)
|
|
124
124
|
c_type(target_type) == c_type(source_type)
|
|
125
|
-
rescue
|
|
125
|
+
rescue CBackendError
|
|
126
126
|
false
|
|
127
127
|
end
|
|
128
128
|
|
|
@@ -104,7 +104,17 @@ module MilkTea
|
|
|
104
104
|
def emit_struct_equality_helpers
|
|
105
105
|
struct_decls_by_linkage = (emitted_aggregate_structs + collect_generic_struct_decls).each_with_object({}) { |decl, map| map[decl.linkage_name] = decl }
|
|
106
106
|
struct_equality_types
|
|
107
|
-
.filter_map
|
|
107
|
+
.filter_map do |type|
|
|
108
|
+
if type.is_a?(Types::VariantArmPayload)
|
|
109
|
+
type
|
|
110
|
+
elsif struct_decls_by_linkage.key?(named_type_c_name(type))
|
|
111
|
+
struct_decls_by_linkage[named_type_c_name(type)]
|
|
112
|
+
elsif type.is_a?(Types::Struct)
|
|
113
|
+
# External structs (raw ABI bindings) are not lowered into
|
|
114
|
+
# @program.structs, but their field layout is still known.
|
|
115
|
+
type
|
|
116
|
+
end
|
|
117
|
+
end
|
|
108
118
|
.flat_map { |decl_or_type| emit_struct_equality_helper(decl_or_type) }
|
|
109
119
|
end
|
|
110
120
|
|
|
@@ -112,11 +122,14 @@ module MilkTea
|
|
|
112
122
|
if struct_decl_or_type.is_a?(IR::StructDecl)
|
|
113
123
|
outer_c = struct_decl_or_type.linkage_name
|
|
114
124
|
fields = struct_decl_or_type.fields
|
|
115
|
-
|
|
125
|
+
elsif struct_decl_or_type.is_a?(Types::VariantArmPayload)
|
|
116
126
|
payload = struct_decl_or_type
|
|
117
127
|
outer_c = named_type_c_name(payload)
|
|
118
128
|
arm_fields = payload.variant_type.arm(payload.arm_name) || {}
|
|
119
129
|
fields = arm_fields.map { |name, field_type| IR::Field.new(name:, type: field_type) }
|
|
130
|
+
else
|
|
131
|
+
outer_c = named_type_c_name(struct_decl_or_type)
|
|
132
|
+
fields = struct_decl_or_type.fields.map { |name, field_type| IR::Field.new(name:, type: field_type) }
|
|
120
133
|
end
|
|
121
134
|
|
|
122
135
|
lines = ["static bool mt_struct_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
|
|
@@ -266,6 +266,10 @@ module MilkTea
|
|
|
266
266
|
simd_types = []
|
|
267
267
|
visited = {}
|
|
268
268
|
|
|
269
|
+
all_emitted_top_level_values.each do |value|
|
|
270
|
+
collect_simd_type(value.type, simd_types, visited)
|
|
271
|
+
end
|
|
272
|
+
|
|
269
273
|
emitted_functions.each do |function|
|
|
270
274
|
collect_simd_type(function.return_type, simd_types, visited)
|
|
271
275
|
function.params.each do |param|
|
|
@@ -280,6 +284,16 @@ module MilkTea
|
|
|
280
284
|
end
|
|
281
285
|
end
|
|
282
286
|
|
|
287
|
+
@program.unions.each do |union_decl|
|
|
288
|
+
union_decl.fields.each do |field|
|
|
289
|
+
collect_simd_type(field.type, simd_types, visited)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
each_variant_arm_field_type do |field_type|
|
|
294
|
+
collect_simd_type(field_type, simd_types, visited)
|
|
295
|
+
end
|
|
296
|
+
|
|
283
297
|
simd_types.uniq
|
|
284
298
|
end
|
|
285
299
|
|
|
@@ -299,9 +313,32 @@ module MilkTea
|
|
|
299
313
|
return unless type
|
|
300
314
|
return if visited[type]
|
|
301
315
|
|
|
302
|
-
|
|
316
|
+
visited[type] = true
|
|
317
|
+
|
|
318
|
+
case type
|
|
319
|
+
when Types::Simd
|
|
303
320
|
simd_types << type
|
|
304
|
-
|
|
321
|
+
when Types::Nullable
|
|
322
|
+
collect_simd_type(type.base, simd_types, visited)
|
|
323
|
+
when Types::GenericInstance
|
|
324
|
+
type.arguments.each do |argument|
|
|
325
|
+
collect_simd_type(argument, simd_types, visited) unless argument.is_a?(Types::LiteralTypeArg)
|
|
326
|
+
end
|
|
327
|
+
when Types::Function
|
|
328
|
+
type.params.each do |param|
|
|
329
|
+
collect_simd_type(param.type, simd_types, visited)
|
|
330
|
+
end
|
|
331
|
+
collect_simd_type(type.return_type, simd_types, visited)
|
|
332
|
+
when Types::Struct, Types::Union
|
|
333
|
+
type.fields.each_value do |field_type|
|
|
334
|
+
collect_simd_type(field_type, simd_types, visited)
|
|
335
|
+
end
|
|
336
|
+
when Types::Variant
|
|
337
|
+
type.arm_names.each do |arm_name|
|
|
338
|
+
type.arm(arm_name).each_value do |field_type|
|
|
339
|
+
collect_simd_type(field_type, simd_types, visited)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
305
342
|
end
|
|
306
343
|
end
|
|
307
344
|
|
|
@@ -27,6 +27,18 @@ module MilkTea
|
|
|
27
27
|
def code
|
|
28
28
|
"backend/internal"
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
def to_diagnostic(path: nil)
|
|
32
|
+
Diagnostic.new(
|
|
33
|
+
path: @path || path,
|
|
34
|
+
line: @line,
|
|
35
|
+
column: @column,
|
|
36
|
+
length: nil,
|
|
37
|
+
code: code,
|
|
38
|
+
message: message,
|
|
39
|
+
severity: :error,
|
|
40
|
+
)
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
class CBackend
|
|
@@ -6,16 +6,27 @@ module MilkTea
|
|
|
6
6
|
module CompileTime
|
|
7
7
|
Layout = ::MilkTea::Types::Layout
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
# Carries the value of a `return` statement out of the block evaluator as
|
|
10
|
+
# an ordinary value instead of an exception; callers unwrap it when present.
|
|
11
|
+
ReturnOutcome = Data.define(:value)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
class Error < StandardError
|
|
14
|
+
def code
|
|
15
|
+
"compile_time/error"
|
|
15
16
|
end
|
|
16
|
-
end
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
def to_diagnostic(path: nil)
|
|
19
|
+
Diagnostic.new(
|
|
20
|
+
path: path,
|
|
21
|
+
line: nil,
|
|
22
|
+
column: nil,
|
|
23
|
+
length: nil,
|
|
24
|
+
code: code,
|
|
25
|
+
message: message,
|
|
26
|
+
severity: :error,
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
19
30
|
|
|
20
31
|
def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
|
|
21
32
|
Evaluator.new(
|
|
@@ -78,6 +89,10 @@ module MilkTea
|
|
|
78
89
|
nil
|
|
79
90
|
when AST::ExpressionList
|
|
80
91
|
expression.elements.filter_map { |element| evaluate(element) }
|
|
92
|
+
when AST::RangeExpr
|
|
93
|
+
start_val = evaluate(expression.start_expr)
|
|
94
|
+
end_val = evaluate(expression.end_expr)
|
|
95
|
+
start_val.is_a?(Integer) && end_val.is_a?(Integer) ? (start_val...end_val).to_a : nil
|
|
81
96
|
when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
|
|
82
97
|
expression.value
|
|
83
98
|
when AST::StringLiteral
|
|
@@ -184,6 +199,8 @@ module MilkTea
|
|
|
184
199
|
right = evaluate(expression.right)
|
|
185
200
|
|
|
186
201
|
case expression.operator
|
|
202
|
+
when ".."
|
|
203
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? (left...right).to_a : nil
|
|
187
204
|
when "=="
|
|
188
205
|
CompileTime.equality_result(left, right)
|
|
189
206
|
when "!="
|
|
@@ -243,35 +260,45 @@ module MilkTea
|
|
|
243
260
|
result = nil
|
|
244
261
|
|
|
245
262
|
statements.each do |statement|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
value = statement.value ? evaluate_expression(statement.value, scopes:) : nil
|
|
251
|
-
raise ReturnValue.new(value)
|
|
252
|
-
when AST::WhileStmt
|
|
253
|
-
result = evaluate_while(statement, scopes:)
|
|
254
|
-
when AST::ForStmt
|
|
255
|
-
result = evaluate_for(statement, scopes:)
|
|
256
|
-
when AST::Assignment
|
|
257
|
-
result = evaluate_assignment(statement, scopes:)
|
|
258
|
-
when AST::IfStmt
|
|
259
|
-
result = evaluate_if(statement, scopes:)
|
|
260
|
-
when AST::ExpressionStmt
|
|
261
|
-
evaluate_expression(statement.expression, scopes:)
|
|
262
|
-
when AST::PassStmt, AST::BreakStmt, AST::ContinueStmt
|
|
263
|
-
# no-op at compile time
|
|
264
|
-
when AST::EmitStmt
|
|
265
|
-
# evaluated during lowering
|
|
266
|
-
result = nil
|
|
267
|
-
else
|
|
268
|
-
result = nil
|
|
269
|
-
end
|
|
263
|
+
outcome = evaluate_statement(statement, scopes:)
|
|
264
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
265
|
+
|
|
266
|
+
result = outcome
|
|
270
267
|
end
|
|
271
268
|
|
|
272
269
|
result
|
|
273
270
|
end
|
|
274
271
|
|
|
272
|
+
def evaluate_statement(statement, scopes:)
|
|
273
|
+
case statement
|
|
274
|
+
when AST::LocalDecl
|
|
275
|
+
evaluate_local_decl(statement, scopes:)
|
|
276
|
+
when AST::ReturnStmt
|
|
277
|
+
value = statement.value ? evaluate_expression(statement.value, scopes:) : nil
|
|
278
|
+
ReturnOutcome.new(value)
|
|
279
|
+
when AST::WhileStmt
|
|
280
|
+
evaluate_while(statement, scopes:)
|
|
281
|
+
when AST::ForStmt
|
|
282
|
+
evaluate_for(statement, scopes:)
|
|
283
|
+
when AST::MatchStmt
|
|
284
|
+
evaluate_match(statement, scopes:)
|
|
285
|
+
when AST::Assignment
|
|
286
|
+
evaluate_assignment(statement, scopes:)
|
|
287
|
+
when AST::IfStmt
|
|
288
|
+
evaluate_if(statement, scopes:)
|
|
289
|
+
when AST::ExpressionStmt
|
|
290
|
+
evaluate_expression(statement.expression, scopes:)
|
|
291
|
+
when AST::PassStmt, AST::BreakStmt, AST::ContinueStmt
|
|
292
|
+
# no-op at compile time
|
|
293
|
+
nil
|
|
294
|
+
when AST::EmitStmt
|
|
295
|
+
# emitted declarations are collected during lowering
|
|
296
|
+
nil
|
|
297
|
+
else
|
|
298
|
+
nil
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
275
302
|
def evaluate_expression(expression, scopes:)
|
|
276
303
|
case expression
|
|
277
304
|
when AST::Identifier
|
|
@@ -304,11 +331,34 @@ module MilkTea
|
|
|
304
331
|
value = evaluate_expression(assignment.value, scopes:)
|
|
305
332
|
case assignment.target
|
|
306
333
|
when AST::Identifier
|
|
334
|
+
if assignment.operator != "="
|
|
335
|
+
current = @variables[assignment.target.name]
|
|
336
|
+
value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
|
|
337
|
+
end
|
|
307
338
|
@variables[assignment.target.name] = value
|
|
308
339
|
end
|
|
309
340
|
value
|
|
310
341
|
end
|
|
311
342
|
|
|
343
|
+
def apply_compile_time_binary(operator, left, right)
|
|
344
|
+
case operator
|
|
345
|
+
when "+" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
|
|
346
|
+
when "-" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
|
|
347
|
+
when "*" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
|
|
348
|
+
when "/" then left.is_a?(Numeric) && right.is_a?(Numeric) && !zero_numeric?(right) ? left / right : nil
|
|
349
|
+
when "%" then left.is_a?(Integer) && right.is_a?(Integer) && !right.zero? ? left % right : nil
|
|
350
|
+
when "&" then left.is_a?(Integer) && right.is_a?(Integer) ? left & right : nil
|
|
351
|
+
when "|" then left.is_a?(Integer) && right.is_a?(Integer) ? left | right : nil
|
|
352
|
+
when "^" then left.is_a?(Integer) && right.is_a?(Integer) ? left ^ right : nil
|
|
353
|
+
when "<<" then left.is_a?(Integer) && right.is_a?(Integer) ? left << right : nil
|
|
354
|
+
when ">>" then left.is_a?(Integer) && right.is_a?(Integer) ? left >> right : nil
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def zero_numeric?(value)
|
|
359
|
+
(value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
|
|
360
|
+
end
|
|
361
|
+
|
|
312
362
|
def evaluate_while(statement, scopes:)
|
|
313
363
|
result = nil
|
|
314
364
|
iterations = 0
|
|
@@ -320,15 +370,8 @@ module MilkTea
|
|
|
320
370
|
break unless CompileTime.boolean_value?(condition)
|
|
321
371
|
|
|
322
372
|
statement.body.each do |body_stmt|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
value = body_stmt.value ? evaluate_expression(body_stmt.value, scopes:) : nil
|
|
326
|
-
raise ReturnValue.new(value)
|
|
327
|
-
when AST::Assignment
|
|
328
|
-
evaluate_assignment(body_stmt, scopes:)
|
|
329
|
-
when AST::ExpressionStmt
|
|
330
|
-
evaluate_expression(body_stmt.expression, scopes:)
|
|
331
|
-
end
|
|
373
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
374
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
332
375
|
end
|
|
333
376
|
iterations += 1
|
|
334
377
|
end
|
|
@@ -348,19 +391,8 @@ module MilkTea
|
|
|
348
391
|
iterable.each do |element|
|
|
349
392
|
@variables[loop_var_name] = element
|
|
350
393
|
statement.body.each do |body_stmt|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
value = body_stmt.value ? evaluate_expression(body_stmt.value, scopes:) : nil
|
|
354
|
-
raise ReturnValue.new(value)
|
|
355
|
-
when AST::Assignment
|
|
356
|
-
evaluate_assignment(body_stmt, scopes:)
|
|
357
|
-
when AST::ExpressionStmt
|
|
358
|
-
evaluate_expression(body_stmt.expression, scopes:)
|
|
359
|
-
when AST::IfStmt
|
|
360
|
-
result = evaluate_if(body_stmt, scopes:)
|
|
361
|
-
when AST::WhileStmt
|
|
362
|
-
result = evaluate_while(body_stmt, scopes:)
|
|
363
|
-
end
|
|
394
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
395
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
364
396
|
end
|
|
365
397
|
end
|
|
366
398
|
|
|
@@ -372,15 +404,8 @@ module MilkTea
|
|
|
372
404
|
condition = evaluate_expression(branch.condition, scopes:)
|
|
373
405
|
if CompileTime.boolean_value?(condition) && condition
|
|
374
406
|
branch.body.each do |body_stmt|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
value = body_stmt.value ? evaluate_expression(body_stmt.value, scopes:) : nil
|
|
378
|
-
raise ReturnValue.new(value)
|
|
379
|
-
when AST::Assignment
|
|
380
|
-
evaluate_assignment(body_stmt, scopes:)
|
|
381
|
-
when AST::ExpressionStmt
|
|
382
|
-
evaluate_expression(body_stmt.expression, scopes:)
|
|
383
|
-
end
|
|
407
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
408
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
384
409
|
end
|
|
385
410
|
return condition
|
|
386
411
|
end
|
|
@@ -388,15 +413,26 @@ module MilkTea
|
|
|
388
413
|
|
|
389
414
|
if statement.else_body
|
|
390
415
|
statement.else_body.each do |body_stmt|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
416
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
417
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
nil
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def evaluate_match(statement, scopes:)
|
|
425
|
+
scrutinee = evaluate_expression(statement.expression, scopes:)
|
|
426
|
+
return nil unless scrutinee
|
|
427
|
+
|
|
428
|
+
statement.arms.each do |arm|
|
|
429
|
+
wildcard = arm.pattern.is_a?(AST::Identifier) && arm.pattern.name == "_"
|
|
430
|
+
if wildcard || CompileTime.equality_result(scrutinee, evaluate_expression(arm.pattern, scopes:)) == true
|
|
431
|
+
arm.body.each do |body_stmt|
|
|
432
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
433
|
+
return outcome if outcome.is_a?(ReturnOutcome)
|
|
399
434
|
end
|
|
435
|
+
return scrutinee
|
|
400
436
|
end
|
|
401
437
|
end
|
|
402
438
|
|
|
@@ -445,9 +481,8 @@ module MilkTea
|
|
|
445
481
|
initial_vars[param.name] = arg_value
|
|
446
482
|
end
|
|
447
483
|
ctx = BlockContext.new(@checker, initial_variables: initial_vars)
|
|
448
|
-
ctx.evaluate_block(func.ast.body, scopes:)
|
|
449
|
-
|
|
450
|
-
e.value
|
|
484
|
+
result = ctx.evaluate_block(func.ast.body, scopes:)
|
|
485
|
+
result.is_a?(ReturnOutcome) ? result.value : result
|
|
451
486
|
end
|
|
452
487
|
end
|
|
453
488
|
|
|
@@ -506,7 +541,7 @@ module MilkTea
|
|
|
506
541
|
end
|
|
507
542
|
|
|
508
543
|
def self.core_member_handles(type)
|
|
509
|
-
type.members.map { |name
|
|
544
|
+
type.members.map { |name| Types::MemberHandle.new(nil, name, type.member_value(name)) }
|
|
510
545
|
end
|
|
511
546
|
|
|
512
547
|
def self.core_evaluate_type_returning(
|
data/lib/milk_tea/core/lexer.rb
CHANGED
|
@@ -26,6 +26,18 @@ module MilkTea
|
|
|
26
26
|
def code
|
|
27
27
|
"lex/error"
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
def to_diagnostic(path: nil)
|
|
31
|
+
Diagnostic.new(
|
|
32
|
+
path: @path || path,
|
|
33
|
+
line: @line,
|
|
34
|
+
column: @column,
|
|
35
|
+
length: nil,
|
|
36
|
+
code: code,
|
|
37
|
+
message: message,
|
|
38
|
+
severity: :error,
|
|
39
|
+
)
|
|
40
|
+
end
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
class Lexer
|