mt-lang 0.3.16 → 0.3.17
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/docs/language-design.md +3 -4
- data/docs/language-manual.md +3 -3
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/{binding_types.rb → bindings.rb} +1 -1
- data/lib/milk_tea/core/c_backend.rb +14 -15
- data/lib/milk_tea/core/compile_time.rb +181 -2
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
- data/lib/milk_tea/core/{compatibility_helpers.rb → intrinsics.rb} +2 -2
- data/lib/milk_tea/core/lexer.rb +10 -14
- data/lib/milk_tea/core/lowering.rb +18 -18
- data/lib/milk_tea/core/module_loader.rb +26 -15
- data/lib/milk_tea/core/parser.rb +17 -17
- data/lib/milk_tea/core/semantic_analyzer.rb +1 -1
- data/lib/milk_tea/core.rb +6 -2
- data/lib/milk_tea/dap/lldb_dap_backend.rb +153 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +1 -1
- data/lib/milk_tea/dap.rb +1 -1
- data/lib/milk_tea/lsp.rb +1 -7
- data/lib/milk_tea/{bindings/cli.rb → tooling/bindgen_cli.rb} +1 -1
- data/lib/milk_tea/tooling.rb +1 -1
- metadata +7 -9
- data/lib/milk_tea/core/compile_time/const_eval.rb +0 -182
- data/lib/milk_tea/core/module_loader/errors.rb +0 -23
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -156
- /data/lib/milk_tea/core/lowering/{async.rb → async/frame_builder.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2f84289b1bcb599ca81cbada0b1e1a925d9962ffe186471b84026f372fb358b
|
|
4
|
+
data.tar.gz: 566b98b015518f583e31fa2f0a75753c3b0abfd5a4047210945b3209c3caf1f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee114072cb26ed2527e0b3703cb7f8a913e37234e6937b3a307ce72363e8a6e10a34e0a149d508a8c02e64c1a0a0f5e9b1ae482b76f068bfb6bf0b8784f34a44
|
|
7
|
+
data.tar.gz: 9b29c88530cf6110381123ef8135434caa47aa85abf955a8bd9f25d93fa1b8059691d52804c17e4080a2a3d0ad916aa689470ec8d7719a7186fa62eddb026525
|
data/docs/language-design.md
CHANGED
|
@@ -521,7 +521,6 @@ The type system must stay simple, explicit, and close to C.
|
|
|
521
521
|
### Primitive types
|
|
522
522
|
|
|
523
523
|
- `bool`
|
|
524
|
-
- `byte`
|
|
525
524
|
- `char`
|
|
526
525
|
- `byte`, `short`, `int`, `long`
|
|
527
526
|
- `ubyte`, `ushort`, `uint`, `ulong`
|
|
@@ -587,7 +586,7 @@ Notes:
|
|
|
587
586
|
- If low-level code needs to validate raw `array[char, N]` storage as text, that conversion belongs in an explicit helper or imported boundary, not as a built-in method family on raw arrays.
|
|
588
587
|
- Addressable `str_buffer[N]` values also coerce to `span[char]`, so writable foreign text APIs can still accept builders directly when they do not want a second application-facing text abstraction.
|
|
589
588
|
- `str_buffer[N]` is not an ABI type. Raw bindings still spell writable text as `ptr[char]` or `span[char]`; `str_buffer[N]` is the caller-side text object.
|
|
590
|
-
- `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
|
|
589
|
+
- `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.assign_format(str)`, `.append_format(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
|
|
591
590
|
- `.assign(...)` replaces the current contents and traps at runtime if the new text exceeds capacity.
|
|
592
591
|
- `.append(...)` extends the current contents and traps at runtime if the appended text would exceed capacity.
|
|
593
592
|
- `.len()` returns the tracked text length, revalidating UTF-8 and rescanning for the trailing NUL if the builder was passed through a writable `span[char]` or `ptr[char]` alias.
|
|
@@ -650,7 +649,7 @@ joins the path with underscores (e.g., `module_Rectangle_Edge`).
|
|
|
650
649
|
|
|
651
650
|
#### Enums
|
|
652
651
|
|
|
653
|
-
Enums
|
|
652
|
+
Enums use an integer backing type, defaulting to `int` when omitted; members auto-increment from 0 or the previous explicit value.
|
|
654
653
|
|
|
655
654
|
```mt
|
|
656
655
|
enum WeaponKind: ubyte
|
|
@@ -661,7 +660,7 @@ enum WeaponKind: ubyte
|
|
|
661
660
|
|
|
662
661
|
#### Flags
|
|
663
662
|
|
|
664
|
-
Flags are named bitmasks with
|
|
663
|
+
Flags are named bitmasks with an integer backing type (defaulting to `int` when omitted).
|
|
665
664
|
|
|
666
665
|
```mt
|
|
667
666
|
flags WindowFlags: uint
|
data/docs/language-manual.md
CHANGED
|
@@ -1007,7 +1007,7 @@ Rules:
|
|
|
1007
1007
|
- `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
|
|
1008
1008
|
- `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
|
|
1009
1009
|
- `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
|
|
1010
|
-
- `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 (`+` `-` `*`
|
|
1010
|
+
- `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 (`+` `-` `*` `/`, and `%` for integer lanes), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
|
|
1011
1011
|
|
|
1012
1012
|
Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
|
|
1013
1013
|
|
|
@@ -1043,7 +1043,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
|
|
|
1043
1043
|
- `Option[T]`
|
|
1044
1044
|
- `Result[T, E]`
|
|
1045
1045
|
- `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
|
|
1046
|
-
- `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*`
|
|
1046
|
+
- `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/` (and `%` for integer lanes), 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.
|
|
1047
1047
|
- `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
|
|
1048
1048
|
- `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.
|
|
1049
1049
|
- `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
|
|
@@ -1054,7 +1054,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
|
|
|
1054
1054
|
- for pointer-like bases (`ptr[T]`, `const_ptr[T]`, `own[T]`, `cstr`, `fn(...)`, `proc(...)`, opaque), `T?` is a nullable pointer with `null` as the absent value
|
|
1055
1055
|
- for non-pointer value bases (`int`, `bool`, `float`, structs, ...), `T?` is stored inline by value as a tagged optional (a presence flag plus the value); it copies by value with no hidden heap allocation or pointer aliasing
|
|
1056
1056
|
- `null` expresses absence in any nullable context; the explicit typed `null[...]` form's target must be pointer-like
|
|
1057
|
-
- in nullable pointer-like contexts,
|
|
1057
|
+
- in nullable pointer-like contexts, `zero[ptr[T]]` is rejected; use `null` instead
|
|
1058
1058
|
- at an FFI boundary (`external` / `foreign function` parameters and returns), only pointer-like `T?` is allowed; a non-pointer value nullable such as `int?` is rejected — use `ptr[T]?` or pass an explicit struct
|
|
1059
1059
|
|
|
1060
1060
|
### 6.4 Generics
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -38,6 +38,20 @@ module MilkTea
|
|
|
38
38
|
# scanning a 600 KB source file) never trigger a false positive, yet low
|
|
39
39
|
# enough that a genuinely infinite loop fires a clear fatal within ~1 s.
|
|
40
40
|
LOOP_GUARD_MAX_ITERATIONS = 50_000_000
|
|
41
|
+
EQUALITY_OPERATORS = ["==", "!="].freeze
|
|
42
|
+
|
|
43
|
+
include AggregateUtils
|
|
44
|
+
include TypeSystem
|
|
45
|
+
include Reachability
|
|
46
|
+
include FeatureDetection
|
|
47
|
+
include TypeCollectors
|
|
48
|
+
include Statements
|
|
49
|
+
include ControlFlowEmission
|
|
50
|
+
include RuntimeHelpers
|
|
51
|
+
include FormatHelpers
|
|
52
|
+
include TypeDeclaration
|
|
53
|
+
include Expressions
|
|
54
|
+
include Reinterpret
|
|
41
55
|
|
|
42
56
|
def self.emit(program, emit_line_directives: true, debug_guards: false)
|
|
43
57
|
new(program, emit_line_directives:, debug_guards:).emit
|
|
@@ -294,20 +308,5 @@ module MilkTea
|
|
|
294
308
|
|
|
295
309
|
lines.join("\n").rstrip + "\n"
|
|
296
310
|
end
|
|
297
|
-
|
|
298
|
-
EQUALITY_OPERATORS = ["==", "!="].freeze
|
|
299
|
-
|
|
300
|
-
include AggregateUtils
|
|
301
|
-
include TypeSystem
|
|
302
|
-
include Reachability
|
|
303
|
-
include FeatureDetection
|
|
304
|
-
include TypeCollectors
|
|
305
|
-
include Statements
|
|
306
|
-
include ControlFlowEmission
|
|
307
|
-
include RuntimeHelpers
|
|
308
|
-
include FormatHelpers
|
|
309
|
-
include TypeDeclaration
|
|
310
|
-
include Expressions
|
|
311
|
-
include Reinterpret
|
|
312
311
|
end
|
|
313
312
|
end
|
|
@@ -1,6 +1,185 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "
|
|
3
|
+
require_relative "types/layout"
|
|
4
|
+
|
|
5
|
+
module MilkTea
|
|
6
|
+
module ConstEval
|
|
7
|
+
|
|
8
|
+
def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
|
|
9
|
+
Evaluator.new(
|
|
10
|
+
resolve_identifier:,
|
|
11
|
+
resolve_member_access:,
|
|
12
|
+
resolve_type_ref:,
|
|
13
|
+
resolve_call:,
|
|
14
|
+
).evaluate(expression)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.equality_result(left, right)
|
|
18
|
+
return left == right if left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
19
|
+
return left == right if left.is_a?(String) && right.is_a?(String)
|
|
20
|
+
return left == right if boolean_value?(left) && boolean_value?(right)
|
|
21
|
+
return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
|
|
22
|
+
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.boolean_value?(value)
|
|
27
|
+
value == true || value == false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Evaluator
|
|
31
|
+
def initialize(resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
|
|
32
|
+
@resolve_identifier = resolve_identifier
|
|
33
|
+
@resolve_member_access = resolve_member_access
|
|
34
|
+
@resolve_type_ref = resolve_type_ref
|
|
35
|
+
@resolve_call = resolve_call
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def evaluate(expression)
|
|
39
|
+
case expression
|
|
40
|
+
when AST::ErrorExpr
|
|
41
|
+
nil
|
|
42
|
+
when AST::ExpressionList
|
|
43
|
+
expression.elements.filter_map { |element| evaluate(element) }
|
|
44
|
+
when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
|
|
45
|
+
expression.value
|
|
46
|
+
when AST::StringLiteral
|
|
47
|
+
expression.value
|
|
48
|
+
when AST::Identifier
|
|
49
|
+
@resolve_identifier&.call(expression)
|
|
50
|
+
when AST::MemberAccess
|
|
51
|
+
@resolve_member_access&.call(expression)
|
|
52
|
+
when AST::Call
|
|
53
|
+
@resolve_call&.call(expression)
|
|
54
|
+
when AST::Specialization
|
|
55
|
+
@resolve_call&.call(expression)
|
|
56
|
+
when AST::SizeofExpr
|
|
57
|
+
type = resolve_layout_type(expression.type)
|
|
58
|
+
type && Layout.size_of(type)
|
|
59
|
+
when AST::AlignofExpr
|
|
60
|
+
type = resolve_layout_type(expression.type)
|
|
61
|
+
type && Layout.alignment_of(type)
|
|
62
|
+
when AST::OffsetofExpr
|
|
63
|
+
type = resolve_layout_type(expression.type)
|
|
64
|
+
if type
|
|
65
|
+
result = Layout.offset_of(type, expression.field)
|
|
66
|
+
return result if result
|
|
67
|
+
|
|
68
|
+
id_expr = AST::Identifier.new(name: expression.field)
|
|
69
|
+
value = @resolve_identifier&.call(id_expr)
|
|
70
|
+
if value.is_a?(Types::FieldHandle)
|
|
71
|
+
return Layout.offset_of(type, value.field_name)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
nil
|
|
75
|
+
when AST::UnaryOp
|
|
76
|
+
evaluate_unary(expression)
|
|
77
|
+
when AST::BinaryOp
|
|
78
|
+
evaluate_binary(expression)
|
|
79
|
+
when AST::IfExpr
|
|
80
|
+
condition = evaluate(expression.condition)
|
|
81
|
+
return unless ConstEval.boolean_value?(condition)
|
|
82
|
+
|
|
83
|
+
evaluate(condition ? expression.then_expression : expression.else_expression)
|
|
84
|
+
else
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def resolve_layout_type(type_ref)
|
|
90
|
+
return unless @resolve_type_ref
|
|
91
|
+
|
|
92
|
+
@resolve_type_ref.call(type_ref)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def evaluate_unary(expression)
|
|
96
|
+
operand = evaluate(expression.operand)
|
|
97
|
+
|
|
98
|
+
case expression.operator
|
|
99
|
+
when "+"
|
|
100
|
+
operand.is_a?(Numeric) ? operand : nil
|
|
101
|
+
when "-"
|
|
102
|
+
operand.is_a?(Numeric) ? -operand : nil
|
|
103
|
+
when "~"
|
|
104
|
+
operand.is_a?(Integer) ? ~operand : nil
|
|
105
|
+
when "not"
|
|
106
|
+
ConstEval.boolean_value?(operand) ? !operand : nil
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def evaluate_binary(expression)
|
|
111
|
+
left = evaluate(expression.left)
|
|
112
|
+
|
|
113
|
+
case expression.operator
|
|
114
|
+
when "and"
|
|
115
|
+
return unless ConstEval.boolean_value?(left)
|
|
116
|
+
return false if left == false
|
|
117
|
+
|
|
118
|
+
right = evaluate(expression.right)
|
|
119
|
+
return right if ConstEval.boolean_value?(right)
|
|
120
|
+
|
|
121
|
+
return nil
|
|
122
|
+
when "or"
|
|
123
|
+
return unless ConstEval.boolean_value?(left)
|
|
124
|
+
return true if left == true
|
|
125
|
+
|
|
126
|
+
right = evaluate(expression.right)
|
|
127
|
+
return right if ConstEval.boolean_value?(right)
|
|
128
|
+
|
|
129
|
+
return nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
right = evaluate(expression.right)
|
|
133
|
+
|
|
134
|
+
case expression.operator
|
|
135
|
+
when "=="
|
|
136
|
+
ConstEval.equality_result(left, right)
|
|
137
|
+
when "!="
|
|
138
|
+
result = ConstEval.equality_result(left, right)
|
|
139
|
+
result.nil? ? nil : !result
|
|
140
|
+
when "+"
|
|
141
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
|
|
142
|
+
when "-"
|
|
143
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
|
|
144
|
+
when "*"
|
|
145
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
|
|
146
|
+
when "/"
|
|
147
|
+
return unless left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
148
|
+
raise CompileTime::Error, "division by zero" if zero_numeric?(right)
|
|
149
|
+
|
|
150
|
+
left / right
|
|
151
|
+
when "%"
|
|
152
|
+
return unless left.is_a?(Integer) && right.is_a?(Integer)
|
|
153
|
+
raise CompileTime::Error, "modulo by zero" if right.zero?
|
|
154
|
+
|
|
155
|
+
left % right
|
|
156
|
+
when "<<"
|
|
157
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? left << right : nil
|
|
158
|
+
when ">>"
|
|
159
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? left >> right : nil
|
|
160
|
+
when "&"
|
|
161
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? left & right : nil
|
|
162
|
+
when "|"
|
|
163
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? left | right : nil
|
|
164
|
+
when "^"
|
|
165
|
+
left.is_a?(Integer) && right.is_a?(Integer) ? left ^ right : nil
|
|
166
|
+
when "<"
|
|
167
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left < right : nil
|
|
168
|
+
when "<="
|
|
169
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left <= right : nil
|
|
170
|
+
when ">"
|
|
171
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left > right : nil
|
|
172
|
+
when ">="
|
|
173
|
+
left.is_a?(Numeric) && right.is_a?(Numeric) ? left >= right : nil
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def zero_numeric?(value)
|
|
178
|
+
(value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
4
183
|
|
|
5
184
|
module MilkTea
|
|
6
185
|
module CompileTime
|
|
@@ -354,4 +533,4 @@ module MilkTea
|
|
|
354
533
|
end
|
|
355
534
|
end
|
|
356
535
|
end
|
|
357
|
-
end
|
|
536
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require_relative "types/predicates"
|
|
4
4
|
|
|
5
5
|
module MilkTea
|
|
6
|
-
module
|
|
6
|
+
module Intrinsics
|
|
7
7
|
include Types::Predicates
|
|
8
8
|
|
|
9
9
|
BUILTIN_CSTR = Types::Registry.primitive("cstr")
|
|
@@ -78,4 +78,4 @@ module MilkTea
|
|
|
78
78
|
expression.is_a?(AST::StringLiteral) && !expression.cstring && expected_type == BUILTIN_CSTR
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
|
-
end
|
|
81
|
+
end
|
data/lib/milk_tea/core/lexer.rb
CHANGED
|
@@ -106,6 +106,16 @@ module MilkTea
|
|
|
106
106
|
BIN_DIGIT_BYTE = Array.new(256) { |b| b.chr.match?(/[01_]/) }.freeze
|
|
107
107
|
SPACE_BYTE = " ".ord
|
|
108
108
|
|
|
109
|
+
include CharacterClasses
|
|
110
|
+
include Trivia
|
|
111
|
+
include Indentation
|
|
112
|
+
include Numbers
|
|
113
|
+
include Strings
|
|
114
|
+
include Heredocs
|
|
115
|
+
include FormatStrings
|
|
116
|
+
include Symbols
|
|
117
|
+
include Recovery
|
|
118
|
+
|
|
109
119
|
def self.lex(source, path: nil, mode: :syntax_only, recovery_errors: nil)
|
|
110
120
|
result = new(source, path: path, mode:, recovery_errors:).lex
|
|
111
121
|
mode == :with_trivia ? result.tokens : result
|
|
@@ -170,16 +180,6 @@ module MilkTea
|
|
|
170
180
|
@tokens
|
|
171
181
|
end
|
|
172
182
|
|
|
173
|
-
include CharacterClasses
|
|
174
|
-
include Trivia
|
|
175
|
-
include Indentation
|
|
176
|
-
include Numbers
|
|
177
|
-
include Strings
|
|
178
|
-
include Heredocs
|
|
179
|
-
include FormatStrings
|
|
180
|
-
include Symbols
|
|
181
|
-
include Recovery
|
|
182
|
-
|
|
183
183
|
def lex_line(lines, line_index, line, line_number, line_offset, has_newline:)
|
|
184
184
|
tab_index = line.index("\t")
|
|
185
185
|
if tab_index
|
|
@@ -371,8 +371,6 @@ module MilkTea
|
|
|
371
371
|
1
|
|
372
372
|
end
|
|
373
373
|
|
|
374
|
-
# ── newline emission ────────────────────────────────────────────
|
|
375
|
-
|
|
376
374
|
def emit_line_newline(line, line_number, line_offset, has_newline)
|
|
377
375
|
newline_start = line_offset + line.bytesize
|
|
378
376
|
newline_end = has_newline ? (newline_start + 1) : newline_start
|
|
@@ -393,8 +391,6 @@ module MilkTea
|
|
|
393
391
|
end
|
|
394
392
|
end
|
|
395
393
|
|
|
396
|
-
# ── token construction ──────────────────────────────────────────
|
|
397
|
-
|
|
398
394
|
def token(type, lexeme, literal, line, column, start_offset:, end_offset:)
|
|
399
395
|
Token.new(
|
|
400
396
|
type:,
|
|
@@ -31,7 +31,7 @@ require_relative "lowering/functions"
|
|
|
31
31
|
require_relative "lowering/async/analysis"
|
|
32
32
|
require_relative "lowering/async/normalization"
|
|
33
33
|
require_relative "lowering/async/lowering"
|
|
34
|
-
require_relative "lowering/async"
|
|
34
|
+
require_relative "lowering/async/frame_builder"
|
|
35
35
|
require_relative "lowering/block"
|
|
36
36
|
require_relative "lowering/proc"
|
|
37
37
|
require_relative "lowering/loops"
|
|
@@ -85,11 +85,27 @@ module MilkTea
|
|
|
85
85
|
OrderResolution = Data.define(:target_type, :binding, :callee_name)
|
|
86
86
|
|
|
87
87
|
class Lowerer
|
|
88
|
-
include
|
|
88
|
+
include Intrinsics
|
|
89
89
|
|
|
90
90
|
attr_accessor :bypass_sema_type_cache
|
|
91
91
|
attr_reader :recorded_expr_types
|
|
92
92
|
|
|
93
|
+
include LowererScans
|
|
94
|
+
include LowererDeclarations
|
|
95
|
+
include LowererEvents
|
|
96
|
+
include LowererFunctions
|
|
97
|
+
include LowererAsync
|
|
98
|
+
include LowererBlock
|
|
99
|
+
include LowererProc
|
|
100
|
+
include LowererLoops
|
|
101
|
+
include LowererExpressions
|
|
102
|
+
include LowererCalls
|
|
103
|
+
include LowererForeignCstr
|
|
104
|
+
include LowererStrBuffer
|
|
105
|
+
include LowererResolve
|
|
106
|
+
include LowererDyn
|
|
107
|
+
include LowererUtils
|
|
108
|
+
|
|
93
109
|
def initialize(program)
|
|
94
110
|
@program = program
|
|
95
111
|
@ctx = ModuleContext.new
|
|
@@ -358,21 +374,5 @@ module MilkTea
|
|
|
358
374
|
ordered.concat(modules.values - ordered)
|
|
359
375
|
ordered
|
|
360
376
|
end
|
|
361
|
-
|
|
362
|
-
include LowererScans
|
|
363
|
-
include LowererDeclarations
|
|
364
|
-
include LowererEvents
|
|
365
|
-
include LowererFunctions
|
|
366
|
-
include LowererAsync
|
|
367
|
-
include LowererBlock
|
|
368
|
-
include LowererProc
|
|
369
|
-
include LowererLoops
|
|
370
|
-
include LowererExpressions
|
|
371
|
-
include LowererCalls
|
|
372
|
-
include LowererForeignCstr
|
|
373
|
-
include LowererStrBuffer
|
|
374
|
-
include LowererResolve
|
|
375
|
-
include LowererDyn
|
|
376
|
-
include LowererUtils
|
|
377
377
|
end
|
|
378
378
|
end
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "module_loader/errors"
|
|
4
|
-
require_relative "module_path_resolver"
|
|
5
|
-
require_relative "module_binder"
|
|
6
|
-
require_relative "async_runtime_installer"
|
|
7
|
-
require_relative "prelude_installer"
|
|
8
|
-
|
|
9
3
|
module MilkTea
|
|
4
|
+
class ModuleLoadError < StandardError
|
|
5
|
+
attr_reader :path, :line, :column
|
|
6
|
+
|
|
7
|
+
def initialize(message, path:, line: nil, column: nil)
|
|
8
|
+
@path = path
|
|
9
|
+
@line = line
|
|
10
|
+
@column = column
|
|
11
|
+
super("#{message}: #{path}")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def code
|
|
15
|
+
"module/error"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
10
19
|
class ModuleLoader
|
|
20
|
+
ImportResolution = Data.define(:modules, :errors)
|
|
21
|
+
ImportResolutionError = Data.define(:import, :error)
|
|
11
22
|
Program = Data.define(:root_path, :root_analysis, :analyses_by_path, :analyses_by_module_name)
|
|
12
23
|
PLATFORM_SUFFIXES = {
|
|
13
24
|
"linux" => :linux,
|
|
@@ -90,6 +101,15 @@ module MilkTea
|
|
|
90
101
|
MilkTea.host_platform
|
|
91
102
|
end
|
|
92
103
|
|
|
104
|
+
def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
|
|
105
|
+
if error_class == ModuleLoadError
|
|
106
|
+
raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
|
|
110
|
+
raise(error_class || ArgumentError, message)
|
|
111
|
+
end
|
|
112
|
+
|
|
93
113
|
def initialize(module_roots: [MilkTea.root], package_graph: nil, shared_cache: nil, source_overrides: nil, platform: nil)
|
|
94
114
|
@module_roots = module_roots.map { |root| File.expand_path(root.to_s) }
|
|
95
115
|
@ast_cache = {}
|
|
@@ -380,15 +400,6 @@ module MilkTea
|
|
|
380
400
|
@collecting_path_errors
|
|
381
401
|
end
|
|
382
402
|
|
|
383
|
-
def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
|
|
384
|
-
if error_class == ModuleLoadError
|
|
385
|
-
raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
|
|
389
|
-
raise(error_class || ArgumentError, message)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
403
|
def check_path(path)
|
|
393
404
|
resolved_path, ast, cached = check_module_cache(path)
|
|
394
405
|
return cached if cached
|
data/lib/milk_tea/core/parser.rb
CHANGED
|
@@ -155,6 +155,23 @@ module MilkTea
|
|
|
155
155
|
next_token.nil? || %i[newline eof].include?(next_token.type)
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
+
def advance
|
|
159
|
+
@current += 1 unless eof?
|
|
160
|
+
previous
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def eof?
|
|
164
|
+
peek.type == :eof
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def peek
|
|
168
|
+
@tokens[@current]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def previous
|
|
172
|
+
@tokens[@current - 1]
|
|
173
|
+
end
|
|
174
|
+
|
|
158
175
|
def match(*types)
|
|
159
176
|
return false unless types.any? { |type| check(type) }
|
|
160
177
|
|
|
@@ -219,23 +236,6 @@ module MilkTea
|
|
|
219
236
|
true
|
|
220
237
|
end
|
|
221
238
|
|
|
222
|
-
def advance
|
|
223
|
-
@current += 1 unless eof?
|
|
224
|
-
previous
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def eof?
|
|
228
|
-
peek.type == :eof
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def peek
|
|
232
|
-
@tokens[@current]
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def previous
|
|
236
|
-
@tokens[@current - 1]
|
|
237
|
-
end
|
|
238
|
-
|
|
239
239
|
def error(token, message)
|
|
240
240
|
ParseError.new(message, token:, path: @path)
|
|
241
241
|
end
|
data/lib/milk_tea/core.rb
CHANGED
|
@@ -10,11 +10,15 @@ require_relative "core/cst"
|
|
|
10
10
|
require_relative "core/cst_builder"
|
|
11
11
|
require_relative "core/ast"
|
|
12
12
|
require_relative "core/types"
|
|
13
|
-
require_relative "core/
|
|
13
|
+
require_relative "core/bindings"
|
|
14
14
|
require_relative "core/compile_time"
|
|
15
|
-
require_relative "core/
|
|
15
|
+
require_relative "core/intrinsics"
|
|
16
16
|
require_relative "core/parser"
|
|
17
17
|
require_relative "core/module_roots"
|
|
18
|
+
require_relative "core/module_path_resolver"
|
|
19
|
+
require_relative "core/module_binder"
|
|
20
|
+
require_relative "core/async_runtime_installer"
|
|
21
|
+
require_relative "core/prelude_installer"
|
|
18
22
|
require_relative "core/module_loader"
|
|
19
23
|
require_relative "core/semantic_analyzer"
|
|
20
24
|
require_relative "core/ir"
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "thread"
|
|
5
|
+
require "timeout"
|
|
6
|
+
|
|
7
|
+
module MilkTea
|
|
8
|
+
module DAP
|
|
9
|
+
class LLDBDAPBackend
|
|
10
|
+
def initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil)
|
|
11
|
+
@adapter_command = adapter_command
|
|
12
|
+
@on_event = on_event
|
|
13
|
+
@on_request = on_request
|
|
14
|
+
@protocol = nil
|
|
15
|
+
@stdin = nil
|
|
16
|
+
@stdout = nil
|
|
17
|
+
@stderr = nil
|
|
18
|
+
@wait_thread = nil
|
|
19
|
+
@reader_thread = nil
|
|
20
|
+
@stderr_thread = nil
|
|
21
|
+
@write_mutex = Mutex.new
|
|
22
|
+
@pending_mutex = Mutex.new
|
|
23
|
+
@pending = {}
|
|
24
|
+
@next_seq = 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def start!
|
|
28
|
+
return if running?
|
|
29
|
+
|
|
30
|
+
@stdin, @stdout, @stderr, @wait_thread = Open3.popen3(*@adapter_command)
|
|
31
|
+
@protocol = Protocol.new(input: @stdout, output: @stdin)
|
|
32
|
+
|
|
33
|
+
@reader_thread = Thread.new { read_loop }
|
|
34
|
+
@stderr_thread = Thread.new { drain_stderr }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def running?
|
|
38
|
+
!@wait_thread.nil? && @wait_thread.alive?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def request(command, arguments = {}, timeout: 5)
|
|
42
|
+
start! unless running?
|
|
43
|
+
|
|
44
|
+
seq = next_seq
|
|
45
|
+
queue = Queue.new
|
|
46
|
+
@pending_mutex.synchronize do
|
|
47
|
+
@pending[seq] = queue
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@write_mutex.synchronize do
|
|
51
|
+
@protocol.write_message({
|
|
52
|
+
seq: seq,
|
|
53
|
+
type: "request",
|
|
54
|
+
command: command,
|
|
55
|
+
arguments: arguments
|
|
56
|
+
})
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Timeout.timeout(timeout) { queue.pop }
|
|
60
|
+
rescue Timeout::Error
|
|
61
|
+
{
|
|
62
|
+
"type" => "response",
|
|
63
|
+
"request_seq" => seq,
|
|
64
|
+
"success" => false,
|
|
65
|
+
"message" => "backend request timed out: #{command}"
|
|
66
|
+
}
|
|
67
|
+
ensure
|
|
68
|
+
@pending_mutex.synchronize { @pending.delete(seq) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def stop!
|
|
72
|
+
@stdin&.close
|
|
73
|
+
@stdout&.close
|
|
74
|
+
@stderr&.close
|
|
75
|
+
if @wait_thread&.alive?
|
|
76
|
+
Process.kill("TERM", @wait_thread.pid)
|
|
77
|
+
end
|
|
78
|
+
rescue StandardError
|
|
79
|
+
nil
|
|
80
|
+
ensure
|
|
81
|
+
@wait_thread&.join(0.2)
|
|
82
|
+
@reader_thread&.join(0.2)
|
|
83
|
+
@stderr_thread&.join(0.2)
|
|
84
|
+
@stdin = nil
|
|
85
|
+
@stdout = nil
|
|
86
|
+
@stderr = nil
|
|
87
|
+
@wait_thread = nil
|
|
88
|
+
@reader_thread = nil
|
|
89
|
+
@stderr_thread = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def next_seq
|
|
93
|
+
seq = @next_seq
|
|
94
|
+
@next_seq += 1
|
|
95
|
+
seq
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def read_loop
|
|
99
|
+
loop do
|
|
100
|
+
message = @protocol.read_message
|
|
101
|
+
break if message.nil?
|
|
102
|
+
|
|
103
|
+
if message["type"] == "response"
|
|
104
|
+
queue = @pending_mutex.synchronize { @pending[message["request_seq"]] }
|
|
105
|
+
queue&.push(message)
|
|
106
|
+
elsif message["type"] == "event"
|
|
107
|
+
@on_event&.call(message)
|
|
108
|
+
elsif message["type"] == "request"
|
|
109
|
+
handle_adapter_request(message)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
rescue StandardError
|
|
113
|
+
nil
|
|
114
|
+
ensure
|
|
115
|
+
@pending_mutex.synchronize do
|
|
116
|
+
@pending.each_value do |queue|
|
|
117
|
+
queue.push({
|
|
118
|
+
"type" => "response",
|
|
119
|
+
"success" => false,
|
|
120
|
+
"message" => "backend closed"
|
|
121
|
+
})
|
|
122
|
+
end
|
|
123
|
+
@pending.clear
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def drain_stderr
|
|
128
|
+
return unless @stderr
|
|
129
|
+
|
|
130
|
+
@stderr.each_line do |_line|
|
|
131
|
+
# Intentionally ignored in this bridge layer.
|
|
132
|
+
end
|
|
133
|
+
rescue StandardError
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def handle_adapter_request(message)
|
|
138
|
+
response = @on_request&.call(message)
|
|
139
|
+
response ||= {
|
|
140
|
+
"type" => "response",
|
|
141
|
+
"request_seq" => message["seq"],
|
|
142
|
+
"success" => false,
|
|
143
|
+
"command" => message["command"],
|
|
144
|
+
"message" => "unsupported reverse request: #{message['command']}"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@write_mutex.synchronize do
|
|
148
|
+
@protocol.write_message(response)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -22,7 +22,7 @@ module MilkTea
|
|
|
22
22
|
@lldb_backend = if @backend_factory
|
|
23
23
|
build_backend_via_factory(adapter_command)
|
|
24
24
|
else
|
|
25
|
-
|
|
25
|
+
LLDBDAPBackend.new(
|
|
26
26
|
adapter_command: adapter_command,
|
|
27
27
|
on_event: method(:handle_backend_event),
|
|
28
28
|
on_request: method(:handle_backend_request)
|
data/lib/milk_tea/dap.rb
CHANGED
data/lib/milk_tea/lsp.rb
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module MilkTea
|
|
4
|
-
module LSP
|
|
5
|
-
# LSP implementation - language server protocol support
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
3
|
require_relative 'lsp/protocol'
|
|
10
4
|
require_relative 'lsp/dependency_resolution'
|
|
11
5
|
require_relative 'lsp/workspace'
|
|
12
6
|
require_relative 'lsp/diagnostics'
|
|
13
|
-
require_relative 'lsp/server'
|
|
7
|
+
require_relative 'lsp/server'
|
data/lib/milk_tea/tooling.rb
CHANGED
|
@@ -18,5 +18,5 @@ require_relative "tooling/linter"
|
|
|
18
18
|
require_relative "tooling/docs_app"
|
|
19
19
|
require_relative "tooling/project_scaffold"
|
|
20
20
|
require_relative "tooling/toolchain_cli"
|
|
21
|
-
require_relative "
|
|
21
|
+
require_relative "tooling/bindgen_cli"
|
|
22
22
|
require_relative "tooling/cli"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mt-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -158,7 +158,6 @@ files:
|
|
|
158
158
|
- lib/milk_tea/bindings/bindgen/emitter.rb
|
|
159
159
|
- lib/milk_tea/bindings/bindgen/overrides.rb
|
|
160
160
|
- lib/milk_tea/bindings/bindgen/type_mapper.rb
|
|
161
|
-
- lib/milk_tea/bindings/cli.rb
|
|
162
161
|
- lib/milk_tea/bindings/imported_bindings.rb
|
|
163
162
|
- lib/milk_tea/bindings/imported_bindings/defaults.rb
|
|
164
163
|
- lib/milk_tea/bindings/imported_bindings/generator.rb
|
|
@@ -185,7 +184,7 @@ files:
|
|
|
185
184
|
- lib/milk_tea/core.rb
|
|
186
185
|
- lib/milk_tea/core/ast.rb
|
|
187
186
|
- lib/milk_tea/core/async_runtime_installer.rb
|
|
188
|
-
- lib/milk_tea/core/
|
|
187
|
+
- lib/milk_tea/core/bindings.rb
|
|
189
188
|
- lib/milk_tea/core/bindings/attribute_binding.rb
|
|
190
189
|
- lib/milk_tea/core/bindings/function_binding.rb
|
|
191
190
|
- lib/milk_tea/core/bindings/module_binding.rb
|
|
@@ -203,9 +202,7 @@ files:
|
|
|
203
202
|
- lib/milk_tea/core/c_backend/type_collectors.rb
|
|
204
203
|
- lib/milk_tea/core/c_backend/type_declaration.rb
|
|
205
204
|
- lib/milk_tea/core/c_backend/type_system.rb
|
|
206
|
-
- lib/milk_tea/core/compatibility_helpers.rb
|
|
207
205
|
- lib/milk_tea/core/compile_time.rb
|
|
208
|
-
- lib/milk_tea/core/compile_time/const_eval.rb
|
|
209
206
|
- lib/milk_tea/core/control_flow.rb
|
|
210
207
|
- lib/milk_tea/core/control_flow/builder.rb
|
|
211
208
|
- lib/milk_tea/core/control_flow/constant_propagation.rb
|
|
@@ -218,6 +215,7 @@ files:
|
|
|
218
215
|
- lib/milk_tea/core/control_flow/termination.rb
|
|
219
216
|
- lib/milk_tea/core/cst.rb
|
|
220
217
|
- lib/milk_tea/core/cst_builder.rb
|
|
218
|
+
- lib/milk_tea/core/intrinsics.rb
|
|
221
219
|
- lib/milk_tea/core/ir.rb
|
|
222
220
|
- lib/milk_tea/core/keywords.rb
|
|
223
221
|
- lib/milk_tea/core/lexer.rb
|
|
@@ -232,8 +230,8 @@ files:
|
|
|
232
230
|
- lib/milk_tea/core/lexer/trivia.rb
|
|
233
231
|
- lib/milk_tea/core/lowering.rb
|
|
234
232
|
- lib/milk_tea/core/lowering/artifacts.rb
|
|
235
|
-
- lib/milk_tea/core/lowering/async.rb
|
|
236
233
|
- lib/milk_tea/core/lowering/async/analysis.rb
|
|
234
|
+
- lib/milk_tea/core/lowering/async/frame_builder.rb
|
|
237
235
|
- lib/milk_tea/core/lowering/async/lowering.rb
|
|
238
236
|
- lib/milk_tea/core/lowering/async/normalization.rb
|
|
239
237
|
- lib/milk_tea/core/lowering/block.rb
|
|
@@ -253,7 +251,6 @@ files:
|
|
|
253
251
|
- lib/milk_tea/core/lowering/utils.rb
|
|
254
252
|
- lib/milk_tea/core/module_binder.rb
|
|
255
253
|
- lib/milk_tea/core/module_loader.rb
|
|
256
|
-
- lib/milk_tea/core/module_loader/errors.rb
|
|
257
254
|
- lib/milk_tea/core/module_path_resolver.rb
|
|
258
255
|
- lib/milk_tea/core/module_roots.rb
|
|
259
256
|
- lib/milk_tea/core/parser.rb
|
|
@@ -294,7 +291,7 @@ files:
|
|
|
294
291
|
- lib/milk_tea/core/types/registry.rb
|
|
295
292
|
- lib/milk_tea/core/types/visitor.rb
|
|
296
293
|
- lib/milk_tea/dap.rb
|
|
297
|
-
- lib/milk_tea/dap/
|
|
294
|
+
- lib/milk_tea/dap/lldb_dap_backend.rb
|
|
298
295
|
- lib/milk_tea/dap/protocol.rb
|
|
299
296
|
- lib/milk_tea/dap/server.rb
|
|
300
297
|
- lib/milk_tea/dap/server/breakpoints.rb
|
|
@@ -361,6 +358,7 @@ files:
|
|
|
361
358
|
- lib/milk_tea/packages/version.rb
|
|
362
359
|
- lib/milk_tea/tooling.rb
|
|
363
360
|
- lib/milk_tea/tooling/asset_pack.rb
|
|
361
|
+
- lib/milk_tea/tooling/bindgen_cli.rb
|
|
364
362
|
- lib/milk_tea/tooling/build.rb
|
|
365
363
|
- lib/milk_tea/tooling/build_cache.rb
|
|
366
364
|
- lib/milk_tea/tooling/cli.rb
|
|
@@ -605,7 +603,7 @@ metadata:
|
|
|
605
603
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
606
604
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
607
605
|
post_install_message: |
|
|
608
|
-
Milk Tea 0.3.
|
|
606
|
+
Milk Tea 0.3.17 installed!
|
|
609
607
|
|
|
610
608
|
System requirements:
|
|
611
609
|
- A C compiler (gcc or clang) must be available on PATH
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../types/layout"
|
|
4
|
-
|
|
5
|
-
module MilkTea
|
|
6
|
-
module ConstEval
|
|
7
|
-
|
|
8
|
-
def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
|
|
9
|
-
Evaluator.new(
|
|
10
|
-
resolve_identifier:,
|
|
11
|
-
resolve_member_access:,
|
|
12
|
-
resolve_type_ref:,
|
|
13
|
-
resolve_call:,
|
|
14
|
-
).evaluate(expression)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def self.equality_result(left, right)
|
|
18
|
-
return left == right if left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
19
|
-
return left == right if left.is_a?(String) && right.is_a?(String)
|
|
20
|
-
return left == right if boolean_value?(left) && boolean_value?(right)
|
|
21
|
-
return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
|
|
22
|
-
|
|
23
|
-
nil
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def self.boolean_value?(value)
|
|
27
|
-
value == true || value == false
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
class Evaluator
|
|
31
|
-
def initialize(resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
|
|
32
|
-
@resolve_identifier = resolve_identifier
|
|
33
|
-
@resolve_member_access = resolve_member_access
|
|
34
|
-
@resolve_type_ref = resolve_type_ref
|
|
35
|
-
@resolve_call = resolve_call
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def evaluate(expression)
|
|
39
|
-
case expression
|
|
40
|
-
when AST::ErrorExpr
|
|
41
|
-
nil
|
|
42
|
-
when AST::ExpressionList
|
|
43
|
-
expression.elements.filter_map { |element| evaluate(element) }
|
|
44
|
-
when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
|
|
45
|
-
expression.value
|
|
46
|
-
when AST::StringLiteral
|
|
47
|
-
expression.value
|
|
48
|
-
when AST::Identifier
|
|
49
|
-
@resolve_identifier&.call(expression)
|
|
50
|
-
when AST::MemberAccess
|
|
51
|
-
@resolve_member_access&.call(expression)
|
|
52
|
-
when AST::Call
|
|
53
|
-
@resolve_call&.call(expression)
|
|
54
|
-
when AST::Specialization
|
|
55
|
-
@resolve_call&.call(expression)
|
|
56
|
-
when AST::SizeofExpr
|
|
57
|
-
type = resolve_layout_type(expression.type)
|
|
58
|
-
type && Layout.size_of(type)
|
|
59
|
-
when AST::AlignofExpr
|
|
60
|
-
type = resolve_layout_type(expression.type)
|
|
61
|
-
type && Layout.alignment_of(type)
|
|
62
|
-
when AST::OffsetofExpr
|
|
63
|
-
type = resolve_layout_type(expression.type)
|
|
64
|
-
if type
|
|
65
|
-
result = Layout.offset_of(type, expression.field)
|
|
66
|
-
return result if result
|
|
67
|
-
|
|
68
|
-
id_expr = AST::Identifier.new(name: expression.field)
|
|
69
|
-
value = @resolve_identifier&.call(id_expr)
|
|
70
|
-
if value.is_a?(Types::FieldHandle)
|
|
71
|
-
return Layout.offset_of(type, value.field_name)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
nil
|
|
75
|
-
when AST::UnaryOp
|
|
76
|
-
evaluate_unary(expression)
|
|
77
|
-
when AST::BinaryOp
|
|
78
|
-
evaluate_binary(expression)
|
|
79
|
-
when AST::IfExpr
|
|
80
|
-
condition = evaluate(expression.condition)
|
|
81
|
-
return unless ConstEval.boolean_value?(condition)
|
|
82
|
-
|
|
83
|
-
evaluate(condition ? expression.then_expression : expression.else_expression)
|
|
84
|
-
else
|
|
85
|
-
nil
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def resolve_layout_type(type_ref)
|
|
90
|
-
return unless @resolve_type_ref
|
|
91
|
-
|
|
92
|
-
@resolve_type_ref.call(type_ref)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def evaluate_unary(expression)
|
|
96
|
-
operand = evaluate(expression.operand)
|
|
97
|
-
|
|
98
|
-
case expression.operator
|
|
99
|
-
when "+"
|
|
100
|
-
operand.is_a?(Numeric) ? operand : nil
|
|
101
|
-
when "-"
|
|
102
|
-
operand.is_a?(Numeric) ? -operand : nil
|
|
103
|
-
when "~"
|
|
104
|
-
operand.is_a?(Integer) ? ~operand : nil
|
|
105
|
-
when "not"
|
|
106
|
-
ConstEval.boolean_value?(operand) ? !operand : nil
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def evaluate_binary(expression)
|
|
111
|
-
left = evaluate(expression.left)
|
|
112
|
-
|
|
113
|
-
case expression.operator
|
|
114
|
-
when "and"
|
|
115
|
-
return unless ConstEval.boolean_value?(left)
|
|
116
|
-
return false if left == false
|
|
117
|
-
|
|
118
|
-
right = evaluate(expression.right)
|
|
119
|
-
return right if ConstEval.boolean_value?(right)
|
|
120
|
-
|
|
121
|
-
return nil
|
|
122
|
-
when "or"
|
|
123
|
-
return unless ConstEval.boolean_value?(left)
|
|
124
|
-
return true if left == true
|
|
125
|
-
|
|
126
|
-
right = evaluate(expression.right)
|
|
127
|
-
return right if ConstEval.boolean_value?(right)
|
|
128
|
-
|
|
129
|
-
return nil
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
right = evaluate(expression.right)
|
|
133
|
-
|
|
134
|
-
case expression.operator
|
|
135
|
-
when "=="
|
|
136
|
-
ConstEval.equality_result(left, right)
|
|
137
|
-
when "!="
|
|
138
|
-
result = ConstEval.equality_result(left, right)
|
|
139
|
-
result.nil? ? nil : !result
|
|
140
|
-
when "+"
|
|
141
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
|
|
142
|
-
when "-"
|
|
143
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
|
|
144
|
-
when "*"
|
|
145
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
|
|
146
|
-
when "/"
|
|
147
|
-
return unless left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
148
|
-
raise CompileTime::Error, "division by zero" if zero_numeric?(right)
|
|
149
|
-
|
|
150
|
-
left / right
|
|
151
|
-
when "%"
|
|
152
|
-
return unless left.is_a?(Integer) && right.is_a?(Integer)
|
|
153
|
-
raise CompileTime::Error, "modulo by zero" if right.zero?
|
|
154
|
-
|
|
155
|
-
left % right
|
|
156
|
-
when "<<"
|
|
157
|
-
left.is_a?(Integer) && right.is_a?(Integer) ? left << right : nil
|
|
158
|
-
when ">>"
|
|
159
|
-
left.is_a?(Integer) && right.is_a?(Integer) ? left >> right : nil
|
|
160
|
-
when "&"
|
|
161
|
-
left.is_a?(Integer) && right.is_a?(Integer) ? left & right : nil
|
|
162
|
-
when "|"
|
|
163
|
-
left.is_a?(Integer) && right.is_a?(Integer) ? left | right : nil
|
|
164
|
-
when "^"
|
|
165
|
-
left.is_a?(Integer) && right.is_a?(Integer) ? left ^ right : nil
|
|
166
|
-
when "<"
|
|
167
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left < right : nil
|
|
168
|
-
when "<="
|
|
169
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left <= right : nil
|
|
170
|
-
when ">"
|
|
171
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left > right : nil
|
|
172
|
-
when ">="
|
|
173
|
-
left.is_a?(Numeric) && right.is_a?(Numeric) ? left >= right : nil
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def zero_numeric?(value)
|
|
178
|
-
(value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MilkTea
|
|
4
|
-
class ModuleLoadError < StandardError
|
|
5
|
-
attr_reader :path, :line, :column
|
|
6
|
-
|
|
7
|
-
def initialize(message, path:, line: nil, column: nil)
|
|
8
|
-
@path = path
|
|
9
|
-
@line = line
|
|
10
|
-
@column = column
|
|
11
|
-
super("#{message}: #{path}")
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def code
|
|
15
|
-
"module/error"
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class ModuleLoader
|
|
20
|
-
ImportResolution = Data.define(:modules, :errors)
|
|
21
|
-
ImportResolutionError = Data.define(:import, :error)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "open3"
|
|
4
|
-
require "thread"
|
|
5
|
-
require "timeout"
|
|
6
|
-
|
|
7
|
-
module MilkTea
|
|
8
|
-
module DAP
|
|
9
|
-
module Backends
|
|
10
|
-
# Thin bridge to an lldb-dap compatible adapter process.
|
|
11
|
-
class LLDBDAP
|
|
12
|
-
def initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil)
|
|
13
|
-
@adapter_command = adapter_command
|
|
14
|
-
@on_event = on_event
|
|
15
|
-
@on_request = on_request
|
|
16
|
-
@protocol = nil
|
|
17
|
-
@stdin = nil
|
|
18
|
-
@stdout = nil
|
|
19
|
-
@stderr = nil
|
|
20
|
-
@wait_thread = nil
|
|
21
|
-
@reader_thread = nil
|
|
22
|
-
@stderr_thread = nil
|
|
23
|
-
@write_mutex = Mutex.new
|
|
24
|
-
@pending_mutex = Mutex.new
|
|
25
|
-
@pending = {}
|
|
26
|
-
@next_seq = 1
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def start!
|
|
30
|
-
return if running?
|
|
31
|
-
|
|
32
|
-
@stdin, @stdout, @stderr, @wait_thread = Open3.popen3(*@adapter_command)
|
|
33
|
-
@protocol = Protocol.new(input: @stdout, output: @stdin)
|
|
34
|
-
|
|
35
|
-
@reader_thread = Thread.new { read_loop }
|
|
36
|
-
@stderr_thread = Thread.new { drain_stderr }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def running?
|
|
40
|
-
!@wait_thread.nil? && @wait_thread.alive?
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def request(command, arguments = {}, timeout: 5)
|
|
44
|
-
start! unless running?
|
|
45
|
-
|
|
46
|
-
seq = next_seq
|
|
47
|
-
queue = Queue.new
|
|
48
|
-
@pending_mutex.synchronize do
|
|
49
|
-
@pending[seq] = queue
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
@write_mutex.synchronize do
|
|
53
|
-
@protocol.write_message({
|
|
54
|
-
seq: seq,
|
|
55
|
-
type: "request",
|
|
56
|
-
command: command,
|
|
57
|
-
arguments: arguments
|
|
58
|
-
})
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
Timeout.timeout(timeout) { queue.pop }
|
|
62
|
-
rescue Timeout::Error
|
|
63
|
-
{
|
|
64
|
-
"type" => "response",
|
|
65
|
-
"request_seq" => seq,
|
|
66
|
-
"success" => false,
|
|
67
|
-
"message" => "backend request timed out: #{command}"
|
|
68
|
-
}
|
|
69
|
-
ensure
|
|
70
|
-
@pending_mutex.synchronize { @pending.delete(seq) }
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def stop!
|
|
74
|
-
@stdin&.close
|
|
75
|
-
@stdout&.close
|
|
76
|
-
@stderr&.close
|
|
77
|
-
if @wait_thread&.alive?
|
|
78
|
-
Process.kill("TERM", @wait_thread.pid)
|
|
79
|
-
end
|
|
80
|
-
rescue StandardError
|
|
81
|
-
nil
|
|
82
|
-
ensure
|
|
83
|
-
@wait_thread&.join(0.2)
|
|
84
|
-
@reader_thread&.join(0.2)
|
|
85
|
-
@stderr_thread&.join(0.2)
|
|
86
|
-
@stdin = nil
|
|
87
|
-
@stdout = nil
|
|
88
|
-
@stderr = nil
|
|
89
|
-
@wait_thread = nil
|
|
90
|
-
@reader_thread = nil
|
|
91
|
-
@stderr_thread = nil
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def next_seq
|
|
95
|
-
seq = @next_seq
|
|
96
|
-
@next_seq += 1
|
|
97
|
-
seq
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def read_loop
|
|
101
|
-
loop do
|
|
102
|
-
message = @protocol.read_message
|
|
103
|
-
break if message.nil?
|
|
104
|
-
|
|
105
|
-
if message["type"] == "response"
|
|
106
|
-
queue = @pending_mutex.synchronize { @pending[message["request_seq"]] }
|
|
107
|
-
queue&.push(message)
|
|
108
|
-
elsif message["type"] == "event"
|
|
109
|
-
@on_event&.call(message)
|
|
110
|
-
elsif message["type"] == "request"
|
|
111
|
-
handle_adapter_request(message)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
rescue StandardError
|
|
115
|
-
nil
|
|
116
|
-
ensure
|
|
117
|
-
@pending_mutex.synchronize do
|
|
118
|
-
@pending.each_value do |queue|
|
|
119
|
-
queue.push({
|
|
120
|
-
"type" => "response",
|
|
121
|
-
"success" => false,
|
|
122
|
-
"message" => "backend closed"
|
|
123
|
-
})
|
|
124
|
-
end
|
|
125
|
-
@pending.clear
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def drain_stderr
|
|
130
|
-
return unless @stderr
|
|
131
|
-
|
|
132
|
-
@stderr.each_line do |_line|
|
|
133
|
-
# Intentionally ignored in this bridge layer.
|
|
134
|
-
end
|
|
135
|
-
rescue StandardError
|
|
136
|
-
nil
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def handle_adapter_request(message)
|
|
140
|
-
response = @on_request&.call(message)
|
|
141
|
-
response ||= {
|
|
142
|
-
"type" => "response",
|
|
143
|
-
"request_seq" => message["seq"],
|
|
144
|
-
"success" => false,
|
|
145
|
-
"command" => message["command"],
|
|
146
|
-
"message" => "unsupported reverse request: #{message['command']}"
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
@write_mutex.synchronize do
|
|
150
|
-
@protocol.write_message(response)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|
|
File without changes
|