mt-lang 0.3.16 → 0.3.20

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/docs/language-design.md +3 -4
  3. data/docs/language-manual.md +3 -3
  4. data/lib/milk_tea/base.rb +1 -1
  5. data/lib/milk_tea/core/{binding_types.rb → bindings.rb} +1 -1
  6. data/lib/milk_tea/core/c_backend/expressions.rb +34 -23
  7. data/lib/milk_tea/core/c_backend/feature_detection.rb +25 -45
  8. data/lib/milk_tea/core/c_backend/type_collectors.rb +18 -30
  9. data/lib/milk_tea/core/c_backend/type_declaration.rb +0 -6
  10. data/lib/milk_tea/core/c_backend.rb +63 -54
  11. data/lib/milk_tea/core/compile_time.rb +279 -74
  12. data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
  13. data/lib/milk_tea/core/{compatibility_helpers.rb → intrinsics.rb} +9 -2
  14. data/lib/milk_tea/core/lexer.rb +55 -48
  15. data/lib/milk_tea/core/lowering/block.rb +9 -0
  16. data/lib/milk_tea/core/lowering/calls.rb +2 -0
  17. data/lib/milk_tea/core/lowering/declarations.rb +1 -1
  18. data/lib/milk_tea/core/lowering/functions.rb +11 -8
  19. data/lib/milk_tea/core/lowering/resolve.rb +10 -3
  20. data/lib/milk_tea/core/lowering/scans.rb +13 -18
  21. data/lib/milk_tea/core/lowering.rb +18 -18
  22. data/lib/milk_tea/core/module_binder.rb +9 -10
  23. data/lib/milk_tea/core/module_loader.rb +64 -57
  24. data/lib/milk_tea/core/module_path_resolver.rb +1 -4
  25. data/lib/milk_tea/core/parser/declarations.rb +43 -19
  26. data/lib/milk_tea/core/parser/expressions.rb +4 -7
  27. data/lib/milk_tea/core/parser/statements.rb +5 -5
  28. data/lib/milk_tea/core/parser.rb +43 -17
  29. data/lib/milk_tea/core/semantic_analyzer/calls.rb +24 -31
  30. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +20 -23
  31. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +117 -85
  32. data/lib/milk_tea/core/semantic_analyzer/statements.rb +19 -41
  33. data/lib/milk_tea/core/semantic_analyzer.rb +57 -38
  34. data/lib/milk_tea/core.rb +6 -2
  35. data/lib/milk_tea/dap/lldb_dap_backend.rb +153 -0
  36. data/lib/milk_tea/dap/server/lldb_backend.rb +1 -1
  37. data/lib/milk_tea/dap.rb +1 -1
  38. data/lib/milk_tea/lsp/server/semantic_tokens.rb +6 -0
  39. data/lib/milk_tea/lsp.rb +1 -7
  40. data/lib/milk_tea/{bindings/cli.rb → tooling/bindgen_cli.rb} +1 -1
  41. data/lib/milk_tea/tooling/cli/commands/bindgen.rb +11 -0
  42. data/lib/milk_tea/tooling/cli/commands/build.rb +37 -0
  43. data/lib/milk_tea/tooling/cli/commands/cache.rb +46 -0
  44. data/lib/milk_tea/tooling/cli/commands/check.rb +116 -0
  45. data/lib/milk_tea/tooling/cli/commands/command_base.rb +8 -0
  46. data/lib/milk_tea/tooling/cli/commands/completions.rb +48 -0
  47. data/lib/milk_tea/tooling/cli/commands/dap.rb +58 -0
  48. data/lib/milk_tea/tooling/cli/commands/debug.rb +77 -0
  49. data/lib/milk_tea/tooling/cli/commands/deps.rb +17 -0
  50. data/lib/milk_tea/tooling/cli/commands/docs.rb +58 -0
  51. data/lib/milk_tea/tooling/cli/commands/emit_c.rb +64 -0
  52. data/lib/milk_tea/tooling/cli/commands/format.rb +199 -0
  53. data/lib/milk_tea/tooling/cli/commands/lex.rb +46 -0
  54. data/lib/milk_tea/tooling/cli/commands/lint.rb +248 -0
  55. data/lib/milk_tea/tooling/cli/commands/lower.rb +50 -0
  56. data/lib/milk_tea/tooling/cli/commands/lsp.rb +43 -0
  57. data/lib/milk_tea/tooling/cli/commands/new.rb +26 -0
  58. data/lib/milk_tea/tooling/cli/commands/parse.rb +51 -0
  59. data/lib/milk_tea/tooling/cli/commands/run.rb +99 -0
  60. data/lib/milk_tea/tooling/cli/commands/snapshot.rb +117 -0
  61. data/lib/milk_tea/tooling/cli/commands/test.rb +557 -0
  62. data/lib/milk_tea/tooling/cli/commands/toolchain.rb +16 -0
  63. data/lib/milk_tea/tooling/cli.rb +101 -1893
  64. data/lib/milk_tea/tooling.rb +1 -1
  65. metadata +29 -9
  66. data/lib/milk_tea/core/compile_time/const_eval.rb +0 -182
  67. data/lib/milk_tea/core/module_loader/errors.rb +0 -23
  68. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -156
  69. /data/lib/milk_tea/core/lowering/{async.rb → async/frame_builder.rb} +0 -0
