mt-lang 0.3.43 → 0.4.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/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/ast.rb +2 -2
- data/lib/milk_tea/core/compile_time/method_folding.rb +157 -0
- data/lib/milk_tea/core/compile_time.rb +303 -26
- data/lib/milk_tea/core/lowering/resolve.rb +76 -2
- data/lib/milk_tea/core/parser/declarations.rb +10 -5
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +10 -8
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +70 -1
- data/lib/milk_tea/core/semantic_analyzer.rb +9 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 990a1e0ede945713a31152caccc4a7de798dc6ead6078d46d5a376f19054a52a
|
|
4
|
+
data.tar.gz: 97d7877587ca50ab673212d1d0d80d6d60ad8f97a74c439965feb32c2f5a4e37
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c04b4d611724c8b4f42ae14929b3d8d98b4aa4da5157f7c3ee765afb6356be643b6ada8c96ed45a8e61103e71792c7855c25d87f65865b9609cdb0cc85544507
|
|
7
|
+
data.tar.gz: 82416c2665d49928f0d6dd5e9b8f225177f9df0f782e973339545a6375cc673e082bccc3bbb6126cbdc50875dc6fa29880b5e23b8e3ccd458934b32f5d2f6718
|
data/lib/milk_tea/base.rb
CHANGED
data/lib/milk_tea/core/ast.rb
CHANGED
|
@@ -120,8 +120,8 @@ module MilkTea
|
|
|
120
120
|
FunctionDef = Data.define(:name, :type_params, :params, :return_type, :body, :visibility, :async, :const, :attributes, :line, :column) do
|
|
121
121
|
def initialize(name:, type_params:, params:, return_type:, body:, visibility:, async:, const: false, attributes: [], line: nil, column: nil) = super
|
|
122
122
|
end
|
|
123
|
-
MethodDef = Data.define(:name, :type_params, :params, :return_type, :body, :kind, :visibility, :async, :attributes, :line, :column) do
|
|
124
|
-
def initialize(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, attributes: [], line: nil, column: nil) = super
|
|
123
|
+
MethodDef = Data.define(:name, :type_params, :params, :return_type, :body, :kind, :visibility, :async, :const, :attributes, :line, :column) do
|
|
124
|
+
def initialize(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, const: false, attributes: [], line: nil, column: nil) = super
|
|
125
125
|
end
|
|
126
126
|
ExternFunctionDecl = Data.define(:name, :type_params, :params, :return_type, :variadic, :attributes, :line, :column, :mapping) do
|
|
127
127
|
def initialize(name:, type_params:, params:, return_type:, variadic:, attributes: [], line: nil, column: nil, mapping: nil) = super
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module CompileTime
|
|
5
|
+
# Compile-time folding for const-method calls and the resolution helpers
|
|
6
|
+
# behind them. Included by both the semantic checker and the lowering
|
|
7
|
+
# engine so the folding behavior lives in one place instead of two
|
|
8
|
+
# near-identical copies. Each includer supplies a small set of `comptime_*`
|
|
9
|
+
# adapters for the phase-specific lookups (ctx maps, scope/env evaluation,
|
|
10
|
+
# and the BlockContext factory).
|
|
11
|
+
module MethodFolding
|
|
12
|
+
def comptime_methods_map_for_binding(type, keys)
|
|
13
|
+
dispatch_type = type.respond_to?(:definition) ? type.definition : type
|
|
14
|
+
method_map = comptime_methods_map.fetch(dispatch_type, nil)
|
|
15
|
+
method_map ||= comptime_methods_map_by_to_s(dispatch_type)
|
|
16
|
+
return nil unless method_map
|
|
17
|
+
|
|
18
|
+
keys.each do |key|
|
|
19
|
+
binding = method_map[key]
|
|
20
|
+
next unless binding
|
|
21
|
+
|
|
22
|
+
ast = binding.ast
|
|
23
|
+
next unless ast.respond_to?(:const) && ast.const && ast.body
|
|
24
|
+
next unless binding.type_params.empty?
|
|
25
|
+
|
|
26
|
+
return binding
|
|
27
|
+
end
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def comptime_method_binding(member_access, ctx)
|
|
32
|
+
member = member_access.member
|
|
33
|
+
keys = ["static:#{member}", member]
|
|
34
|
+
|
|
35
|
+
if (type = resolve_type_expression(member_access.receiver)) &&
|
|
36
|
+
(binding = comptime_methods_map_for_binding(type, keys))
|
|
37
|
+
return binding
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
receiver_type = comptime_receiver_type(member_access.receiver, ctx)
|
|
41
|
+
return nil unless receiver_type
|
|
42
|
+
|
|
43
|
+
comptime_methods_map_for_binding(receiver_type, keys)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def comptime_method_binding_for_receiver(receiver_type, member)
|
|
47
|
+
comptime_methods_map_for_binding(receiver_type, ["static:#{member}", member])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def comptime_receiver_type(receiver, ctx)
|
|
51
|
+
case receiver
|
|
52
|
+
when AST::Identifier
|
|
53
|
+
type = comptime_scoped_value_type(receiver.name, ctx)
|
|
54
|
+
return type if type
|
|
55
|
+
|
|
56
|
+
comptime_value_type(receiver.name)
|
|
57
|
+
when AST::MemberAccess
|
|
58
|
+
return nil unless receiver.receiver.is_a?(AST::Identifier)
|
|
59
|
+
|
|
60
|
+
comptime_imported_value_type(receiver.receiver.name, receiver.member)
|
|
61
|
+
when AST::Call, AST::Specialization
|
|
62
|
+
comptime_call_return_type(receiver, ctx)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def comptime_member_receiver_value(receiver, ctx)
|
|
67
|
+
return nil if resolve_type_expression(receiver)
|
|
68
|
+
|
|
69
|
+
comptime_eval(receiver, ctx)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def comptime_call_return_type(call_expr, ctx)
|
|
73
|
+
case call_expr.callee
|
|
74
|
+
when AST::MemberAccess
|
|
75
|
+
binding = comptime_method_binding(call_expr.callee, ctx)
|
|
76
|
+
return binding.body_return_type if binding
|
|
77
|
+
when AST::Identifier
|
|
78
|
+
func = comptime_const_function(call_expr.callee.name)
|
|
79
|
+
return func.body_return_type if comptime_const_function_return?(func)
|
|
80
|
+
when AST::Specialization
|
|
81
|
+
callee_name = call_expr.callee.callee.is_a?(AST::Identifier) ? call_expr.callee.callee.name : nil
|
|
82
|
+
if callee_name
|
|
83
|
+
func = comptime_const_function(callee_name)
|
|
84
|
+
return func.body_return_type if comptime_const_function_return?(func)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def comptime_expression_type(expression, scopes:)
|
|
91
|
+
case expression
|
|
92
|
+
when AST::Call, AST::Specialization
|
|
93
|
+
comptime_call_return_type(expression, scopes)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def comptime_struct_field_names(type_name)
|
|
98
|
+
parts = type_name.is_a?(Array) ? type_name.map(&:to_s) : [type_name.to_s]
|
|
99
|
+
return nil if parts.empty?
|
|
100
|
+
|
|
101
|
+
type = resolve_type_expression(::MilkTea::AST.build_chain_from_parts(parts))
|
|
102
|
+
return nil unless type
|
|
103
|
+
return nil unless type.respond_to?(:fields) && type.fields
|
|
104
|
+
|
|
105
|
+
type.fields.keys
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def comptime_folded_value?(value)
|
|
109
|
+
return false if value.nil?
|
|
110
|
+
return false if value.is_a?(Types::Base)
|
|
111
|
+
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def comptime_const_function_return?(func)
|
|
116
|
+
func && func.respond_to?(:ast) && func.ast.respond_to?(:const) && func.ast.const
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def comptime_const_method_body(binding, arguments, scopes:, receiver_value:)
|
|
120
|
+
method = binding.ast
|
|
121
|
+
return nil unless method.respond_to?(:body) && method.body
|
|
122
|
+
return nil if binding.type_params.any?
|
|
123
|
+
return nil unless method.params.length == arguments.length
|
|
124
|
+
|
|
125
|
+
initial_vars = {}
|
|
126
|
+
if binding.type.receiver_type
|
|
127
|
+
return nil unless comptime_folded_value?(receiver_value)
|
|
128
|
+
|
|
129
|
+
initial_vars["this"] = receiver_value
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
method.params.each_with_index do |param, idx|
|
|
133
|
+
arg_value = comptime_eval(arguments[idx].value, scopes)
|
|
134
|
+
return nil unless arg_value
|
|
135
|
+
|
|
136
|
+
initial_vars[param.name] = arg_value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
variable_types = binding.body_params.each_with_object({}) do |param, acc|
|
|
140
|
+
acc[param.name] = param.type
|
|
141
|
+
end
|
|
142
|
+
block_context = comptime_block_context(initial_vars, variable_types)
|
|
143
|
+
result = block_context.evaluate_block(method.body, scopes: nil)
|
|
144
|
+
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
145
|
+
rescue CompileTime::Error => e
|
|
146
|
+
comptime_raise_error(e)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def comptime_imported_value_type(import_name, member)
|
|
150
|
+
imported_module = @ctx.imports.fetch(import_name, nil)
|
|
151
|
+
return nil unless imported_module
|
|
152
|
+
|
|
153
|
+
imported_module.values[member]&.type
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "types/layout"
|
|
4
|
+
require_relative "compile_time/method_folding"
|
|
4
5
|
|
|
5
6
|
module MilkTea
|
|
6
7
|
module CompileTime
|
|
@@ -9,6 +10,10 @@ module MilkTea
|
|
|
9
10
|
# Carries the value of a `return` statement out of the block evaluator as
|
|
10
11
|
# an ordinary value instead of an exception; callers unwrap it when present.
|
|
11
12
|
ReturnOutcome = Data.define(:value)
|
|
13
|
+
# Signals that a `break` was executed inside a compile-time loop body.
|
|
14
|
+
BreakOutcome = Data.define(:value)
|
|
15
|
+
# Signals that a `continue` was executed inside a compile-time loop body.
|
|
16
|
+
ContinueOutcome = Data.define(:value)
|
|
12
17
|
|
|
13
18
|
class Error < StandardError
|
|
14
19
|
def code
|
|
@@ -93,7 +98,7 @@ module MilkTea
|
|
|
93
98
|
start_val = evaluate(expression.start_expr)
|
|
94
99
|
end_val = evaluate(expression.end_expr)
|
|
95
100
|
start_val.is_a?(Integer) && end_val.is_a?(Integer) ? (start_val...end_val).to_a : nil
|
|
96
|
-
when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
|
|
101
|
+
when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral, AST::CharLiteral
|
|
97
102
|
expression.value
|
|
98
103
|
when AST::StringLiteral
|
|
99
104
|
expression.value
|
|
@@ -133,11 +138,98 @@ module MilkTea
|
|
|
133
138
|
return unless CompileTime.boolean_value?(condition)
|
|
134
139
|
|
|
135
140
|
evaluate(condition ? expression.then_expression : expression.else_expression)
|
|
141
|
+
when AST::MatchExpr
|
|
142
|
+
evaluate_match_expression(expression)
|
|
143
|
+
when AST::IndexAccess
|
|
144
|
+
evaluate_index_access(expression)
|
|
145
|
+
when AST::PrefixCast
|
|
146
|
+
evaluate_prefix_cast(expression)
|
|
136
147
|
else
|
|
137
148
|
nil
|
|
138
149
|
end
|
|
139
150
|
end
|
|
140
151
|
|
|
152
|
+
def evaluate_prefix_cast(expression)
|
|
153
|
+
operand = evaluate(expression.expression)
|
|
154
|
+
operand = 1 if operand == true
|
|
155
|
+
operand = 0 if operand == false
|
|
156
|
+
return nil unless operand.is_a?(Numeric)
|
|
157
|
+
|
|
158
|
+
target_type = begin
|
|
159
|
+
@resolve_type_ref&.call(expression.target_type)
|
|
160
|
+
rescue SemanticError
|
|
161
|
+
nil
|
|
162
|
+
end
|
|
163
|
+
return nil unless target_type.is_a?(Types::Primitive)
|
|
164
|
+
|
|
165
|
+
if target_type.boolean?
|
|
166
|
+
return operand != 0
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
if target_type.integer?
|
|
170
|
+
return nil unless target_type.integer_width
|
|
171
|
+
|
|
172
|
+
value = operand.is_a?(Float) ? operand.truncate : operand
|
|
173
|
+
return nil unless value.is_a?(Integer)
|
|
174
|
+
|
|
175
|
+
return wrap_integer(value, target_type.integer_width, target_type.signed_integer?)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
return nil unless target_type.float?
|
|
179
|
+
|
|
180
|
+
value = operand.to_f
|
|
181
|
+
target_type.name == "float" ? float32_round(value) : value
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def wrap_integer(value, width, signed)
|
|
185
|
+
mask = (1 << width) - 1
|
|
186
|
+
wrapped = value & mask
|
|
187
|
+
return wrapped unless signed
|
|
188
|
+
|
|
189
|
+
half = 1 << (width - 1)
|
|
190
|
+
wrapped >= half ? wrapped - (1 << width) : wrapped
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def float32_round(value)
|
|
194
|
+
[value].pack("f").unpack1("f")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def evaluate_index_access(expression)
|
|
198
|
+
receiver = evaluate(expression.receiver)
|
|
199
|
+
return nil if receiver.nil?
|
|
200
|
+
|
|
201
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
202
|
+
start_val = evaluate(expression.index.start_expr)
|
|
203
|
+
end_val = evaluate(expression.index.end_expr)
|
|
204
|
+
return nil unless start_val.is_a?(Integer) && end_val.is_a?(Integer)
|
|
205
|
+
|
|
206
|
+
return receiver[start_val...end_val] if receiver.is_a?(Array) || receiver.is_a?(String)
|
|
207
|
+
|
|
208
|
+
return nil
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
index = evaluate(expression.index)
|
|
212
|
+
return nil unless index.is_a?(Integer)
|
|
213
|
+
return receiver[index] if receiver.is_a?(Array)
|
|
214
|
+
return receiver.getbyte(index) if receiver.is_a?(String)
|
|
215
|
+
|
|
216
|
+
nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def evaluate_match_expression(expression)
|
|
220
|
+
scrutinee = evaluate(expression.expression)
|
|
221
|
+
return nil unless scrutinee
|
|
222
|
+
|
|
223
|
+
expression.arms.each do |arm|
|
|
224
|
+
wildcard = arm.pattern.is_a?(AST::Identifier) && arm.pattern.name == "_"
|
|
225
|
+
if wildcard || CompileTime.equality_result(scrutinee, evaluate(arm.pattern)) == true
|
|
226
|
+
return evaluate(arm.value)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
nil
|
|
231
|
+
end
|
|
232
|
+
|
|
141
233
|
def resolve_layout_type(type_ref)
|
|
142
234
|
result = begin
|
|
143
235
|
@resolve_type_ref&.call(type_ref)
|
|
@@ -207,7 +299,13 @@ module MilkTea
|
|
|
207
299
|
result = CompileTime.equality_result(left, right)
|
|
208
300
|
result.nil? ? nil : !result
|
|
209
301
|
when "+"
|
|
210
|
-
left.is_a?(
|
|
302
|
+
if left.is_a?(String) && right.is_a?(String)
|
|
303
|
+
left + right
|
|
304
|
+
elsif left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
305
|
+
left + right
|
|
306
|
+
else
|
|
307
|
+
nil
|
|
308
|
+
end
|
|
211
309
|
when "-"
|
|
212
310
|
left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
|
|
213
311
|
when "*"
|
|
@@ -251,9 +349,10 @@ module MilkTea
|
|
|
251
349
|
class BlockContext
|
|
252
350
|
attr_reader :checker
|
|
253
351
|
|
|
254
|
-
def initialize(checker, initial_variables: nil)
|
|
352
|
+
def initialize(checker, initial_variables: nil, variable_types: nil)
|
|
255
353
|
@checker = checker
|
|
256
354
|
@variables = initial_variables || {}
|
|
355
|
+
@variable_types = variable_types || {}
|
|
257
356
|
end
|
|
258
357
|
|
|
259
358
|
def evaluate_block(statements, scopes: nil)
|
|
@@ -261,7 +360,7 @@ module MilkTea
|
|
|
261
360
|
|
|
262
361
|
statements.each do |statement|
|
|
263
362
|
outcome = evaluate_statement(statement, scopes:)
|
|
264
|
-
return outcome if
|
|
363
|
+
return outcome if control_outcome?(outcome)
|
|
265
364
|
|
|
266
365
|
result = outcome
|
|
267
366
|
end
|
|
@@ -269,6 +368,10 @@ module MilkTea
|
|
|
269
368
|
result
|
|
270
369
|
end
|
|
271
370
|
|
|
371
|
+
def control_outcome?(outcome)
|
|
372
|
+
outcome.is_a?(ReturnOutcome) || outcome.is_a?(BreakOutcome) || outcome.is_a?(ContinueOutcome)
|
|
373
|
+
end
|
|
374
|
+
|
|
272
375
|
def evaluate_statement(statement, scopes:)
|
|
273
376
|
case statement
|
|
274
377
|
when AST::LocalDecl
|
|
@@ -288,9 +391,13 @@ module MilkTea
|
|
|
288
391
|
evaluate_if(statement, scopes:)
|
|
289
392
|
when AST::ExpressionStmt
|
|
290
393
|
evaluate_expression(statement.expression, scopes:)
|
|
291
|
-
when AST::PassStmt
|
|
394
|
+
when AST::PassStmt
|
|
292
395
|
# no-op at compile time
|
|
293
396
|
nil
|
|
397
|
+
when AST::BreakStmt
|
|
398
|
+
BreakOutcome.new(nil)
|
|
399
|
+
when AST::ContinueStmt
|
|
400
|
+
ContinueOutcome.new(nil)
|
|
294
401
|
when AST::EmitStmt
|
|
295
402
|
# emitted declarations are collected during lowering
|
|
296
403
|
nil
|
|
@@ -312,9 +419,25 @@ module MilkTea
|
|
|
312
419
|
@checker.evaluate_compile_time_const_value(id_expr, scopes:)
|
|
313
420
|
},
|
|
314
421
|
resolve_member_access: ->(ma_expr) {
|
|
422
|
+
if ma_expr.receiver.is_a?(AST::Identifier) && @variables.key?(ma_expr.receiver.name)
|
|
423
|
+
value = @variables[ma_expr.receiver.name]
|
|
424
|
+
case value
|
|
425
|
+
when Hash
|
|
426
|
+
return value[ma_expr.member] if value.key?(ma_expr.member)
|
|
427
|
+
when Array
|
|
428
|
+
if ma_expr.member =~ /\A_(\d+)\z/
|
|
429
|
+
return value[Regexp.last_match(1).to_i]
|
|
430
|
+
end
|
|
431
|
+
return value.length if ma_expr.member == "len"
|
|
432
|
+
when String
|
|
433
|
+
return value.length if ma_expr.member == "len"
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
315
437
|
@checker.evaluate_compile_time_const_value(ma_expr, scopes:)
|
|
316
438
|
},
|
|
317
439
|
resolve_call: ->(call_expr) { resolve_compile_time_call(call_expr, scopes:) },
|
|
440
|
+
resolve_type_ref: ->(type_ref) { @checker.resolve_type_ref(type_ref) },
|
|
318
441
|
)
|
|
319
442
|
end
|
|
320
443
|
end
|
|
@@ -323,26 +446,153 @@ module MilkTea
|
|
|
323
446
|
return nil unless decl.value
|
|
324
447
|
|
|
325
448
|
value = evaluate_expression(decl.value, scopes:)
|
|
449
|
+
return value unless value
|
|
450
|
+
|
|
451
|
+
if decl.else_body
|
|
452
|
+
return evaluate_guard_local_decl(decl, value, scopes:)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
if decl.destructure_bindings&.any?
|
|
456
|
+
return evaluate_destructure_local_decl(decl, value)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
@variables[decl.name] = value
|
|
460
|
+
@variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
|
|
461
|
+
value
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def evaluate_guard_local_decl(decl, value, scopes:)
|
|
465
|
+
if value.is_a?(CompileTime::VariantValue)
|
|
466
|
+
case value.arm
|
|
467
|
+
when "some", "success"
|
|
468
|
+
@variables[decl.name] = value.fields["value"]
|
|
469
|
+
@variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
|
|
470
|
+
return value
|
|
471
|
+
when "none", "failure"
|
|
472
|
+
@variables[decl.else_binding.name] = value.fields["error"] if decl.else_binding
|
|
473
|
+
return evaluate_block(decl.else_body, scopes:)
|
|
474
|
+
end
|
|
475
|
+
return nil
|
|
476
|
+
end
|
|
477
|
+
|
|
326
478
|
@variables[decl.name] = value
|
|
479
|
+
@variable_types[decl.name] = compile_time_decl_type(decl.value, scopes:) unless @variable_types.key?(decl.name)
|
|
327
480
|
value
|
|
328
481
|
end
|
|
329
482
|
|
|
483
|
+
def evaluate_destructure_local_decl(decl, value)
|
|
484
|
+
if value.is_a?(Array)
|
|
485
|
+
decl.destructure_bindings.each_with_index do |name, idx|
|
|
486
|
+
next if name == "_"
|
|
487
|
+
|
|
488
|
+
@variables[name] = value[idx]
|
|
489
|
+
end
|
|
490
|
+
return value
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
if value.is_a?(Hash)
|
|
494
|
+
field_names = destructure_field_names(decl.destructure_type_name)
|
|
495
|
+
decl.destructure_bindings.each_with_index do |name, idx|
|
|
496
|
+
next if name == "_"
|
|
497
|
+
|
|
498
|
+
field_name = field_names&.[](idx) || name
|
|
499
|
+
@variables[name] = value[field_name]
|
|
500
|
+
end
|
|
501
|
+
return value
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
nil
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def destructure_field_names(type_name)
|
|
508
|
+
return nil unless type_name
|
|
509
|
+
return nil unless @checker.respond_to?(:comptime_struct_field_names)
|
|
510
|
+
|
|
511
|
+
@checker.comptime_struct_field_names(type_name)
|
|
512
|
+
rescue StandardError
|
|
513
|
+
nil
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def compile_time_decl_type(expression, scopes:)
|
|
517
|
+
return nil unless @checker.respond_to?(:comptime_expression_type)
|
|
518
|
+
|
|
519
|
+
@checker.comptime_expression_type(expression, scopes:)
|
|
520
|
+
rescue StandardError
|
|
521
|
+
nil
|
|
522
|
+
end
|
|
523
|
+
|
|
330
524
|
def evaluate_assignment(assignment, scopes:)
|
|
331
525
|
value = evaluate_expression(assignment.value, scopes:)
|
|
526
|
+
return nil unless value
|
|
527
|
+
|
|
332
528
|
case assignment.target
|
|
333
529
|
when AST::Identifier
|
|
530
|
+
return nil unless @variables.key?(assignment.target.name)
|
|
531
|
+
|
|
334
532
|
if assignment.operator != "="
|
|
335
533
|
current = @variables[assignment.target.name]
|
|
336
534
|
value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
|
|
337
535
|
end
|
|
338
536
|
@variables[assignment.target.name] = value
|
|
537
|
+
when AST::MemberAccess
|
|
538
|
+
return nil unless evaluate_member_assignment(assignment, value, scopes:)
|
|
539
|
+
when AST::IndexAccess
|
|
540
|
+
return nil unless evaluate_index_assignment(assignment, value, scopes:)
|
|
339
541
|
end
|
|
340
542
|
value
|
|
341
543
|
end
|
|
342
544
|
|
|
545
|
+
def evaluate_member_assignment(assignment, value, scopes:)
|
|
546
|
+
target = assignment.target
|
|
547
|
+
return nil unless target.receiver.is_a?(AST::Identifier)
|
|
548
|
+
return nil unless @variables.key?(target.receiver.name)
|
|
549
|
+
|
|
550
|
+
receiver = @variables[target.receiver.name]
|
|
551
|
+
return nil unless receiver.is_a?(Hash)
|
|
552
|
+
|
|
553
|
+
if assignment.operator != "="
|
|
554
|
+
current = receiver[target.member]
|
|
555
|
+
value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
|
|
556
|
+
return nil unless value
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
updated = receiver.dup
|
|
560
|
+
updated[target.member] = value
|
|
561
|
+
@variables[target.receiver.name] = updated
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def evaluate_index_assignment(assignment, value, scopes:)
|
|
565
|
+
target = assignment.target
|
|
566
|
+
return nil unless target.receiver.is_a?(AST::Identifier)
|
|
567
|
+
return nil unless @variables.key?(target.receiver.name)
|
|
568
|
+
|
|
569
|
+
receiver = @variables[target.receiver.name]
|
|
570
|
+
return nil unless receiver.is_a?(Array)
|
|
571
|
+
|
|
572
|
+
index = evaluate_expression(target.index, scopes:)
|
|
573
|
+
return nil unless index.is_a?(Integer)
|
|
574
|
+
|
|
575
|
+
if assignment.operator != "="
|
|
576
|
+
current = receiver[index]
|
|
577
|
+
value = apply_compile_time_binary(assignment.operator.chomp("="), current, value)
|
|
578
|
+
return nil unless value
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
updated = receiver.dup
|
|
582
|
+
updated[index] = value
|
|
583
|
+
@variables[target.receiver.name] = updated
|
|
584
|
+
end
|
|
585
|
+
|
|
343
586
|
def apply_compile_time_binary(operator, left, right)
|
|
344
587
|
case operator
|
|
345
|
-
when "+"
|
|
588
|
+
when "+"
|
|
589
|
+
if left.is_a?(String) && right.is_a?(String)
|
|
590
|
+
left + right
|
|
591
|
+
elsif left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
592
|
+
left + right
|
|
593
|
+
else
|
|
594
|
+
nil
|
|
595
|
+
end
|
|
346
596
|
when "-" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
|
|
347
597
|
when "*" then left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
|
|
348
598
|
when "/" then left.is_a?(Numeric) && right.is_a?(Numeric) && !zero_numeric?(right) ? left / right : nil
|
|
@@ -371,7 +621,11 @@ module MilkTea
|
|
|
371
621
|
|
|
372
622
|
statement.body.each do |body_stmt|
|
|
373
623
|
outcome = evaluate_statement(body_stmt, scopes:)
|
|
374
|
-
|
|
624
|
+
case outcome
|
|
625
|
+
when ReturnOutcome then return outcome
|
|
626
|
+
when BreakOutcome then return result
|
|
627
|
+
when ContinueOutcome then break
|
|
628
|
+
end
|
|
375
629
|
end
|
|
376
630
|
iterations += 1
|
|
377
631
|
end
|
|
@@ -392,32 +646,35 @@ module MilkTea
|
|
|
392
646
|
@variables[loop_var_name] = element
|
|
393
647
|
statement.body.each do |body_stmt|
|
|
394
648
|
outcome = evaluate_statement(body_stmt, scopes:)
|
|
395
|
-
|
|
649
|
+
case outcome
|
|
650
|
+
when ReturnOutcome then return outcome
|
|
651
|
+
when BreakOutcome then return result
|
|
652
|
+
when ContinueOutcome then break
|
|
653
|
+
end
|
|
396
654
|
end
|
|
397
655
|
end
|
|
398
656
|
|
|
399
657
|
result
|
|
400
658
|
end
|
|
401
659
|
|
|
660
|
+
def run_body(body, scopes:, fallback: nil)
|
|
661
|
+
body.each do |body_stmt|
|
|
662
|
+
outcome = evaluate_statement(body_stmt, scopes:)
|
|
663
|
+
return outcome if control_outcome?(outcome)
|
|
664
|
+
end
|
|
665
|
+
fallback
|
|
666
|
+
end
|
|
667
|
+
|
|
402
668
|
def evaluate_if(statement, scopes:)
|
|
403
669
|
statement.branches.each do |branch|
|
|
404
670
|
condition = evaluate_expression(branch.condition, scopes:)
|
|
405
|
-
|
|
406
|
-
branch.body.each do |body_stmt|
|
|
407
|
-
outcome = evaluate_statement(body_stmt, scopes:)
|
|
408
|
-
return outcome if outcome.is_a?(ReturnOutcome)
|
|
409
|
-
end
|
|
410
|
-
return condition
|
|
411
|
-
end
|
|
412
|
-
end
|
|
671
|
+
next unless CompileTime.boolean_value?(condition) && condition
|
|
413
672
|
|
|
414
|
-
|
|
415
|
-
statement.else_body.each do |body_stmt|
|
|
416
|
-
outcome = evaluate_statement(body_stmt, scopes:)
|
|
417
|
-
return outcome if outcome.is_a?(ReturnOutcome)
|
|
418
|
-
end
|
|
673
|
+
return run_body(branch.body, scopes:, fallback: condition)
|
|
419
674
|
end
|
|
420
675
|
|
|
676
|
+
return run_body(statement.else_body, scopes:) if statement.else_body
|
|
677
|
+
|
|
421
678
|
nil
|
|
422
679
|
end
|
|
423
680
|
|
|
@@ -428,11 +685,7 @@ module MilkTea
|
|
|
428
685
|
statement.arms.each do |arm|
|
|
429
686
|
wildcard = arm.pattern.is_a?(AST::Identifier) && arm.pattern.name == "_"
|
|
430
687
|
if wildcard || CompileTime.equality_result(scrutinee, evaluate_expression(arm.pattern, scopes:)) == true
|
|
431
|
-
arm.body
|
|
432
|
-
outcome = evaluate_statement(body_stmt, scopes:)
|
|
433
|
-
return outcome if outcome.is_a?(ReturnOutcome)
|
|
434
|
-
end
|
|
435
|
-
return scrutinee
|
|
688
|
+
return run_body(arm.body, scopes:, fallback: scrutinee)
|
|
436
689
|
end
|
|
437
690
|
end
|
|
438
691
|
|
|
@@ -443,6 +696,9 @@ module MilkTea
|
|
|
443
696
|
result = try_const_function_call(call_expr, scopes:)
|
|
444
697
|
return result if result
|
|
445
698
|
|
|
699
|
+
result = try_const_method_call(call_expr, scopes:)
|
|
700
|
+
return result if result
|
|
701
|
+
|
|
446
702
|
result = try_struct_constructor_call(call_expr, scopes:)
|
|
447
703
|
return result if result
|
|
448
704
|
|
|
@@ -452,6 +708,27 @@ module MilkTea
|
|
|
452
708
|
@checker.evaluate_compile_time_const_value(call_expr, scopes:)
|
|
453
709
|
end
|
|
454
710
|
|
|
711
|
+
def try_const_method_call(call_expr, scopes:)
|
|
712
|
+
return unless call_expr.callee.is_a?(AST::MemberAccess)
|
|
713
|
+
return unless @checker.respond_to?(:comptime_method_binding_for_receiver)
|
|
714
|
+
return unless @checker.respond_to?(:comptime_const_method_body)
|
|
715
|
+
|
|
716
|
+
receiver = call_expr.callee.receiver
|
|
717
|
+
return unless receiver.is_a?(AST::Identifier)
|
|
718
|
+
return unless @variables.key?(receiver.name)
|
|
719
|
+
|
|
720
|
+
receiver_value = @variables[receiver.name]
|
|
721
|
+
return nil if receiver_value.nil? || receiver_value.is_a?(Types::Base)
|
|
722
|
+
|
|
723
|
+
receiver_type = @variable_types[receiver.name]
|
|
724
|
+
return nil unless receiver_type
|
|
725
|
+
|
|
726
|
+
binding = @checker.comptime_method_binding_for_receiver(receiver_type, call_expr.callee.member)
|
|
727
|
+
return nil unless binding
|
|
728
|
+
|
|
729
|
+
@checker.comptime_const_method_body(binding, call_expr.arguments, scopes:, receiver_value:)
|
|
730
|
+
end
|
|
731
|
+
|
|
455
732
|
def try_const_function_call(call_expr, scopes:)
|
|
456
733
|
return unless call_expr.callee.is_a?(AST::Identifier)
|
|
457
734
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
module Lowering
|
|
5
5
|
module Resolve
|
|
6
|
+
include CompileTime::MethodFolding
|
|
6
7
|
PASS_THROUGH_BUILTINS = {
|
|
7
8
|
"fatal" => :fatal,
|
|
8
9
|
"ref_of" => :ref_of,
|
|
@@ -1558,7 +1559,7 @@ module MilkTea
|
|
|
1558
1559
|
end
|
|
1559
1560
|
resolve_current_module_const_value(identifier_expression.name)
|
|
1560
1561
|
end,
|
|
1561
|
-
resolve_member_access: lambda { |ma|
|
|
1562
|
+
resolve_member_access: lambda { |ma| compile_time_const_value(ma, env:) },
|
|
1562
1563
|
resolve_type_ref: lambda { |tr| resolve_type_ref(tr) },
|
|
1563
1564
|
resolve_call: lambda { |ce| evaluate_compile_time_call(ce, env:) },
|
|
1564
1565
|
))
|
|
@@ -1573,6 +1574,15 @@ module MilkTea
|
|
|
1573
1574
|
when "name" then next receiver_value.member_name
|
|
1574
1575
|
when "value" then next receiver_value.member_value
|
|
1575
1576
|
end
|
|
1577
|
+
when Hash
|
|
1578
|
+
next receiver_value[member_access_expression.member] if receiver_value.key?(member_access_expression.member)
|
|
1579
|
+
when Array
|
|
1580
|
+
if member_access_expression.member =~ /\A_(\d+)\z/
|
|
1581
|
+
next receiver_value[Regexp.last_match(1).to_i]
|
|
1582
|
+
end
|
|
1583
|
+
next receiver_value.length if member_access_expression.member == "len"
|
|
1584
|
+
when String
|
|
1585
|
+
next receiver_value.length if member_access_expression.member == "len"
|
|
1576
1586
|
end
|
|
1577
1587
|
end
|
|
1578
1588
|
|
|
@@ -1594,6 +1604,11 @@ module MilkTea
|
|
|
1594
1604
|
|
|
1595
1605
|
def evaluate_compile_time_call(expression, env:)
|
|
1596
1606
|
case expression.callee
|
|
1607
|
+
when AST::MemberAccess
|
|
1608
|
+
if (method_binding = comptime_method_binding(expression.callee, env))
|
|
1609
|
+
receiver_value = comptime_member_receiver_value(expression.callee.receiver, env)
|
|
1610
|
+
return comptime_const_method_body(method_binding, expression.arguments, scopes: nil, receiver_value:)
|
|
1611
|
+
end
|
|
1597
1612
|
when AST::Identifier
|
|
1598
1613
|
case expression.callee.name
|
|
1599
1614
|
when "field_of"
|
|
@@ -1794,12 +1809,51 @@ module MilkTea
|
|
|
1794
1809
|
initial_vars[param.name] = arg_value
|
|
1795
1810
|
end
|
|
1796
1811
|
|
|
1812
|
+
variable_types = func.body_params.each_with_object({}) do |param, acc|
|
|
1813
|
+
acc[param.name] = param.type
|
|
1814
|
+
end
|
|
1797
1815
|
evaluator = ConstFnLowerEvaluator.new(self)
|
|
1798
|
-
ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars)
|
|
1816
|
+
ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars, variable_types: variable_types)
|
|
1799
1817
|
result = ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
1800
1818
|
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
1801
1819
|
end
|
|
1802
1820
|
|
|
1821
|
+
def comptime_methods_map
|
|
1822
|
+
@ctx.methods
|
|
1823
|
+
end
|
|
1824
|
+
|
|
1825
|
+
def comptime_methods_map_by_to_s(type)
|
|
1826
|
+
entry = @ctx.methods.find { |k, _| k.to_s == type.to_s }
|
|
1827
|
+
entry&.last
|
|
1828
|
+
end
|
|
1829
|
+
|
|
1830
|
+
def comptime_scoped_value_type(name, ctx)
|
|
1831
|
+
return nil unless ctx
|
|
1832
|
+
|
|
1833
|
+
binding = lookup_value(name, ctx)
|
|
1834
|
+
binding[:type] if binding && binding[:type]
|
|
1835
|
+
end
|
|
1836
|
+
|
|
1837
|
+
def comptime_value_type(name)
|
|
1838
|
+
@ctx.values[name]&.type
|
|
1839
|
+
end
|
|
1840
|
+
|
|
1841
|
+
def comptime_const_function(name)
|
|
1842
|
+
@ctx.functions[name]
|
|
1843
|
+
end
|
|
1844
|
+
|
|
1845
|
+
def comptime_eval(expression, _scopes)
|
|
1846
|
+
compile_time_const_value(expression, env: empty_env)
|
|
1847
|
+
end
|
|
1848
|
+
|
|
1849
|
+
def comptime_block_context(initial_vars, variable_types)
|
|
1850
|
+
::MilkTea::CompileTime::BlockContext.new(ConstFnLowerEvaluator.new(self), initial_variables: initial_vars, variable_types: variable_types)
|
|
1851
|
+
end
|
|
1852
|
+
|
|
1853
|
+
def comptime_raise_error(error)
|
|
1854
|
+
raise error
|
|
1855
|
+
end
|
|
1856
|
+
|
|
1803
1857
|
class ConstFnLowerEvaluator
|
|
1804
1858
|
def initialize(lowerer)
|
|
1805
1859
|
@lowerer = lowerer
|
|
@@ -1816,6 +1870,26 @@ module MilkTea
|
|
|
1816
1870
|
def top_level_function(name)
|
|
1817
1871
|
@lowerer.instance_variable_get(:@ctx).functions&.[](name)
|
|
1818
1872
|
end
|
|
1873
|
+
|
|
1874
|
+
def resolve_type_ref(type_ref)
|
|
1875
|
+
@lowerer.resolve_type_ref(type_ref)
|
|
1876
|
+
end
|
|
1877
|
+
|
|
1878
|
+
def comptime_struct_field_names(type_name)
|
|
1879
|
+
@lowerer.comptime_struct_field_names(type_name)
|
|
1880
|
+
end
|
|
1881
|
+
|
|
1882
|
+
def comptime_method_binding_for_receiver(receiver_type, member)
|
|
1883
|
+
@lowerer.comptime_method_binding_for_receiver(receiver_type, member)
|
|
1884
|
+
end
|
|
1885
|
+
|
|
1886
|
+
def comptime_const_method_body(binding, arguments, scopes: nil, receiver_value: nil)
|
|
1887
|
+
@lowerer.comptime_const_method_body(binding, arguments, scopes:, receiver_value:)
|
|
1888
|
+
end
|
|
1889
|
+
|
|
1890
|
+
def comptime_expression_type(expression, scopes: nil)
|
|
1891
|
+
@lowerer.comptime_expression_type(expression, scopes:)
|
|
1892
|
+
end
|
|
1819
1893
|
end
|
|
1820
1894
|
|
|
1821
1895
|
def evaluate_reflection_target_argument(expression, env:)
|
|
@@ -459,6 +459,7 @@ module MilkTea
|
|
|
459
459
|
match(:async)
|
|
460
460
|
match(:editable) if check(:editable)
|
|
461
461
|
match(:static) if check(:static)
|
|
462
|
+
match(:const) if check(:const)
|
|
462
463
|
result = check(:function) && !check_next(:colon)
|
|
463
464
|
@current = saved
|
|
464
465
|
result
|
|
@@ -657,10 +658,10 @@ module MilkTea
|
|
|
657
658
|
end
|
|
658
659
|
|
|
659
660
|
def parse_method_def(attributes: [])
|
|
660
|
-
visibility, _visibility_token, async, kind, line, name_token = parse_method_head
|
|
661
|
+
visibility, _visibility_token, async, kind, const, line, name_token = parse_method_head
|
|
661
662
|
name = name_token.lexeme
|
|
662
663
|
type_params, params, return_type, body = parse_callable_signature
|
|
663
|
-
AST::MethodDef.new(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, attributes:, line:, column: name_token.column)
|
|
664
|
+
AST::MethodDef.new(name:, type_params:, params:, return_type:, body:, kind:, visibility:, async:, const:, attributes:, line:, column: name_token.column)
|
|
664
665
|
rescue ParseError => e
|
|
665
666
|
raise unless @recovery_errors
|
|
666
667
|
|
|
@@ -668,11 +669,12 @@ module MilkTea
|
|
|
668
669
|
name = name_token&.lexeme || "unknown"
|
|
669
670
|
col = name_token&.column || 1
|
|
670
671
|
advance until eof? || check(:function) || check(:dedent)
|
|
671
|
-
AST::MethodDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, kind: kind || :plain, visibility: visibility || :private, async: async || false, attributes:, line:, column: col)
|
|
672
|
+
AST::MethodDef.new(name:, type_params: [], params: [], return_type: nil, body: nil, kind: kind || :plain, visibility: visibility || :private, async: async || false, const: const || false, attributes:, line:, column: col)
|
|
672
673
|
end
|
|
673
674
|
|
|
674
675
|
def parse_interface_method_decl(attributes: [])
|
|
675
|
-
visibility, visibility_token, async, kind, line, name_token = parse_method_head
|
|
676
|
+
visibility, visibility_token, async, kind, const, line, name_token = parse_method_head
|
|
677
|
+
raise error(name_token, "const is not allowed on interface methods") if const
|
|
676
678
|
raise error(visibility_token, "public is not allowed on interface methods") if visibility == :public
|
|
677
679
|
|
|
678
680
|
name = name_token.lexeme
|
|
@@ -689,6 +691,9 @@ module MilkTea
|
|
|
689
691
|
visibility, visibility_token = parse_visibility
|
|
690
692
|
async = match(:async)
|
|
691
693
|
kind = parse_method_kind
|
|
694
|
+
const = match(:const)
|
|
695
|
+
raise error(previous, "editable methods cannot be const") if kind == :editable && const
|
|
696
|
+
raise error(previous, "async methods cannot be const") if async && const
|
|
692
697
|
if check(:function)
|
|
693
698
|
consume(:function, "expected function declaration")
|
|
694
699
|
elsif check(:identifier)
|
|
@@ -699,7 +704,7 @@ module MilkTea
|
|
|
699
704
|
end
|
|
700
705
|
line = previous.line
|
|
701
706
|
name_token = consume_name("expected function name")
|
|
702
|
-
[visibility, visibility_token, async, kind, line, name_token]
|
|
707
|
+
[visibility, visibility_token, async, kind, const, line, name_token]
|
|
703
708
|
end
|
|
704
709
|
|
|
705
710
|
def parse_method_kind
|
|
@@ -298,14 +298,16 @@ module MilkTea
|
|
|
298
298
|
|
|
299
299
|
def render_function_signature(function, prefix: "")
|
|
300
300
|
signature_prefix = if function.is_a?(AST::MethodDef)
|
|
301
|
-
case function.kind
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
301
|
+
kind_prefix = case function.kind
|
|
302
|
+
when :editable
|
|
303
|
+
"editable "
|
|
304
|
+
when :static
|
|
305
|
+
"static "
|
|
306
|
+
else
|
|
307
|
+
""
|
|
308
|
+
end
|
|
309
|
+
const_prefix = function.respond_to?(:const) && function.const ? "const " : ""
|
|
310
|
+
"#{kind_prefix}#{const_prefix}function "
|
|
309
311
|
elsif function.is_a?(AST::ForeignFunctionDecl)
|
|
310
312
|
"foreign function "
|
|
311
313
|
else
|
|
@@ -298,6 +298,33 @@ module MilkTea
|
|
|
298
298
|
current_type_params[identifier_expression.name] || @ctx.types[identifier_expression.name]
|
|
299
299
|
end,
|
|
300
300
|
resolve_member_access: lambda do |member_access_expression|
|
|
301
|
+
if (receiver_value = CompileTime.evaluate(
|
|
302
|
+
member_access_expression.receiver,
|
|
303
|
+
resolve_identifier: lambda do |identifier_expression|
|
|
304
|
+
if scopes
|
|
305
|
+
binding = lookup_value(identifier_expression.name, scopes)
|
|
306
|
+
return binding.const_value unless binding&.const_value.nil?
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
resolve_current_module_const_value(identifier_expression.name)
|
|
310
|
+
end,
|
|
311
|
+
resolve_member_access: lambda { |ma| evaluate_compile_time_const_value(ma, scopes:) },
|
|
312
|
+
resolve_type_ref: lambda { |tr| resolve_type_ref(tr) },
|
|
313
|
+
resolve_call: lambda { |ce| evaluate_compile_time_call(ce, scopes:) },
|
|
314
|
+
))
|
|
315
|
+
case receiver_value
|
|
316
|
+
when Hash
|
|
317
|
+
next receiver_value[member_access_expression.member] if receiver_value.key?(member_access_expression.member)
|
|
318
|
+
when Array
|
|
319
|
+
if member_access_expression.member =~ /\A_(\d+)\z/
|
|
320
|
+
next receiver_value[Regexp.last_match(1).to_i]
|
|
321
|
+
end
|
|
322
|
+
next receiver_value.length if member_access_expression.member == "len"
|
|
323
|
+
when String
|
|
324
|
+
next receiver_value.length if member_access_expression.member == "len"
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
301
328
|
if member_access_expression.receiver.is_a?(AST::Identifier) && scopes
|
|
302
329
|
binding = lookup_value(member_access_expression.receiver.name, scopes)
|
|
303
330
|
if binding && binding.const_value
|
|
@@ -392,6 +419,11 @@ module MilkTea
|
|
|
392
419
|
end
|
|
393
420
|
return CompileTime::VariantValue.new(arm: arm_name, fields: fields)
|
|
394
421
|
end
|
|
422
|
+
|
|
423
|
+
if (method_binding = comptime_method_binding(expression.callee, scopes))
|
|
424
|
+
receiver_value = comptime_member_receiver_value(expression.callee.receiver, scopes)
|
|
425
|
+
return comptime_const_method_body(method_binding, expression.arguments, scopes:, receiver_value:)
|
|
426
|
+
end
|
|
395
427
|
when AST::Identifier
|
|
396
428
|
if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct)
|
|
397
429
|
fields = {}
|
|
@@ -587,7 +619,10 @@ module MilkTea
|
|
|
587
619
|
initial_vars[param.name] = arg_value
|
|
588
620
|
end
|
|
589
621
|
|
|
590
|
-
|
|
622
|
+
variable_types = func.body_params.each_with_object({}) do |param, acc|
|
|
623
|
+
acc[param.name] = param.type
|
|
624
|
+
end
|
|
625
|
+
ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars, variable_types: variable_types)
|
|
591
626
|
result = ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
592
627
|
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
593
628
|
rescue CompileTime::Error => e
|
|
@@ -613,6 +648,40 @@ module MilkTea
|
|
|
613
648
|
end
|
|
614
649
|
end
|
|
615
650
|
|
|
651
|
+
def comptime_methods_map
|
|
652
|
+
@ctx.methods
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def comptime_methods_map_by_to_s(_type)
|
|
656
|
+
nil
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def comptime_scoped_value_type(name, ctx)
|
|
660
|
+
return nil unless ctx
|
|
661
|
+
|
|
662
|
+
lookup_value(name, ctx)&.type
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
def comptime_value_type(name)
|
|
666
|
+
@ctx.top_level_values[name]&.type
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def comptime_const_function(name)
|
|
670
|
+
@ctx.top_level_functions[name]
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def comptime_eval(expression, scopes)
|
|
674
|
+
evaluate_compile_time_const_value(expression, scopes: scopes)
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
def comptime_block_context(initial_vars, variable_types)
|
|
678
|
+
::MilkTea::CompileTime::BlockContext.new(self, initial_variables: initial_vars, variable_types: variable_types)
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def comptime_raise_error(error)
|
|
682
|
+
raise_sema_error(error.message)
|
|
683
|
+
end
|
|
684
|
+
|
|
616
685
|
def evaluate_has_attribute_call(arguments, scopes:)
|
|
617
686
|
target = evaluate_reflection_target_argument(arguments.first.value, scopes:)
|
|
618
687
|
binding = resolve_attribute_name_argument(arguments[1].value)
|
|
@@ -131,6 +131,7 @@ module MilkTea
|
|
|
131
131
|
|
|
132
132
|
class Checker
|
|
133
133
|
include Intrinsics
|
|
134
|
+
include CompileTime::MethodFolding
|
|
134
135
|
|
|
135
136
|
attr_reader :ctx
|
|
136
137
|
|
|
@@ -222,8 +223,14 @@ module MilkTea
|
|
|
222
223
|
when AST::WhenStmt
|
|
223
224
|
body = when_chosen_body(decl)
|
|
224
225
|
collect_emit_from_declarations(body) if body
|
|
225
|
-
when AST::StructDecl
|
|
226
|
-
# no emit in structs
|
|
226
|
+
when AST::StructDecl
|
|
227
|
+
# no emit in structs
|
|
228
|
+
when AST::ExtendingBlock
|
|
229
|
+
decl.methods.each do |method|
|
|
230
|
+
next unless method.respond_to?(:const) && method.const && method.body
|
|
231
|
+
|
|
232
|
+
collect_emit_from_statements(method.body)
|
|
233
|
+
end
|
|
227
234
|
end
|
|
228
235
|
end
|
|
229
236
|
end
|
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.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -205,6 +205,7 @@ files:
|
|
|
205
205
|
- lib/milk_tea/core/c_backend/type_declaration.rb
|
|
206
206
|
- lib/milk_tea/core/c_backend/type_system.rb
|
|
207
207
|
- lib/milk_tea/core/compile_time.rb
|
|
208
|
+
- lib/milk_tea/core/compile_time/method_folding.rb
|
|
208
209
|
- lib/milk_tea/core/control_flow.rb
|
|
209
210
|
- lib/milk_tea/core/control_flow/builder.rb
|
|
210
211
|
- lib/milk_tea/core/control_flow/constant_propagation.rb
|
|
@@ -628,7 +629,7 @@ metadata:
|
|
|
628
629
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
629
630
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
630
631
|
post_install_message: |
|
|
631
|
-
Milk Tea 0.
|
|
632
|
+
Milk Tea 0.4.0 installed!
|
|
632
633
|
|
|
633
634
|
System requirements:
|
|
634
635
|
- A C compiler (gcc or clang) must be available on PATH
|