mt-lang 0.2.22 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +24 -1
- data/docs/index.html +2 -1
- data/docs/language-design.md +2 -0
- data/docs/language-manual.md +16 -1
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/ast.rb +2 -2
- data/lib/milk_tea/core/c_backend/expressions.rb +11 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +49 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +7 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +12 -0
- data/lib/milk_tea/core/c_backend.rb +8 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +4 -0
- data/lib/milk_tea/core/compile_time.rb +16 -0
- data/lib/milk_tea/core/ir.rb +1 -0
- data/lib/milk_tea/core/keywords.rb +1 -1
- data/lib/milk_tea/core/lowering/block.rb +48 -16
- data/lib/milk_tea/core/lowering/calls.rb +75 -1
- data/lib/milk_tea/core/lowering/declarations.rb +7 -0
- data/lib/milk_tea/core/lowering/expressions.rb +39 -8
- data/lib/milk_tea/core/lowering/functions.rb +1 -1
- data/lib/milk_tea/core/lowering/resolve.rb +70 -5
- data/lib/milk_tea/core/lowering/utils.rb +26 -0
- data/lib/milk_tea/core/module_binder.rb +4 -0
- data/lib/milk_tea/core/parser/expressions.rb +11 -3
- data/lib/milk_tea/core/parser/statements.rb +2 -2
- data/lib/milk_tea/core/parser/types.rb +8 -1
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +97 -4
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +52 -2
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +7 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +40 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +11 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +21 -4
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +23 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +37 -0
- data/lib/milk_tea/core/types/layout.rb +7 -0
- data/lib/milk_tea/core/types/predicates.rb +44 -3
- data/lib/milk_tea/core/types/registry.rb +4 -0
- data/lib/milk_tea/core/types/types.rb +42 -0
- data/lib/milk_tea/core/types/visitor.rb +2 -0
- data/lib/milk_tea/lsp/server/completion.rb +1 -1
- data/lib/milk_tea/lsp/server/hover.rb +8 -1
- data/std/raylib.mt +14 -14
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7b3cb1fb6603adc1eae75fca276b3cbe83fa5daac424d70dda2386200c541da
|
|
4
|
+
data.tar.gz: ccd1d4f1d2bfb4d19be13053a90445be1c6aedc0785d6e089f43f4ad650e72d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b378bb8cc0e369b7b9f318821c4c5c287135e4e88c75bef3e26944935b4a3679d128f5e273b3de2851fe16dd3af38065fd74ceba23d334ef67a8f716cb73319
|
|
7
|
+
data.tar.gz: 39c93ea971b8092f9222a1ded6a01512087f232dcb431868227708897cac5defd72ca1b30fa180113a43688aebd5aad2983629b2ad7cc3314d0fb583c3418093
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Milk Tea
|
|
1
|
+
# Milk Tea (Simple Game) Programming Language
|
|
2
2
|
|
|
3
3
|
A statically typed, indentation-based systems language for games.
|
|
4
4
|
Compiles to readable C. Safe by default, explicit by design.
|
|
@@ -360,10 +360,20 @@ Ordinary functions:
|
|
|
360
360
|
|
|
361
361
|
- Parameters must be typed.
|
|
362
362
|
- Parameters are non-rebindable.
|
|
363
|
+
- Parameters may include default values using `name: type = default_expr`. All required parameters must appear before defaulted parameters. Default calls are resolved at compile time by inlining the expression at each call site that omits the argument. Defaults are not supported on `external function` or `foreign function`.
|
|
363
364
|
- Return type defaults to `void` if omitted.
|
|
364
365
|
- Generic functions are supported.
|
|
365
366
|
- Generic function and method type parameters may use `implements` constraints.
|
|
366
367
|
|
|
368
|
+
```mt
|
|
369
|
+
function configure(host: str, port: int = 8080, debug: bool = false) -> void:
|
|
370
|
+
pass
|
|
371
|
+
|
|
372
|
+
configure("localhost") # port=8080, debug=false
|
|
373
|
+
configure("localhost", port = 3000) # debug=false
|
|
374
|
+
configure(host = "remote", debug = true) # port=8080
|
|
375
|
+
```
|
|
376
|
+
|
|
367
377
|
`const function`:
|
|
368
378
|
|
|
369
379
|
A `const function` is evaluable at compile time. Its body follows the same restrictions as a block-bodied `const`. When called from a compile-time context (`const`, `when`, `inline if`, `inline for`), the call is constant-folded:
|
|
@@ -476,6 +486,17 @@ let label = match code:
|
|
|
476
486
|
_: "other"
|
|
477
487
|
```
|
|
478
488
|
|
|
489
|
+
Integer match arms also support range patterns via `start..end`:
|
|
490
|
+
|
|
491
|
+
```mt
|
|
492
|
+
let tier = match score:
|
|
493
|
+
0..10: "low"
|
|
494
|
+
11..50: "mid"
|
|
495
|
+
_: "high"
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Range arms are refutable — they do not count toward exhaustiveness — and require `_`.
|
|
499
|
+
|
|
479
500
|
`match` rules:
|
|
480
501
|
|
|
481
502
|
- Enum and variant matches must be exhaustive unless `_` is present.
|
|
@@ -713,6 +734,7 @@ Native type operators:
|
|
|
713
734
|
- Vectors (`vecN`/`ivecN`): `+`, `-`, `*` (component-wise) with same-type vectors; `*`, `/` with scalar; unary `-`
|
|
714
735
|
- Matrices (`matN`): `+`, `-` with same-type matrices; `*`, `/` with scalar; unary `-`
|
|
715
736
|
- Quaternions (`quat`): `+`, `-`, `*` (component-wise) with same-type quaternions; unary `-`
|
|
737
|
+
- SIMD vectors (`simd[T, N]`): `+`, `-`, `*`, `/` (component-wise same-type or scalar), `%`, `&`, `|`, `^`, `~` (integer lanes), `<<`, `>>` (integer lanes, scalar shift), unary `-`/`~` (integer lanes for `~`)
|
|
716
738
|
|
|
717
739
|
## 10. Type System
|
|
718
740
|
|
|
@@ -762,6 +784,7 @@ Type constructors:
|
|
|
762
784
|
- `fn(params...) -> R`
|
|
763
785
|
- `proc(params...) -> R`
|
|
764
786
|
- `SoA[T, N]` — Structure-of-Arrays: each struct field becomes a separate array of length `N`; access `soa[i].field` reads from column `field` at row `i`
|
|
787
|
+
- `simd[T, N]` — SIMD vector: `N` numeric lanes (128 or 256 bits). Component-wise `+` `-` `*` `/` `%` `&` `|` `^` `~` `<<` `>>`. Lane access via `[i]` (compile-time index). Lowers to GCC/Clang vector extensions for portable x86/ARM vector code.
|
|
765
788
|
- `dyn[InterfaceName]` — runtime interface value (fat pointer: `{ void* data, void* vtable }`). Constructed via `adapt[Interface](value: ref[T])`. @see §6.
|
|
766
789
|
- `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins.
|
|
767
790
|
- `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
|
data/docs/index.html
CHANGED
|
@@ -1272,6 +1272,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)</code></pre>
|
|
|
1272
1272
|
<tr><td><code>fn(params...) -> R</code></td><td>Function pointer type</td></tr>
|
|
1273
1273
|
<tr><td><code>proc(params...) -> R</code></td><td>Closure type (value captures)</td></tr>
|
|
1274
1274
|
<tr><td><code>SoA[T, N]</code></td><td>Structure-of-Arrays: fields become separate arrays</td></tr>
|
|
1275
|
+
<tr><td><code>simd[T, N]</code></td><td>SIMD vector: N numeric lanes (128/256-bit), GCC vector extensions</td></tr>
|
|
1275
1276
|
<tr><td><code>dyn[Interface]</code></td><td>Runtime interface value (fat pointer)</td></tr>
|
|
1276
1277
|
<tr><td><code>atomic[T]</code></td><td>Atomic value for lock-free concurrent access</td></tr>
|
|
1277
1278
|
<tr><td><code>(T, U)</code></td><td>Tuple type</td></tr>
|
|
@@ -2529,7 +2530,7 @@ const TYPES = new Set([
|
|
|
2529
2530
|
'bool','byte','short','int','long','ubyte','ushort','uint','ulong',
|
|
2530
2531
|
'char','ptr_int','ptr_uint','float','double','void',
|
|
2531
2532
|
'str','cstr','vec2','vec3','vec4','ivec2','ivec3','ivec4',
|
|
2532
|
-
'mat3','mat4','quat','ptr','span','array','SoA',
|
|
2533
|
+
'mat3','mat4','quat','ptr','span','array','SoA','simd',
|
|
2533
2534
|
'Option','Result','Task','dyn','ref','const_ptr','own','str_buffer','proc',
|
|
2534
2535
|
'field_handle','member_handle','callable_handle','attribute_handle',
|
|
2535
2536
|
'EventError','Subscription','atomic',
|
data/docs/language-design.md
CHANGED
|
@@ -561,6 +561,7 @@ Notes:
|
|
|
561
561
|
```mt
|
|
562
562
|
array[T, N] # fixed-size array
|
|
563
563
|
SoA[T, N] # Structure-of-Arrays: flatten struct fields into separate arrays
|
|
564
|
+
simd[T, N] # SIMD vector: N numeric lanes, 128/256-bit, lowers to GCC vector extensions
|
|
564
565
|
str_buffer[N] # fixed-capacity mutable UTF-8 text buffer
|
|
565
566
|
ptr[T] # raw pointer
|
|
566
567
|
span[T] # pointer + length view
|
|
@@ -771,6 +772,7 @@ Built-in vector, matrix, and quaternion types support component-wise arithmetic
|
|
|
771
772
|
- Vectors (`vecN`/`ivecN`): `+`, `-`, `*` (component-wise same-type); `*`, `/` (scalar); unary `-`
|
|
772
773
|
- Matrices (`matN`): `+`, `-` (component-wise same-type); `*`, `/` (scalar); unary `-`
|
|
773
774
|
- Quaternions (`quat`): `+`, `-`, `*` (component-wise same-type); unary `-`
|
|
775
|
+
- SIMD vectors (`simd[T, N]`): `+`, `-`, `*`, `/` (component-wise same-type); `+`, `-`, `*`, `/` (scalar, when scalar matches element type); `%`, `&`, `|`, `^`, `~` (integer element types only); `<<`, `>>` (integer element types, shift-amount is scalar int); unary `-`, `~`
|
|
774
776
|
|
|
775
777
|
### Casts
|
|
776
778
|
|
data/docs/language-manual.md
CHANGED
|
@@ -426,6 +426,19 @@ function add(a: int, b: int) -> int:
|
|
|
426
426
|
return a + b
|
|
427
427
|
```
|
|
428
428
|
|
|
429
|
+
Parameters may include default values using `= expr` after the type annotation. All required parameters must appear before any defaulted parameter. The default expression is inlined at each call site that omits the argument — there is zero runtime overhead and no function signature change.
|
|
430
|
+
|
|
431
|
+
```mt
|
|
432
|
+
function configure(host: str, port: int = 8080, debug: bool = false) -> void:
|
|
433
|
+
pass
|
|
434
|
+
|
|
435
|
+
configure("localhost") # port=8080, debug=false
|
|
436
|
+
configure("localhost", port = 3000) # debug=false
|
|
437
|
+
configure(host = "remote", debug = true) # port=8080
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Defaults are not supported on `external function` or `foreign function` declarations.
|
|
441
|
+
|
|
429
442
|
Rules:
|
|
430
443
|
|
|
431
444
|
- Parameters must be typed.
|
|
@@ -566,7 +579,7 @@ Scrutinee types supported:
|
|
|
566
579
|
|
|
567
580
|
- Enum: arm patterns must be members of that enum.
|
|
568
581
|
- Variant: arm patterns must be arms of that variant; a payload arm may bind its fields with `as name` or destructure them inline with struct patterns.
|
|
569
|
-
- Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals or
|
|
582
|
+
- Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals, char literals, or range patterns (`a..b`).
|
|
570
583
|
- `str`: arm patterns must be string literals (e.g., `"lex": ...`). Matches via full content comparison using the `equal` builtin.
|
|
571
584
|
- Tuple: arm patterns must be tuple literal patterns whose elements may be integer literals, char literals, string literals, booleans, or `_` discard.
|
|
572
585
|
|
|
@@ -960,6 +973,7 @@ Rules:
|
|
|
960
973
|
- `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
|
|
961
974
|
- `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
|
|
962
975
|
- `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
|
|
976
|
+
- `simd[T, N]` — SIMD vector with `N` lanes of numeric primitive type `T`; total width must be 128 or 256 bits. Supports component-wise arithmetic (`+` `-` `*` `/`), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
|
|
963
977
|
|
|
964
978
|
Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
|
|
965
979
|
|
|
@@ -995,6 +1009,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
|
|
|
995
1009
|
- `Option[T]`
|
|
996
1010
|
- `Result[T, E]`
|
|
997
1011
|
- `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
|
|
1012
|
+
- `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/`, bitwise `&` `|` `^` `~`, shift `<<` `>>`, unary `-` `~`, and compound assignment. Lane access via compile-time constant `[i]`. Total width must be 128 or 256 bits. Lowers to GCC/Clang vector extensions.
|
|
998
1013
|
- `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
|
|
999
1014
|
- `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins. `atomic[T]` is zero-initializable and sendable.
|
|
1000
1015
|
- `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
|
data/lib/milk_tea/base.rb
CHANGED
data/lib/milk_tea/core/ast.rb
CHANGED
|
@@ -117,8 +117,8 @@ module MilkTea
|
|
|
117
117
|
ForeignFunctionDecl = Data.define(:name, :type_params, :params, :return_type, :variadic, :mapping, :visibility, :attributes, :line) do
|
|
118
118
|
def initialize(name:, type_params:, params:, return_type:, variadic:, mapping:, visibility:, attributes: [], line: nil) = super
|
|
119
119
|
end
|
|
120
|
-
Param = Data.define(:name, :type, :line, :column) do
|
|
121
|
-
def initialize(name:, type:, line: nil, column: nil) = super
|
|
120
|
+
Param = Data.define(:name, :type, :line, :column, :default_value) do
|
|
121
|
+
def initialize(name:, type:, line: nil, column: nil, default_value: nil) = super
|
|
122
122
|
end
|
|
123
123
|
ForeignParam = Data.define(:name, :type, :mode, :boundary_type)
|
|
124
124
|
LocalDecl = Data.define(:kind, :name, :type, :value, :else_binding, :else_body, :line, :column, :recovered_else, :destructure_bindings, :destructure_type_name) do
|
|
@@ -95,6 +95,8 @@ module MilkTea
|
|
|
95
95
|
emit_aggregate_literal(expression)
|
|
96
96
|
when IR::ArrayLiteral
|
|
97
97
|
emit_array_compound_literal(expression)
|
|
98
|
+
when IR::SimdLaneWith
|
|
99
|
+
emit_simd_lane_with(expression)
|
|
98
100
|
when IR::VariantLiteral
|
|
99
101
|
emit_variant_literal(expression)
|
|
100
102
|
else
|
|
@@ -359,6 +361,15 @@ module MilkTea
|
|
|
359
361
|
"(#{c_declaration(expression.type, '')}) #{emit_array_initializer(expression)}"
|
|
360
362
|
end
|
|
361
363
|
|
|
364
|
+
def emit_simd_lane_with(expression)
|
|
365
|
+
simd_type = expression.type
|
|
366
|
+
elem_c = primitive_c_type(simd_type.element_type.name)
|
|
367
|
+
src = wrap_expression(expression.src)
|
|
368
|
+
idx = emit_expression(expression.index)
|
|
369
|
+
val = wrap_expression(expression.value)
|
|
370
|
+
"({ #{c_type(simd_type)} _mt_with = #{src}; ((#{elem_c}*)&_mt_with)[#{idx}] = #{val}; _mt_with; })"
|
|
371
|
+
end
|
|
372
|
+
|
|
362
373
|
def emit_zero_initializer(type)
|
|
363
374
|
return "{ 0 }" if type.is_a?(Types::StringView)
|
|
364
375
|
return "{ 0 }" if array_type?(type)
|
|
@@ -270,6 +270,55 @@ module MilkTea
|
|
|
270
270
|
end
|
|
271
271
|
end
|
|
272
272
|
|
|
273
|
+
def collect_simd_types
|
|
274
|
+
simd_types = []
|
|
275
|
+
visited = {}
|
|
276
|
+
|
|
277
|
+
emitted_functions.each do |function|
|
|
278
|
+
collect_simd_type(function.return_type, simd_types, visited)
|
|
279
|
+
function.params.each do |param|
|
|
280
|
+
collect_simd_type(param.type, simd_types, visited)
|
|
281
|
+
end
|
|
282
|
+
collect_simd_from_statements(function.body, simd_types, visited)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
@program.structs.each do |struct_decl|
|
|
286
|
+
struct_decl.fields.each do |field|
|
|
287
|
+
collect_simd_type(field.type, simd_types, visited)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
simd_types.uniq
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def collect_simd_from_statements(statements, simd_types, visited)
|
|
295
|
+
statements.each do |stmt|
|
|
296
|
+
case stmt
|
|
297
|
+
when IR::LocalDecl
|
|
298
|
+
collect_simd_type(stmt.type, simd_types, visited)
|
|
299
|
+
when IR::BlockStmt
|
|
300
|
+
collect_simd_from_statements(stmt.body, simd_types, visited)
|
|
301
|
+
when IR::IfStmt
|
|
302
|
+
collect_simd_from_statements(stmt.then_body || [], simd_types, visited)
|
|
303
|
+
collect_simd_from_statements(stmt.else_body || [], simd_types, visited)
|
|
304
|
+
when IR::WhileStmt
|
|
305
|
+
collect_simd_from_statements(stmt.body || [], simd_types, visited)
|
|
306
|
+
when IR::ForStmt
|
|
307
|
+
collect_simd_from_statements(stmt.body || [], simd_types, visited)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def collect_simd_type(type, simd_types, visited)
|
|
313
|
+
return unless type
|
|
314
|
+
return if visited[type]
|
|
315
|
+
|
|
316
|
+
if type.is_a?(Types::Simd)
|
|
317
|
+
simd_types << type
|
|
318
|
+
visited[type] = true
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
273
322
|
def collect_generic_struct_decls
|
|
274
323
|
collect_generic_struct_types.map do |type|
|
|
275
324
|
fields = type.fields.map { |field_name, field_type| IR::Field.new(name: field_name, type: field_type) }
|
|
@@ -195,6 +195,13 @@ module MilkTea
|
|
|
195
195
|
lines
|
|
196
196
|
end
|
|
197
197
|
|
|
198
|
+
def emit_simd_type(type)
|
|
199
|
+
name = simd_type_name(type)
|
|
200
|
+
elem_c = primitive_c_type(type.element_type.name)
|
|
201
|
+
width = type.lane_count * Layout.primitive_layout(type.element_type).first
|
|
202
|
+
["typedef #{elem_c} #{name} __attribute__((__vector_size__(#{width})));"]
|
|
203
|
+
end
|
|
204
|
+
|
|
198
205
|
def struct_layout_attributes(struct_decl)
|
|
199
206
|
attributes = []
|
|
200
207
|
attributes << "packed" if struct_decl.packed
|
|
@@ -162,6 +162,9 @@ module MilkTea
|
|
|
162
162
|
when Types::SoA
|
|
163
163
|
base = soa_type_name(type)
|
|
164
164
|
pointer ? "#{base}*" : base
|
|
165
|
+
when Types::Simd
|
|
166
|
+
base = simd_type_name(type)
|
|
167
|
+
pointer ? "#{base}*" : base
|
|
165
168
|
when Types::Tuple
|
|
166
169
|
base = tuple_type_name(type)
|
|
167
170
|
pointer ? "#{base}*" : base
|
|
@@ -191,6 +194,11 @@ module MilkTea
|
|
|
191
194
|
"mt_soa_#{element_name}_#{type.count}"
|
|
192
195
|
end
|
|
193
196
|
|
|
197
|
+
def simd_type_name(type)
|
|
198
|
+
element_name = sanitize_identifier(type.element_type.to_s)
|
|
199
|
+
"mt_simd_#{element_name}x#{type.lane_count}"
|
|
200
|
+
end
|
|
201
|
+
|
|
194
202
|
def tuple_type_name(type)
|
|
195
203
|
sanitized = type.element_types.map { |et| sanitize_identifier(et.to_s) }.join("_")
|
|
196
204
|
base = "mt_tuple_#{sanitized}"
|
|
@@ -268,6 +276,10 @@ module MilkTea
|
|
|
268
276
|
raise CBackendError, "ref requires at least one type argument" unless [1, 2].include?(type.arguments.length)
|
|
269
277
|
|
|
270
278
|
"#{c_type(type.arguments.length == 1 ? type.arguments.first : type.arguments[1])}*"
|
|
279
|
+
when "array"
|
|
280
|
+
raise CBackendError, "array requires exactly two type arguments" unless type.arguments.length == 2
|
|
281
|
+
|
|
282
|
+
c_type(type.arguments.first)
|
|
271
283
|
when "str_buffer"
|
|
272
284
|
raise CBackendError, "str_buffer requires exactly one type argument" unless str_buffer_type?(type)
|
|
273
285
|
|
|
@@ -169,6 +169,14 @@ module MilkTea
|
|
|
169
169
|
lines << ""
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
+
collect_simd_types.each do |type|
|
|
173
|
+
next if @emitted_simd_type_names&.include?(simd_type_name(type))
|
|
174
|
+
@emitted_simd_type_names ||= Set.new
|
|
175
|
+
@emitted_simd_type_names.add(simd_type_name(type))
|
|
176
|
+
lines.concat(emit_simd_type(type))
|
|
177
|
+
lines << ""
|
|
178
|
+
end
|
|
179
|
+
|
|
172
180
|
if uses_entrypoint_argv_helpers?
|
|
173
181
|
lines.concat(emit_entrypoint_argv_helpers)
|
|
174
182
|
lines << ""
|
|
@@ -108,6 +108,22 @@ module MilkTea
|
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
|
+
|
|
112
|
+
types = if @checker.respond_to?(:types)
|
|
113
|
+
@checker.types
|
|
114
|
+
else
|
|
115
|
+
@checker.instance_variable_get(:@ctx).types
|
|
116
|
+
end
|
|
117
|
+
if (type = types[call_expr.callee.name]) && type.is_a?(Types::Struct)
|
|
118
|
+
fields = {}
|
|
119
|
+
call_expr.arguments.each do |argument|
|
|
120
|
+
val = evaluate_expression(argument.value, scopes:)
|
|
121
|
+
return nil unless val
|
|
122
|
+
fields[argument.name] = val
|
|
123
|
+
end
|
|
124
|
+
next fields
|
|
125
|
+
end
|
|
126
|
+
|
|
111
127
|
@checker.send(:evaluate_compile_time_const_value, call_expr, scopes:)
|
|
112
128
|
},
|
|
113
129
|
)
|
data/lib/milk_tea/core/ir.rb
CHANGED
|
@@ -76,6 +76,7 @@ module MilkTea
|
|
|
76
76
|
AggregateLiteral = Data.define(:type, :fields)
|
|
77
77
|
AggregateField = Data.define(:name, :value)
|
|
78
78
|
ArrayLiteral = Data.define(:type, :elements)
|
|
79
|
+
SimdLaneWith = Data.define(:src, :index, :value, :type)
|
|
79
80
|
VariantDecl = Data.define(:name, :linkage_name, :arms, :source_module) do
|
|
80
81
|
def initialize(name:, linkage_name:, arms:, source_module: nil) = super
|
|
81
82
|
end
|
|
@@ -90,7 +90,7 @@ module MilkTea
|
|
|
90
90
|
].freeze
|
|
91
91
|
|
|
92
92
|
BUILTIN_TYPE_NAMES = (BUILTIN_PRIMITIVE_NAMES + %w[
|
|
93
|
-
ptr const_ptr own ref span array str_buffer atomic Task Option Result SoA
|
|
93
|
+
ptr const_ptr own ref span array str_buffer atomic Task Option Result SoA simd
|
|
94
94
|
struct_handle field_handle callable_handle attribute_handle member_handle type
|
|
95
95
|
EventError Subscription
|
|
96
96
|
]).freeze
|
|
@@ -597,24 +597,56 @@ module MilkTea
|
|
|
597
597
|
end
|
|
598
598
|
lowered.concat(else_body)
|
|
599
599
|
else
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
600
|
+
has_range_arms = statement.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
|
|
601
|
+
|
|
602
|
+
if has_range_arms
|
|
603
|
+
arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
|
|
604
|
+
bool_type = @ctx.types.fetch("bool")
|
|
605
|
+
non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
|
|
606
|
+
wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
|
|
607
|
+
else_body = if wildcard
|
|
608
|
+
lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
|
|
609
|
+
else
|
|
610
|
+
[]
|
|
611
|
+
end
|
|
612
|
+
non_wildcard.reverse_each do |arm|
|
|
613
|
+
arm_body = lower_block(
|
|
614
|
+
arm.body,
|
|
615
|
+
env: local_env,
|
|
616
|
+
active_defers: active_defers + local_defers,
|
|
617
|
+
return_type:,
|
|
618
|
+
loop_flow: arm_loop_flow,
|
|
619
|
+
allow_return:,
|
|
620
|
+
)
|
|
621
|
+
cond = if arm.pattern.is_a?(AST::RangeExpr)
|
|
622
|
+
lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
|
|
623
|
+
else
|
|
624
|
+
lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
|
|
625
|
+
IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
|
|
626
|
+
end
|
|
627
|
+
else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
|
|
628
|
+
end
|
|
629
|
+
lowered.concat(else_body)
|
|
630
|
+
else
|
|
631
|
+
arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
|
|
632
|
+
cases = statement.arms.map do |arm|
|
|
633
|
+
body = lower_block(
|
|
634
|
+
arm.body,
|
|
635
|
+
env: local_env,
|
|
636
|
+
active_defers: active_defers + local_defers,
|
|
637
|
+
return_type:,
|
|
638
|
+
loop_flow: arm_loop_flow,
|
|
639
|
+
allow_return:,
|
|
640
|
+
)
|
|
641
|
+
if wildcard_arm_pattern?(arm.pattern)
|
|
642
|
+
IR::SwitchDefaultCase.new(body:)
|
|
643
|
+
else
|
|
644
|
+
value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
|
|
645
|
+
IR::SwitchCase.new(value:, body:)
|
|
646
|
+
end
|
|
615
647
|
end
|
|
648
|
+
lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
|
|
616
649
|
end
|
|
617
|
-
lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
|
|
618
650
|
end
|
|
619
651
|
lowered.concat(expression_cleanups.flat_map(&:itself))
|
|
620
652
|
end
|
|
@@ -26,7 +26,9 @@ module MilkTea
|
|
|
26
26
|
return lower_foreign_call_inline(expression, callee_binding, env:, type:)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
lowered_arguments = expression.arguments
|
|
30
|
+
lowered_arguments = expand_function_defaults(lowered_arguments, callee_binding) if callee_binding
|
|
31
|
+
arguments = lower_call_arguments(lowered_arguments, callee_type, env:)
|
|
30
32
|
IR::Call.new(callee: callee_name, arguments:, type:)
|
|
31
33
|
when :callable_value
|
|
32
34
|
callee_expression = lower_expression(expression.callee, env:, expected_type: callee_type)
|
|
@@ -136,6 +138,8 @@ module MilkTea
|
|
|
136
138
|
end
|
|
137
139
|
when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange
|
|
138
140
|
lower_atomic_method_call(kind, receiver, expression, env:, type:)
|
|
141
|
+
when :simd_lane_with
|
|
142
|
+
lower_simd_lane_with(kind, receiver, expression, env:, type:)
|
|
139
143
|
when :associated_method
|
|
140
144
|
arguments = lower_call_arguments(expression.arguments, callee_type, env:)
|
|
141
145
|
IR::Call.new(callee: callee_name, arguments:, type:)
|
|
@@ -218,6 +222,12 @@ module MilkTea
|
|
|
218
222
|
lower_contextual_expression(argument.value, env:, expected_type: element_type)
|
|
219
223
|
end
|
|
220
224
|
IR::ArrayLiteral.new(type:, elements:)
|
|
225
|
+
when :simd
|
|
226
|
+
element_type = simd_type?(type) ? type.element_type : array_element_type(type)
|
|
227
|
+
elements = expression.arguments.map do |argument|
|
|
228
|
+
lower_contextual_expression(argument.value, env:, expected_type: element_type)
|
|
229
|
+
end
|
|
230
|
+
IR::ArrayLiteral.new(type:, elements:)
|
|
221
231
|
when :reinterpret
|
|
222
232
|
argument = expression.arguments.fetch(0)
|
|
223
233
|
source_type = infer_expression_type(argument.value, env:)
|
|
@@ -366,6 +376,49 @@ module MilkTea
|
|
|
366
376
|
IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
|
|
367
377
|
end
|
|
368
378
|
|
|
379
|
+
def expand_function_defaults(arguments, binding)
|
|
380
|
+
return arguments unless binding.respond_to?(:ast) && binding.ast.respond_to?(:params)
|
|
381
|
+
|
|
382
|
+
ast_params = binding.ast.params
|
|
383
|
+
has_defaults = ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value }
|
|
384
|
+
return arguments unless has_defaults || arguments.any?(&:name)
|
|
385
|
+
|
|
386
|
+
if arguments.any?(&:name)
|
|
387
|
+
by_position = Array.new(ast_params.length)
|
|
388
|
+
param_names = ast_params.map(&:name)
|
|
389
|
+
|
|
390
|
+
arguments.each do |arg|
|
|
391
|
+
if arg.name
|
|
392
|
+
param_idx = param_names.index(arg.name)
|
|
393
|
+
next unless param_idx
|
|
394
|
+
|
|
395
|
+
by_position[param_idx] = arg
|
|
396
|
+
else
|
|
397
|
+
first_empty = by_position.index(nil)
|
|
398
|
+
by_position[first_empty] = arg if first_empty
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
by_position.each_with_index do |slot, idx|
|
|
403
|
+
next if slot
|
|
404
|
+
next unless ast_params[idx].respond_to?(:default_value) && ast_params[idx].default_value
|
|
405
|
+
|
|
406
|
+
by_position[idx] = AST::Argument.new(name: nil, value: ast_params[idx].default_value)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
by_position.compact
|
|
410
|
+
else
|
|
411
|
+
expanded = arguments.dup
|
|
412
|
+
while expanded.length < ast_params.length
|
|
413
|
+
ast_param = ast_params[expanded.length]
|
|
414
|
+
break unless ast_param.respond_to?(:default_value) && ast_param.default_value
|
|
415
|
+
|
|
416
|
+
expanded << AST::Argument.new(name: nil, value: ast_param.default_value)
|
|
417
|
+
end
|
|
418
|
+
expanded
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
369
422
|
def lower_call_arguments(arguments, callee_type, env:)
|
|
370
423
|
arguments.map.with_index do |argument, index|
|
|
371
424
|
parameter = index < callee_type.params.length ? callee_type.params[index] : nil
|
|
@@ -1517,6 +1570,14 @@ module MilkTea
|
|
|
1517
1570
|
end
|
|
1518
1571
|
end
|
|
1519
1572
|
|
|
1573
|
+
def simd_method_kind(receiver_type, name)
|
|
1574
|
+
return unless simd_type?(receiver_type)
|
|
1575
|
+
|
|
1576
|
+
case name
|
|
1577
|
+
when "with" then :simd_lane_with
|
|
1578
|
+
end
|
|
1579
|
+
end
|
|
1580
|
+
|
|
1520
1581
|
def lower_atomic_method_call(kind, receiver, expression, env:, type:)
|
|
1521
1582
|
receiver_type = infer_expression_type(receiver, env:)
|
|
1522
1583
|
elem_type = atomic_element_type(receiver_type)
|
|
@@ -1545,6 +1606,19 @@ module MilkTea
|
|
|
1545
1606
|
end
|
|
1546
1607
|
end
|
|
1547
1608
|
|
|
1609
|
+
def lower_simd_lane_with(_kind, receiver, expression, env:, type:)
|
|
1610
|
+
receiver_ir = lower_expression(receiver, env:)
|
|
1611
|
+
index_ir = lower_expression(expression.arguments[0].value, env:, expected_type: @ctx.types.fetch("int"))
|
|
1612
|
+
value_ir = lower_contextual_expression(expression.arguments[1].value, env:, expected_type: type.element_type)
|
|
1613
|
+
|
|
1614
|
+
IR::SimdLaneWith.new(
|
|
1615
|
+
src: receiver_ir,
|
|
1616
|
+
index: index_ir,
|
|
1617
|
+
value: value_ir,
|
|
1618
|
+
type:,
|
|
1619
|
+
)
|
|
1620
|
+
end
|
|
1621
|
+
|
|
1548
1622
|
def lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: false)
|
|
1549
1623
|
stateful = expression.arguments.length == 2
|
|
1550
1624
|
callee_key = if stateful
|
|
@@ -72,6 +72,13 @@ module MilkTea
|
|
|
72
72
|
lower_const_value_literal(element_type, element)
|
|
73
73
|
end
|
|
74
74
|
IR::ArrayLiteral.new(type:, elements:)
|
|
75
|
+
when Hash
|
|
76
|
+
fields = const_value.map do |name, field_value|
|
|
77
|
+
field_type = type.field(name)
|
|
78
|
+
raise LoweringError, "constant struct field #{name} not found in #{type}" unless field_type
|
|
79
|
+
IR::AggregateField.new(name:, value: lower_const_value_literal(field_type, field_value))
|
|
80
|
+
end
|
|
81
|
+
IR::AggregateLiteral.new(type:, fields:)
|
|
75
82
|
else
|
|
76
83
|
IR::IntegerLiteral.new(value: 0, type:)
|
|
77
84
|
end
|
|
@@ -1125,6 +1125,7 @@ module MilkTea
|
|
|
1125
1125
|
|
|
1126
1126
|
switch_expression = lowered_expression
|
|
1127
1127
|
string_if_chain = nil
|
|
1128
|
+
range_if_chain = nil
|
|
1128
1129
|
cases = if scrutinee_type.is_a?(Types::Variant)
|
|
1129
1130
|
kind_type = @ctx.types.fetch("int")
|
|
1130
1131
|
switch_expression = IR::Member.new(receiver: lowered_expression, member: "kind", type: kind_type)
|
|
@@ -1194,20 +1195,50 @@ module MilkTea
|
|
|
1194
1195
|
string_if_chain = else_body
|
|
1195
1196
|
nil
|
|
1196
1197
|
else
|
|
1197
|
-
expression.arms.
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1198
|
+
has_range_arms = expression.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
|
|
1199
|
+
|
|
1200
|
+
if has_range_arms
|
|
1201
|
+
bool_type = @ctx.types.fetch("bool")
|
|
1202
|
+
non_wildcard = expression.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
|
|
1203
|
+
wildcard = expression.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
|
|
1204
|
+
else_body = []
|
|
1205
|
+
if wildcard
|
|
1206
|
+
wildcard_env = duplicate_env(env)
|
|
1207
|
+
wildcard_value_setup, wildcard_prepared = prepare_expression_for_inline_lowering(wildcard.value, env: wildcard_env, expected_type: result_type)
|
|
1208
|
+
else_body = wildcard_value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(wildcard_prepared, env: wildcard_env, expected_type: result_type))]
|
|
1209
|
+
end
|
|
1210
|
+
non_wildcard.reverse_each do |arm|
|
|
1211
|
+
arm_env = duplicate_env(env)
|
|
1212
|
+
value_setup, prepared_value = prepare_expression_for_inline_lowering(arm.value, env: arm_env, expected_type: result_type)
|
|
1213
|
+
then_body = value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(prepared_value, env: arm_env, expected_type: result_type))]
|
|
1214
|
+
cond = if arm.pattern.is_a?(AST::RangeExpr)
|
|
1215
|
+
lower_range_match_condition(arm.pattern, switch_expression, bool_type, env: arm_env)
|
|
1216
|
+
else
|
|
1217
|
+
lit = lower_expression(arm.pattern, env: arm_env, expected_type: scrutinee_type)
|
|
1218
|
+
IR::Binary.new(operator: "==", left: switch_expression, right: lit, type: bool_type)
|
|
1219
|
+
end
|
|
1220
|
+
else_body = [IR::IfStmt.new(condition: cond, then_body:, else_body:)]
|
|
1221
|
+
end
|
|
1222
|
+
range_if_chain = else_body
|
|
1223
|
+
nil
|
|
1224
|
+
else
|
|
1225
|
+
expression.arms.map do |arm|
|
|
1226
|
+
arm_env = duplicate_env(env)
|
|
1227
|
+
value_setup, prepared_value = prepare_expression_for_inline_lowering(arm.value, env: arm_env, expected_type: result_type)
|
|
1228
|
+
body = value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(prepared_value, env: arm_env, expected_type: result_type))]
|
|
1229
|
+
if wildcard_arm_pattern?(arm.pattern)
|
|
1230
|
+
IR::SwitchDefaultCase.new(body: body)
|
|
1231
|
+
else
|
|
1232
|
+
IR::SwitchCase.new(value: lower_expression(arm.pattern, env: arm_env, expected_type: scrutinee_type), body: body)
|
|
1233
|
+
end
|
|
1205
1234
|
end
|
|
1206
1235
|
end
|
|
1207
1236
|
end
|
|
1208
1237
|
|
|
1209
1238
|
if string_if_chain
|
|
1210
1239
|
[setup + string_if_chain, AST::Identifier.new(name: result_name)]
|
|
1240
|
+
elsif range_if_chain
|
|
1241
|
+
[setup + range_if_chain, AST::Identifier.new(name: result_name)]
|
|
1211
1242
|
else
|
|
1212
1243
|
[setup + [IR::SwitchStmt.new(expression: switch_expression, cases: cases, exhaustive: true)], AST::Identifier.new(name: result_name)]
|
|
1213
1244
|
end
|