@@ -1,6 +1,185 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "compile_time/const_eval"
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
@@ -73,75 +252,7 @@ module MilkTea
73
252
  resolve_member_access: ->(ma_expr) {
74
253
  @checker.evaluate_compile_time_const_value(ma_expr, scopes:)
75
254
  },
76
- resolve_call: ->(call_expr) {
77
- if call_expr.callee.is_a?(AST::Identifier)
78
- func = @checker.top_level_function(call_expr.callee.name)
79
- if func&.ast&.respond_to?(:const) && func.ast.const
80
- begin
81
- initial_vars = {}
82
- func.ast.params.each_with_index do |param, idx|
83
- return nil if idx >= call_expr.arguments.length
84
-
85
- arg_expr = call_expr.arguments[idx].value
86
- arg_value = case arg_expr
87
- when AST::Identifier
88
- @variables[arg_expr.name] || @checker.evaluate_compile_time_const_value(arg_expr, scopes:)
89
- else
90
- CompileTime.evaluate(
91
- arg_expr,
92
- resolve_identifier: ->(id) { @variables[id.name] || @checker.evaluate_compile_time_const_value(id, scopes:) },
93
- resolve_member_access: ->(ma) { @checker.evaluate_compile_time_const_value(ma, scopes:) },
94
- resolve_type_ref: nil,
95
- resolve_call: nil,
96
- )
97
- end
98
- return nil unless arg_value
99
-
100
- initial_vars[param.name] = arg_value
101
- end
102
- ctx = CompileTime::BlockContext.new(@checker, initial_variables: initial_vars)
103
- next ctx.evaluate_block(func.ast.body, scopes:)
104
- rescue CompileTime::ReturnValue => e
105
- next e.value
106
- end
107
- end
108
- end
109
-
110
- types = if @checker.respond_to?(:types)
111
- @checker.types
112
- else
113
- @checker.instance_variable_get(:@ctx).types
114
- end
115
- callee_name = if call_expr.callee.is_a?(AST::Specialization) && call_expr.callee.callee.respond_to?(:name)
116
- call_expr.callee.callee.name
117
- elsif call_expr.callee.respond_to?(:name)
118
- call_expr.callee.name
119
- end
120
- if callee_name && (type = types[callee_name]) && type.is_a?(Types::Struct)
121
- fields = {}
122
- call_expr.arguments.each do |argument|
123
- val = evaluate_expression(argument.value, scopes:)
124
- return nil unless val
125
- fields[argument.name] = val
126
- end
127
- next fields
128
- end
129
-
130
- if call_expr.callee.is_a?(AST::Specialization) && @checker.respond_to?(:resolve_type_expression)
131
- resolved = @checker.resolve_type_expression(call_expr.callee)
132
- if resolved && @checker.respond_to?(:array_type?) && @checker.array_type?(resolved)
133
- values = []
134
- call_expr.arguments.each do |argument|
135
- val = evaluate_expression(argument.value, scopes:)
136
- return nil unless val
137
- values << val
138
- end
139
- next values
140
- end
141
- end
142
-
143
- @checker.evaluate_compile_time_const_value(call_expr, scopes:)
144
- },
255
+ resolve_call: ->(call_expr) { resolve_compile_time_call(call_expr, scopes:) },
145
256
  )
