mt-lang 0.3.42 → 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/README.md +5 -2
- data/docs/index.html +20 -4
- data/docs/language-design.md +5 -2
- data/docs/language-manual.md +28 -5
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/ast.rb +2 -2
- data/lib/milk_tea/core/c_backend/feature_detection.rb +6 -1
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +30 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +74 -0
- data/lib/milk_tea/core/c_backend.rb +9 -0
- 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/async/normalization.rb +2 -0
- data/lib/milk_tea/core/lowering/expressions.rb +31 -6
- data/lib/milk_tea/core/lowering/proc.rb +3 -0
- data/lib/milk_tea/core/lowering/resolve.rb +79 -2
- data/lib/milk_tea/core/lowering/utils.rb +26 -0
- 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/expressions.rb +13 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +46 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +70 -1
- data/lib/milk_tea/core/semantic_analyzer.rb +9 -2
- data/lib/milk_tea/core/types/registry.rb +39 -16
- data/lib/milk_tea/lsp/server/hover.rb +0 -1
- data/lib/milk_tea/lsp/server/text_documents.rb +1 -1
- metadata +3 -2
|
@@ -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
|
|
|
@@ -400,6 +400,8 @@ module MilkTea
|
|
|
400
400
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.element_type))], nullable: false)
|
|
401
401
|
when Types::Task
|
|
402
402
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["Task"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.result_type))], nullable: false)
|
|
403
|
+
when Types::StringView
|
|
404
|
+
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["str"]), arguments: [], nullable: false)
|
|
403
405
|
when Types::TypeVar
|
|
404
406
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
|
|
405
407
|
when Types::StructInstance
|
|
@@ -1364,13 +1364,19 @@ module MilkTea
|
|
|
1364
1364
|
when AST::IndexAccess
|
|
1365
1365
|
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1366
1366
|
receiver = lower_expression(expression.receiver, env:)
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1370
|
-
elsif receiver_type.is_a?(Types::Span)
|
|
1371
|
-
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1367
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
1368
|
+
lower_range_index_access(receiver, receiver_type, expression.index, env:, type:)
|
|
1372
1369
|
else
|
|
1373
|
-
|
|
1370
|
+
index = lower_expression(expression.index, env:)
|
|
1371
|
+
if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
|
|
1372
|
+
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1373
|
+
elsif receiver_type.is_a?(Types::Span)
|
|
1374
|
+
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1375
|
+
elsif receiver_type.is_a?(Types::StringView)
|
|
1376
|
+
IR::Call.new(callee: "mt_str_index", arguments: [receiver, index], type:)
|
|
1377
|
+
else
|
|
1378
|
+
IR::Index.new(receiver:, index:, type:)
|
|
1379
|
+
end
|
|
1374
1380
|
end
|
|
1375
1381
|
when AST::UnaryOp
|
|
1376
1382
|
raise LoweringError.new("propagation expressions must be prepared before direct lowering", line: 0, column: 0, path: @ctx.current_analysis_path) if expression.operator == "?"
|
|
@@ -1452,6 +1458,25 @@ module MilkTea
|
|
|
1452
1458
|
end
|
|
1453
1459
|
end
|
|
1454
1460
|
|
|
1461
|
+
def lower_range_index_access(receiver, receiver_type, range, env:, type:)
|
|
1462
|
+
start = lower_range_index_bound(range.start_expr, env:)
|
|
1463
|
+
stop = lower_range_index_bound(range.end_expr, env:)
|
|
1464
|
+
|
|
1465
|
+
if receiver_type.is_a?(Types::StringView)
|
|
1466
|
+
return IR::Call.new(callee: "mt_str_slice", arguments: [receiver, start, stop], type:)
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
span_type = range_index_result_type(receiver_type)
|
|
1470
|
+
span_argument = receiver_type.is_a?(Types::Span) ? receiver : lower_array_to_span_expression(receiver, span_type)
|
|
1471
|
+
IR::Call.new(callee: span_slice_callee(span_type), arguments: [span_argument, start, stop], type: span_type)
|
|
1472
|
+
end
|
|
1473
|
+
|
|
1474
|
+
def lower_range_index_bound(bound, env:)
|
|
1475
|
+
ptr_uint = @ctx.types.fetch("ptr_uint")
|
|
1476
|
+
lowered = lower_expression(bound, env:, expected_type: ptr_uint)
|
|
1477
|
+
lowered.type == ptr_uint ? lowered : IR::Cast.new(target_type: ptr_uint, expression: lowered, type: ptr_uint)
|
|
1478
|
+
end
|
|
1479
|
+
|
|
1455
1480
|
def lower_member_access(expression, env:, type:)
|
|
1456
1481
|
if (type_expr = resolve_type_expression(expression.receiver))
|
|
1457
1482
|
if type_expr.is_a?(Types::Variant)
|
|
@@ -206,6 +206,9 @@ module MilkTea
|
|
|
206
206
|
when AST::IndexAccess
|
|
207
207
|
collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
|
|
208
208
|
collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
|
|
209
|
+
when AST::RangeExpr
|
|
210
|
+
collect_proc_captures_from_expression(expression.start_expr, env, local_scopes, captures)
|
|
211
|
+
collect_proc_captures_from_expression(expression.end_expr, env, local_scopes, captures)
|
|
209
212
|
when AST::Specialization
|
|
210
213
|
collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
|
|
211
214
|
expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
|
|
@@ -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,
|
|
@@ -786,6 +787,9 @@ module MilkTea
|
|
|
786
787
|
raise LoweringError.new("unknown member #{expression.member}", line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
|
|
787
788
|
when AST::IndexAccess
|
|
788
789
|
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
790
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
791
|
+
return range_index_result_type(receiver_type)
|
|
792
|
+
end
|
|
789
793
|
index_type = infer_expression_type(expression.index, env:)
|
|
790
794
|
infer_index_result_type(receiver_type, index_type)
|
|
791
795
|
when AST::UnaryOp
|
|
@@ -1555,7 +1559,7 @@ module MilkTea
|
|
|
1555
1559
|
end
|
|
1556
1560
|
resolve_current_module_const_value(identifier_expression.name)
|
|
1557
1561
|
end,
|
|
1558
|
-
resolve_member_access: lambda { |ma|
|
|
1562
|
+
resolve_member_access: lambda { |ma| compile_time_const_value(ma, env:) },
|
|
1559
1563
|
resolve_type_ref: lambda { |tr| resolve_type_ref(tr) },
|
|
1560
1564
|
resolve_call: lambda { |ce| evaluate_compile_time_call(ce, env:) },
|
|
1561
1565
|
))
|
|
@@ -1570,6 +1574,15 @@ module MilkTea
|
|
|
1570
1574
|
when "name" then next receiver_value.member_name
|
|
1571
1575
|
when "value" then next receiver_value.member_value
|
|
1572
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"
|
|
1573
1586
|
end
|
|
1574
1587
|
end
|
|
1575
1588
|
|
|
@@ -1591,6 +1604,11 @@ module MilkTea
|
|
|
1591
1604
|
|
|
1592
1605
|
def evaluate_compile_time_call(expression, env:)
|
|
1593
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
|
|
1594
1612
|
when AST::Identifier
|
|
1595
1613
|
case expression.callee.name
|
|
1596
1614
|
when "field_of"
|
|
@@ -1791,12 +1809,51 @@ module MilkTea
|
|
|
1791
1809
|
initial_vars[param.name] = arg_value
|
|
1792
1810
|
end
|
|
1793
1811
|
|
|
1812
|
+
variable_types = func.body_params.each_with_object({}) do |param, acc|
|
|
1813
|
+
acc[param.name] = param.type
|
|
1814
|
+
end
|
|
1794
1815
|
evaluator = ConstFnLowerEvaluator.new(self)
|
|
1795
|
-
ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars)
|
|
1816
|
+
ctx = CompileTime::BlockContext.new(evaluator, initial_variables: initial_vars, variable_types: variable_types)
|
|
1796
1817
|
result = ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
1797
1818
|
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
1798
1819
|
end
|
|
1799
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
|
+
|
|
1800
1857
|
class ConstFnLowerEvaluator
|
|
1801
1858
|
def initialize(lowerer)
|
|
1802
1859
|
@lowerer = lowerer
|
|
@@ -1813,6 +1870,26 @@ module MilkTea
|
|
|
1813
1870
|
def top_level_function(name)
|
|
1814
1871
|
@lowerer.instance_variable_get(:@ctx).functions&.[](name)
|
|
1815
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
|
|
1816
1893
|
end
|
|
1817
1894
|
|
|
1818
1895
|
def evaluate_reflection_target_argument(expression, env:)
|