146
257
  end
147
258
  end
@@ -256,13 +367,108 @@ module MilkTea
256
367
 
257
368
  nil
258
369
  end
370
+
371
+ def resolve_compile_time_call(call_expr, scopes:)
372
+ result = try_const_function_call(call_expr, scopes:)
373
+ return result if result
374
+
375
+ result = try_struct_constructor_call(call_expr, scopes:)
376
+ return result if result
377
+
378
+ result = try_array_constructor_call(call_expr, scopes:)
379
+ return result if result
380
+
381
+ @checker.evaluate_compile_time_const_value(call_expr, scopes:)
382
+ end
383
+
384
+ def try_const_function_call(call_expr, scopes:)
385
+ return unless call_expr.callee.is_a?(AST::Identifier)
386
+
387
+ func = @checker.top_level_function(call_expr.callee.name)
388
+ return unless func&.ast&.respond_to?(:const) && func.ast.const
389
+
390
+ begin
391
+ initial_vars = {}
392
+ func.ast.params.each_with_index do |param, idx|
393
+ return nil if idx >= call_expr.arguments.length
394
+
395
+ arg_expr = call_expr.arguments[idx].value
396
+ arg_value = case arg_expr
397
+ when AST::Identifier
398
+ @variables[arg_expr.name] || @checker.evaluate_compile_time_const_value(arg_expr, scopes:)
399
+ else
400
+ CompileTime.evaluate(
401
+ arg_expr,
402
+ resolve_identifier: ->(id) { @variables[id.name] || @checker.evaluate_compile_time_const_value(id, scopes:) },
403
+ resolve_member_access: ->(ma) { @checker.evaluate_compile_time_const_value(ma, scopes:) },
404
+ resolve_type_ref: nil,
405
+ resolve_call: nil,
406
+ )
407
+ end
408
+ return nil unless arg_value
409
+
410
+ initial_vars[param.name] = arg_value
411
+ end
412
+ ctx = BlockContext.new(@checker, initial_variables: initial_vars)
413
+ ctx.evaluate_block(func.ast.body, scopes:)
414
+ rescue ReturnValue => e
415
+ e.value
416
+ end
417
+ end
418
+
419
+ def try_struct_constructor_call(call_expr, scopes:)
420
+ types = if @checker.respond_to?(:types)
421
+ @checker.types
422
+ else
423
+ @checker.instance_variable_get(:@ctx).types
424
+ end
425
+ callee_name = if call_expr.callee.is_a?(AST::Specialization) && call_expr.callee.callee.respond_to?(:name)
426
+ call_expr.callee.callee.name
427
+ elsif call_expr.callee.respond_to?(:name)
428
+ call_expr.callee.name
429
+ end
430
+ return unless callee_name
431
+
432
+ type = types[callee_name]
433
+ return unless type.is_a?(Types::Struct)
434
+
435
+ fields = {}
436
+ call_expr.arguments.each do |argument|
437
+ val = evaluate_expression(argument.value, scopes:)
438
+ return nil unless val
439
+ fields[argument.name] = val
440
+ end
441
+ fields
442
+ end
443
+
444
+ def try_array_constructor_call(call_expr, scopes:)
445
+ return unless call_expr.callee.is_a?(AST::Specialization)
446
+ return unless @checker.respond_to?(:resolve_type_expression)
447
+
448
+ resolved = @checker.resolve_type_expression(call_expr.callee)
449
+ return unless resolved && @checker.respond_to?(:array_type?) && @checker.array_type?(resolved)
450
+
451
+ values = []
452
+ call_expr.arguments.each do |argument|
453
+ val = evaluate_expression(argument.value, scopes:)
454
+ return nil unless val
455
+ values << val
456
+ end
457
+ values
458
+ end
259
459
  end
260
460
 
261
461
  class Evaluator < ConstEval::Evaluator
262
462
 
263
463
  def resolve_layout_type(type_ref)
264
- super
265
- rescue SemanticError
464
+ result = begin
465
+ super
466
+ rescue SemanticError
467
+ nil
468
+ end
469
+ return result if result
470
+ return result if result
471
+
266
472
  return unless type_ref.respond_to?(:name) && type_ref.name.parts.length >= 1
267
473
 
268
474
  expression = ::MilkTea::AST.build_chain_from_parts(type_ref.name.parts)
@@ -276,7 +482,6 @@ module MilkTea
276
482
  nil
277
483
  end
278
484
 
279
-
280
485
  end
281
486
 
282
487
  def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
@@ -354,4 +559,4 @@ module MilkTea
354
559
  end
355
560
  end
356
561
  end
357
- end
562
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../compile_time/const_eval"
3
+ require_relative "../compile_time"
4
4
 
5
5
  module MilkTea
6
6
  module ControlFlow
@@ -3,7 +3,7 @@
3
3
  require_relative "types/predicates"
4
4
 
5
5
  module MilkTea
6
- module CompatibilityHelpers
6
+ module Intrinsics
7
7
  include Types::Predicates
8
8
 
9
9
  BUILTIN_CSTR = Types::Registry.primitive("cstr")
@@ -45,6 +45,13 @@ module MilkTea
45
45
  "with" => :simd_lane_with,
46
46
  }.freeze
47
47
 
48
+ SPECIALIZED_RECEIVERS = {
49
+ "str_buffer" => { predicate: :str_buffer_type?, kinds: STR_BUFFER_METHOD_KINDS },
50
+ "event" => { predicate: :event_type?, kinds: EVENT_METHOD_KINDS },
51
+ "atomic" => { predicate: :atomic_type?, kinds: ATOMIC_METHOD_KINDS },
52
+ "simd" => { predicate: :simd_type?, kinds: SIMD_METHOD_KINDS },
53
+ }.freeze
54
+
48
55
  def type_ref_from_specialization(expression)
49
56
  case expression.callee
50
57
  when AST::Identifier
@@ -78,4 +85,4 @@ module MilkTea
78
85
  expression.is_a?(AST::StringLiteral) && !expression.cstring && expected_type == BUILTIN_CSTR
79
86
  end
80
87
  end
81
- end
88
+ end
@@ -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
@@ -279,43 +279,17 @@ module MilkTea
279
279
  break
280
280
  end
281
281
 
282
- if char == "c" && heredoc_start?(line, index, cstring: true)
283
- return lex_heredoc(lines, line_index, index, line_number, line_offset, cstring: true)
284
- end
285
-
286
- if char == "c" && line[index + 1] == "<" && line[index + 2] == "-" && identifier_start?(line[index + 3])
287
- error = LexError.new("expected '<<-' for heredoc string; did you mean 'c<<-#{identifier_start_token(line, index + 3)}'?", line: line_number, column: index + 1, path: @path)
288
- if @recovery_errors
289
- @recovery_errors << error
290
- else
291
- raise error
282
+ if (char == "c" || char == "f")
283
+ result = dispatch_cf_prefix(char, line, index, line_number, line_offset, lines:, line_index:)
284
+ if result
285
+ if result[0] == :return
286
+ return result[1]
287
+ else
288
+ index = result[1]
289
+ next
290
+ end
292
291
  end
293
292
  end
294
- if char == "c" && line[index + 1] == '"'
295
- result = lex_string(lines, line_index, line, index, line_number, line_offset:, cstring: true)
296
- return result.consumed_lines if result.consumed_lines > 1
297
-
298
- index = result.next_index
299
- next
300
- end
301
-
302
- if char == "f" && heredoc_start?(line, index, format: true)
303
- return lex_heredoc(lines, line_index, index, line_number, line_offset, cstring: false, format: true)
304
- end
305
-
306
- if char == "f" && line[index + 1] == "<" && line[index + 2] == "-" && identifier_start?(line[index + 3])
307
- error = LexError.new("expected '<<-' for heredoc string; did you mean 'f<<-#{identifier_start_token(line, index + 3)}'?", line: line_number, column: index + 1, path: @path)
308
- if @recovery_errors
309
- @recovery_errors << error
310
- else
311
- raise error
312
- end
313
- end
314
-
315
- if char == "f" && line[index + 1] == '"'
316
- index = lex_format_string(line, index, line_number, line_offset:)
317
- next
318
- end
319
293
 
320
294
  if char == "<" && heredoc_start?(line, index)
321
295
  return lex_heredoc(lines, line_index, index, line_number, line_offset, cstring: false)
@@ -371,8 +345,6 @@ module MilkTea
371
345
  1
372
346
  end
373
347
 
374
- # ── newline emission ────────────────────────────────────────────
375
-
376
348
  def emit_line_newline(line, line_number, line_offset, has_newline)
377
349
  newline_start = line_offset + line.bytesize
378
350
  newline_end = has_newline ? (newline_start + 1) : newline_start
@@ -393,7 +365,42 @@ module MilkTea
393
365
  end
394
366
  end
395
367
 
396
- # ── token construction ──────────────────────────────────────────
368
+ def dispatch_cf_prefix(char, line, index, line_number, line_offset, lines:, line_index:)
369
+ return nil unless char == "c" || char == "f"
370
+
371
+ is_c = char == "c"
372
+ is_f = char == "f"
373
+
374
+ if heredoc_start?(line, index, cstring: is_c, format: is_f)
375
+ n = lex_heredoc(lines, line_index, index, line_number, line_offset, cstring: is_c, format: is_f)
376
+ return [:return, n]
377
+ end
378
+
379
+ if line[index + 1] == "<" && line[index + 2] == "-" && identifier_start?(line[index + 3])
380
+ error = LexError.new("expected '<<-' for heredoc string; did you mean '#{char}<<-#{identifier_start_token(line, index + 3)}'?", line: line_number, column: index + 1, path: @path)
381
+ if @recovery_errors
382
+ @recovery_errors << error
383
+ else
384
+ raise error
385
+ end
386
+ return nil
387
+ end
388
+
389
+ if line[index + 1] == '"'
390
+ if is_c
391
+ string_result = lex_string(lines, line_index, line, index, line_number, line_offset:, cstring: true)
392
+ if string_result.consumed_lines > 1
393
+ return [:return, string_result.consumed_lines]
394
+ end
395
+ return [:next, string_result.next_index]
396
+ end
397
+
398
+ new_index = lex_format_string(line, index, line_number, line_offset:)
399
+ return [:next, new_index]
400
+ end
401
+
402
+ nil
403
+ end
397
404
 
398
405
  def token(type, lexeme, literal, line, column, start_offset:, end_offset:)
399
406
  Token.new(
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "expressions"
4
+ require_relative "loops"
5
+ require_relative "proc"
6
+ require_relative "resolve"
7
+ require_relative "utils"
8
+ require_relative "foreign_cstr"
9
+ require_relative "declarations"
10
+ require_relative "calls"
11
+
3
12
  module MilkTea
4
13
  module LowererBlock
5
14
  def lower_block(statements, env:, active_defers:, return_type:, loop_flow:, allow_return: true)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "dyn"
4
+
3
5
  module MilkTea
4
6
  module LowererCalls
5
7
  def lower_call(expression, env:, type:)
@@ -78,7 +78,7 @@ module MilkTea
78
78
  end
79
79
  IR::AggregateLiteral.new(type:, fields:)
80
80
  else
81
- IR::IntegerLiteral.new(value: 0, type:)
81
+ raise LoweringError.new("unsupported const value type #{const_value.class}", line: 0, column: 0, path: @ctx.current_analysis_path)
82
82
  end
83
83
  end
84
84
 
@@ -1,14 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "resolve"
4
+ require_relative "utils"
5
+ require_relative "block"
6
+ require_relative "async/normalization"
7
+ require_relative "async/lowering"
8
+
3
9
  module MilkTea
4
10
  module LowererFunctions
5
11
  def lower_functions
6
12
  lowered = []
13
+ cursor = 0
14
+ declarations = @ctx.ast.declarations
7
15
 
8
- changed = true
9
- while changed
10
- changed = false
11
-
16
+ while cursor < declarations.length
12
17
  expanded_declarations.each do |decl|
13
18
  case decl
14
19
  when AST::FunctionDef
@@ -21,7 +26,6 @@ module MilkTea
21
26
 
22
27
  lowered << lower_function_decl(instance)
23
28
  @artifacts.lowered_function_linkage_names[linkage_name] = true
24
- changed = true
25
29
  end
26
30
  else
27
31
  linkage_name = function_binding_c_name(binding, module_name: @ctx.module_name)
@@ -37,7 +41,6 @@ module MilkTea
37
41
  @artifacts.lowered_function_linkage_names[entrypoint.linkage_name] = true
38
42
  end
39
43
  end
40
- changed = true
41
44
  end
42
45
  when AST::ExtendingBlock
43
46
  receiver_type = resolve_extending_receiver_type(@ctx.analysis, decl.type_name)
@@ -56,7 +59,6 @@ module MilkTea
56
59
 
57
60
  lowered << lower_function_decl(instance, receiver_type:)
58
61
  @artifacts.lowered_function_linkage_names[linkage_name] = true
59
- changed = true
60
62
  end
61
63
  else
62
64
  linkage_name = function_binding_c_name(binding, module_name: @ctx.module_name, receiver_type:)
@@ -64,11 +66,12 @@ module MilkTea
64
66
 
65
67
  lowered << lower_function_decl(binding, receiver_type:)
66
68
  @artifacts.lowered_function_linkage_names[linkage_name] = true
67
- changed = true
68
69
  end
69
70
  end
70
71
  end
71
72
  end
73
+
74
+ cursor = declarations.length
72
75
  end
73
76
 
74
77
  lowered
@@ -1518,12 +1518,19 @@ module MilkTea
1518
1518
  def resolve_type_argument_ref(type_ref, type_params:)
1519
1519
  return resolve_type_ref(type_ref, type_params:) unless literal_type_argument_name_candidate?(type_ref)
1520
1520
 
1521
- resolve_type_ref(type_ref, type_params:)
1522
- rescue LoweringError => error
1521
+ result = try_resolve_type_ref(type_ref, type_params:)
1522
+ return result if result
1523
+
1523
1524
  literal_type_argument = resolve_named_literal_type_argument(type_ref)
1524
1525
  return literal_type_argument if literal_type_argument
1525
1526
 
1526
- raise error
1527
+ resolve_type_ref(type_ref, type_params:)
1528
+ end
1529
+
1530
+ def try_resolve_type_ref(type_ref, type_params:)
1531
+ resolve_type_ref(type_ref, type_params:)
1532
+ rescue LoweringError
1533
+ nil
1527
1534
  end
1528
1535
 
1529
1536
  def literal_type_argument_name_candidate?(type_ref)