mt-lang 0.3.17 → 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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/c_backend/expressions.rb +34 -23
- data/lib/milk_tea/core/c_backend/feature_detection.rb +25 -45
- data/lib/milk_tea/core/c_backend/type_collectors.rb +18 -30
- data/lib/milk_tea/core/c_backend/type_declaration.rb +0 -6
- data/lib/milk_tea/core/c_backend.rb +49 -39
- data/lib/milk_tea/core/compile_time.rb +98 -72
- data/lib/milk_tea/core/intrinsics.rb +7 -0
- data/lib/milk_tea/core/lexer.rb +46 -35
- data/lib/milk_tea/core/lowering/block.rb +9 -0
- data/lib/milk_tea/core/lowering/calls.rb +2 -0
- data/lib/milk_tea/core/lowering/declarations.rb +1 -1
- data/lib/milk_tea/core/lowering/functions.rb +11 -8
- data/lib/milk_tea/core/lowering/resolve.rb +10 -3
- data/lib/milk_tea/core/lowering/scans.rb +13 -18
- data/lib/milk_tea/core/module_binder.rb +9 -10
- data/lib/milk_tea/core/module_loader.rb +38 -42
- data/lib/milk_tea/core/module_path_resolver.rb +1 -4
- data/lib/milk_tea/core/parser/declarations.rb +43 -19
- data/lib/milk_tea/core/parser/expressions.rb +4 -7
- data/lib/milk_tea/core/parser/statements.rb +5 -5
- data/lib/milk_tea/core/parser.rb +26 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +24 -31
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +20 -23
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +117 -85
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +19 -41
- data/lib/milk_tea/core/semantic_analyzer.rb +56 -37
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +6 -0
- data/lib/milk_tea/tooling/cli/commands/bindgen.rb +11 -0
- data/lib/milk_tea/tooling/cli/commands/build.rb +37 -0
- data/lib/milk_tea/tooling/cli/commands/cache.rb +46 -0
- data/lib/milk_tea/tooling/cli/commands/check.rb +116 -0
- data/lib/milk_tea/tooling/cli/commands/command_base.rb +8 -0
- data/lib/milk_tea/tooling/cli/commands/completions.rb +48 -0
- data/lib/milk_tea/tooling/cli/commands/dap.rb +58 -0
- data/lib/milk_tea/tooling/cli/commands/debug.rb +77 -0
- data/lib/milk_tea/tooling/cli/commands/deps.rb +17 -0
- data/lib/milk_tea/tooling/cli/commands/docs.rb +58 -0
- data/lib/milk_tea/tooling/cli/commands/emit_c.rb +64 -0
- data/lib/milk_tea/tooling/cli/commands/format.rb +199 -0
- data/lib/milk_tea/tooling/cli/commands/lex.rb +46 -0
- data/lib/milk_tea/tooling/cli/commands/lint.rb +248 -0
- data/lib/milk_tea/tooling/cli/commands/lower.rb +50 -0
- data/lib/milk_tea/tooling/cli/commands/lsp.rb +43 -0
- data/lib/milk_tea/tooling/cli/commands/new.rb +26 -0
- data/lib/milk_tea/tooling/cli/commands/parse.rb +51 -0
- data/lib/milk_tea/tooling/cli/commands/run.rb +99 -0
- data/lib/milk_tea/tooling/cli/commands/snapshot.rb +117 -0
- data/lib/milk_tea/tooling/cli/commands/test.rb +557 -0
- data/lib/milk_tea/tooling/cli/commands/toolchain.rb +16 -0
- data/lib/milk_tea/tooling/cli.rb +101 -1893
- metadata +24 -2
|
@@ -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
|
data/lib/milk_tea/core/lexer.rb
CHANGED
|
@@ -279,43 +279,17 @@ module MilkTea
|
|
|
279
279
|
break
|
|
280
280
|
end
|
|
281
281
|
|
|
282
|
-
if char == "c"
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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)
|
|
@@ -391,6 +365,43 @@ module MilkTea
|
|
|
391
365
|
end
|
|
392
366
|
end
|
|
393
367
|
|
|
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
|
|
404
|
+
|
|
394
405
|
def token(type, lexeme, literal, line, column, start_offset:, end_offset:)
|
|
395
406
|
Token.new(
|
|
396
407
|
type:,
|
|
@@ -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)
|
|
@@ -78,7 +78,7 @@ module MilkTea
|
|
|
78
78
|
end
|
|
79
79
|
IR::AggregateLiteral.new(type:, fields:)
|
|
80
80
|
else
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1522
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "functions"
|
|
4
|
+
require_relative "resolve"
|
|
5
|
+
require_relative "utils"
|
|
6
|
+
|
|
3
7
|
module MilkTea
|
|
4
8
|
module LowererScans
|
|
5
9
|
def collect_structs
|
|
6
|
-
@ctx.ast.declarations.each
|
|
7
|
-
case decl
|
|
8
|
-
when AST::WhenStmt
|
|
9
|
-
body = lower_when_chosen_body(decl)
|
|
10
|
-
body&.each { |nested| collect_struct_from_decl(nested) }
|
|
11
|
-
when AST::OpaqueDecl
|
|
12
|
-
@ctx.opaque_types[decl.name] = @ctx.types.fetch(decl.name)
|
|
13
|
-
when AST::StructDecl
|
|
14
|
-
@ctx.struct_types[decl.name] = @ctx.types.fetch(decl.name)
|
|
15
|
-
collect_nested_structs(decl)
|
|
16
|
-
when AST::UnionDecl
|
|
17
|
-
@ctx.union_types[decl.name] = @ctx.types.fetch(decl.name)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
10
|
+
@ctx.ast.declarations.each { |decl| collect_one_struct_decl(decl) }
|
|
20
11
|
end
|
|
21
12
|
|
|
22
|
-
def
|
|
13
|
+
def collect_one_struct_decl(decl)
|
|
23
14
|
case decl
|
|
15
|
+
when AST::WhenStmt
|
|
16
|
+
body = lower_when_chosen_body(decl)
|
|
17
|
+
body&.each { |nested| collect_one_struct_decl(nested) }
|
|
24
18
|
when AST::OpaqueDecl
|
|
25
19
|
@ctx.opaque_types[decl.name] = @ctx.types.fetch(decl.name)
|
|
26
20
|
when AST::StructDecl
|
|
@@ -28,12 +22,13 @@ module MilkTea
|
|
|
28
22
|
collect_nested_structs(decl)
|
|
29
23
|
when AST::UnionDecl
|
|
30
24
|
@ctx.union_types[decl.name] = @ctx.types.fetch(decl.name)
|
|
31
|
-
when AST::WhenStmt
|
|
32
|
-
body = lower_when_chosen_body(decl)
|
|
33
|
-
body&.each { |nested| collect_struct_from_decl(nested) }
|
|
34
25
|
end
|
|
35
26
|
end
|
|
36
27
|
|
|
28
|
+
def collect_struct_from_decl(decl)
|
|
29
|
+
collect_one_struct_decl(decl)
|
|
30
|
+
end
|
|
31
|
+
|
|
37
32
|
def lower_when_chosen_body(decl)
|
|
38
33
|
val = compile_time_const_value(decl.discriminant)
|
|
39
34
|
return nil if val.nil?
|
|
@@ -136,17 +136,16 @@ module MilkTea
|
|
|
136
136
|
imported_interface_binding?(interface, analysis.imports)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
BUILTIN_EXPORTABLE_RECEIVER_TYPES = %w[
|
|
140
|
+
StringView Primitive Vector Matrix Quaternion SoA Simd Span Task Dyn
|
|
141
|
+
].map { |n| "Types::#{n}" }.freeze
|
|
142
|
+
|
|
143
|
+
def builtin_exportable_receiver?(receiver_type)
|
|
144
|
+
BUILTIN_EXPORTABLE_RECEIVER_TYPES.any? { |name| receiver_type.class.to_s.end_with?(name.split("::").last) }
|
|
145
|
+
end
|
|
146
|
+
|
|
139
147
|
def exported_method_receiver?(receiver_type, analysis, exported_types)
|
|
140
|
-
return true if
|
|
141
|
-
return true if receiver_type.is_a?(Types::Primitive)
|
|
142
|
-
return true if receiver_type.is_a?(Types::Vector)
|
|
143
|
-
return true if receiver_type.is_a?(Types::Matrix)
|
|
144
|
-
return true if receiver_type.is_a?(Types::Quaternion)
|
|
145
|
-
return true if receiver_type.is_a?(Types::SoA)
|
|
146
|
-
return true if receiver_type.is_a?(Types::Simd)
|
|
147
|
-
return true if receiver_type.is_a?(Types::Span)
|
|
148
|
-
return true if receiver_type.is_a?(Types::Task)
|
|
149
|
-
return true if receiver_type.is_a?(Types::Dyn)
|
|
148
|
+
return true if builtin_exportable_receiver?(receiver_type)
|
|
150
149
|
return true if exported_types.value?(receiver_type)
|
|
151
150
|
return true if imported_receiver_type?(receiver_type, analysis.imports)
|
|
152
151
|
return exported_method_receiver?(receiver_type.base, analysis, exported_types) if receiver_type.is_a?(Types::Nullable)
|
|
@@ -317,48 +317,14 @@ module MilkTea
|
|
|
317
317
|
end
|
|
318
318
|
|
|
319
319
|
def imported_modules_for_ast(ast, importer_path: nil)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
ast.imports.each do |import|
|
|
323
|
-
import_path = @path_resolver.resolve_module_path(import.path.to_s, importer_path:, importer_module_name: ast.module_name.to_s)
|
|
324
|
-
|
|
325
|
-
if @forward_bindings.key?(import_path)
|
|
326
|
-
modules[import.path.to_s] = @forward_bindings[import_path]
|
|
327
|
-
else
|
|
328
|
-
import_analysis = check_path(import_path)
|
|
329
|
-
modules[import.path.to_s] = @binder.module_binding(import_analysis)
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
@async_runtime_installer.install_async_runtime_dependency!(ast, modules, importer_path:, collecting_errors: false)
|
|
334
|
-
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors: false)
|
|
335
|
-
modules.freeze
|
|
320
|
+
resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
336
321
|
end
|
|
337
322
|
|
|
338
|
-
def
|
|
339
|
-
|
|
340
|
-
current_imports = ast.imports.map { |import| import.path.to_s }.to_set
|
|
341
|
-
|
|
342
|
-
@analysis_cache.each_value do |analysis|
|
|
343
|
-
next unless analysis
|
|
344
|
-
next unless analysis.module_name
|
|
345
|
-
|
|
346
|
-
mod_name = analysis.module_name.to_s
|
|
347
|
-
next if current_imports.include?(mod_name)
|
|
348
|
-
next if mod_name == ast.module_name.to_s
|
|
349
|
-
|
|
350
|
-
types = analysis.respond_to?(:types) ? analysis.types : {}
|
|
351
|
-
types.each_key do |type_name|
|
|
352
|
-
type_str = type_name.to_s
|
|
353
|
-
index[type_str] ||= []
|
|
354
|
-
index[type_str] << mod_name unless index[type_str].include?(mod_name)
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
index
|
|
323
|
+
def imported_modules_for_ast_collecting_errors(ast, importer_path: nil)
|
|
324
|
+
resolve_imports_for_ast(ast, importer_path:, collecting: true)
|
|
359
325
|
end
|
|
360
326
|
|
|
361
|
-
def
|
|
327
|
+
def resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
362
328
|
modules = {}
|
|
363
329
|
errors = []
|
|
364
330
|
|
|
@@ -369,27 +335,55 @@ module MilkTea
|
|
|
369
335
|
if @forward_bindings.key?(import_path)
|
|
370
336
|
modules[import.path.to_s] = @forward_bindings[import_path]
|
|
371
337
|
else
|
|
372
|
-
import_analysis = check_path_collecting_errors(import_path)
|
|
338
|
+
import_analysis = collecting ? check_path_collecting_errors(import_path) : check_path(import_path)
|
|
373
339
|
modules[import.path.to_s] = @binder.module_binding(import_analysis)
|
|
374
340
|
end
|
|
375
341
|
rescue ModuleLoadError, PackageLockError, SemanticError => e
|
|
342
|
+
raise unless collecting
|
|
376
343
|
errors << ImportResolutionError.new(import:, error: e)
|
|
377
344
|
end
|
|
378
345
|
end
|
|
379
346
|
|
|
380
347
|
begin
|
|
381
|
-
@async_runtime_installer.install_async_runtime_dependency!(ast, modules, importer_path:, collecting_errors:
|
|
348
|
+
@async_runtime_installer.install_async_runtime_dependency!(ast, modules, importer_path:, collecting_errors: collecting)
|
|
382
349
|
rescue ModuleLoadError, PackageLockError => e
|
|
350
|
+
raise unless collecting
|
|
383
351
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
384
352
|
end
|
|
385
353
|
|
|
386
354
|
begin
|
|
387
|
-
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors:
|
|
355
|
+
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors: collecting)
|
|
388
356
|
rescue ModuleLoadError, PackageLockError => e
|
|
357
|
+
raise unless collecting
|
|
389
358
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
390
359
|
end
|
|
391
360
|
|
|
392
|
-
ImportResolution.new(modules: modules.freeze, errors: errors.freeze)
|
|
361
|
+
return ImportResolution.new(modules: modules.freeze, errors: errors.freeze) if collecting
|
|
362
|
+
|
|
363
|
+
modules.freeze
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def build_global_import_index(ast)
|
|
367
|
+
index = {}
|
|
368
|
+
current_imports = ast.imports.map { |import| import.path.to_s }.to_set
|
|
369
|
+
|
|
370
|
+
@analysis_cache.each_value do |analysis|
|
|
371
|
+
next unless analysis
|
|
372
|
+
next unless analysis.module_name
|
|
373
|
+
|
|
374
|
+
mod_name = analysis.module_name.to_s
|
|
375
|
+
next if current_imports.include?(mod_name)
|
|
376
|
+
next if mod_name == ast.module_name.to_s
|
|
377
|
+
|
|
378
|
+
types = analysis.respond_to?(:types) ? analysis.types : {}
|
|
379
|
+
types.each_key do |type_name|
|
|
380
|
+
type_str = type_name.to_s
|
|
381
|
+
index[type_str] ||= []
|
|
382
|
+
index[type_str] << mod_name unless index[type_str].include?(mod_name)
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
index
|
|
393
387
|
end
|
|
394
388
|
|
|
395
389
|
# Errors collected per analyzed import path during collecting-mode checks.
|
|
@@ -543,6 +537,8 @@ module MilkTea
|
|
|
543
537
|
types[decl.name] = Types::Flags.new(decl.name, module_name:)
|
|
544
538
|
when AST::OpaqueDecl
|
|
545
539
|
types[decl.name] = Types::Opaque.new(decl.name, module_name:, external: false)
|
|
540
|
+
when AST::UnionDecl
|
|
541
|
+
types[decl.name] = Types::Union.new(decl.name, module_name:)
|
|
546
542
|
end
|
|
547
543
|
end
|
|
548
544
|
|
|
@@ -119,10 +119,7 @@ module MilkTea
|
|
|
119
119
|
return @package_manifest_cache[manifest_path] if @package_manifest_cache.key?(manifest_path)
|
|
120
120
|
|
|
121
121
|
@package_manifest_cache[manifest_path] = PackageManifest.load(path)
|
|
122
|
-
|
|
123
|
-
@package_manifest_cache[manifest_path] = nil if manifest_path
|
|
124
|
-
nil
|
|
125
|
-
end
|
|
122
|
+
end
|
|
126
123
|
|
|
127
124
|
def package_namespace_match?(module_name, package_name)
|
|
128
125
|
module_name == package_name || module_name.start_with?("#{package_name}.")
|
|
@@ -18,6 +18,39 @@ module MilkTea
|
|
|
18
18
|
AST::Import.new(path:, alias_name:, line:, column: local_column, length: local_name.length)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
DECL_KIND = {
|
|
22
|
+
var: { method: :parse_var_decl, reject_attrs: "var" },
|
|
23
|
+
type: { method: :parse_type_alias_decl, reject_attrs: "type" },
|
|
24
|
+
union: { method: :parse_union_decl },
|
|
25
|
+
enum: { method: :parse_enum_decl, enum_class: AST::EnumDecl },
|
|
26
|
+
flags: { method: :parse_enum_decl, enum_class: AST::FlagsDecl },
|
|
27
|
+
opaque: { method: :parse_opaque_decl, reject_attrs: "opaque" },
|
|
28
|
+
interface: { method: :parse_interface_decl, reject_attrs: "interface" },
|
|
29
|
+
event: { method: :parse_event_decl },
|
|
30
|
+
attribute: { method: :parse_attribute_decl, reject_attrs: "attribute" },
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
def dispatch_decl_kind(kind, visibility:, attributes:)
|
|
34
|
+
info = DECL_KIND.fetch(kind)
|
|
35
|
+
reject_attributes!(attributes, info[:reject_attrs]) if info[:reject_attrs]
|
|
36
|
+
|
|
37
|
+
if info[:enum_class]
|
|
38
|
+
send(info[:method], info[:enum_class], visibility:, attributes:)
|
|
39
|
+
elsif info[:reject_attrs]
|
|
40
|
+
send(info[:method], visibility:)
|
|
41
|
+
else
|
|
42
|
+
send(info[:method], visibility:, attributes:)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse_const_or_function_decl(visibility:, attributes:)
|
|
47
|
+
if match(:function)
|
|
48
|
+
parse_function_def(visibility:, const: true, attributes:)
|
|
49
|
+
else
|
|
50
|
+
parse_const_decl(visibility:, attributes:)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
21
54
|
def parse_declaration
|
|
22
55
|
attributes = parse_attribute_applications
|
|
23
56
|
visibility, visibility_token = parse_visibility
|
|
@@ -25,38 +58,29 @@ module MilkTea
|
|
|
25
58
|
if builtin_attribute_identifier?(peek)
|
|
26
59
|
raise error(peek, "layout modifiers must use attributes like @[packed] or @[align(...)]")
|
|
27
60
|
elsif match(:attribute)
|
|
28
|
-
|
|
29
|
-
parse_attribute_decl(visibility:)
|
|
61
|
+
dispatch_decl_kind(:attribute, visibility:, attributes:)
|
|
30
62
|
elsif match(:const)
|
|
31
|
-
|
|
32
|
-
parse_function_def(visibility:, const: true, attributes:)
|
|
33
|
-
else
|
|
34
|
-
parse_const_decl(visibility:, attributes:)
|
|
35
|
-
end
|
|
63
|
+
parse_const_or_function_decl(visibility:, attributes:)
|
|
36
64
|
elsif match(:var)
|
|
37
|
-
|
|
38
|
-
parse_var_decl(visibility:)
|
|
65
|
+
dispatch_decl_kind(:var, visibility:, attributes:)
|
|
39
66
|
elsif match(:event)
|
|
40
|
-
|
|
67
|
+
dispatch_decl_kind(:event, visibility:, attributes:)
|
|
41
68
|
elsif match(:type)
|
|
42
|
-
|
|
43
|
-
parse_type_alias_decl(visibility:)
|
|
69
|
+
dispatch_decl_kind(:type, visibility:, attributes:)
|
|
44
70
|
elsif match(:struct)
|
|
45
71
|
parse_struct_decl(visibility:, attributes:)
|
|
46
72
|
elsif match(:union)
|
|
47
|
-
|
|
73
|
+
dispatch_decl_kind(:union, visibility:, attributes:)
|
|
48
74
|
elsif match(:enum)
|
|
49
|
-
|
|
75
|
+
dispatch_decl_kind(:enum, visibility:, attributes:)
|
|
50
76
|
elsif match(:flags)
|
|
51
|
-
|
|
77
|
+
dispatch_decl_kind(:flags, visibility:, attributes:)
|
|
52
78
|
elsif match(:variant)
|
|
53
79
|
parse_variant_decl(visibility:, attributes:)
|
|
54
80
|
elsif match(:interface)
|
|
55
|
-
|
|
56
|
-
parse_interface_decl(visibility:)
|
|
81
|
+
dispatch_decl_kind(:interface, visibility:, attributes:)
|
|
57
82
|
elsif match(:opaque)
|
|
58
|
-
|
|
59
|
-
parse_opaque_decl(visibility:)
|
|
83
|
+
dispatch_decl_kind(:opaque, visibility:, attributes:)
|
|
60
84
|
elsif match(:extending)
|
|
61
85
|
reject_attributes!(attributes, "extending")
|
|
62
86
|
raise error(visibility_token, "public is not allowed on extending blocks") if visibility == :public
|
|
@@ -35,14 +35,14 @@ module MilkTea
|
|
|
35
35
|
consume(:newline, "expected newline after 'if condition:' in if expression")
|
|
36
36
|
consume(:indent, "expected indented body in if expression")
|
|
37
37
|
then_expression = parse_expression
|
|
38
|
-
|
|
38
|
+
finish_expression_statement(then_expression)
|
|
39
39
|
consume(:dedent, "expected end of if expression body")
|
|
40
40
|
consume(:else, "expected 'else' in if expression")
|
|
41
41
|
consume(:colon, "expected ':' after 'else' in if expression")
|
|
42
42
|
consume(:newline, "expected newline after 'else:' in if expression")
|
|
43
43
|
consume(:indent, "expected indented else body in if expression")
|
|
44
44
|
else_expression = parse_expression
|
|
45
|
-
|
|
45
|
+
finish_expression_statement(else_expression)
|
|
46
46
|
consume(:dedent, "expected end of else expression body")
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -97,7 +97,7 @@ module MilkTea
|
|
|
97
97
|
end
|
|
98
98
|
consume(:colon, "expected ':' after match expression arm pattern")
|
|
99
99
|
value = parse_expression
|
|
100
|
-
|
|
100
|
+
finish_expression_statement(value)
|
|
101
101
|
patterns.map do |pattern|
|
|
102
102
|
AST::MatchExprArm.new(
|
|
103
103
|
pattern:,
|
|
@@ -549,10 +549,7 @@ module MilkTea
|
|
|
549
549
|
end
|
|
550
550
|
|
|
551
551
|
parser = self.class.new(tokens, path: @path)
|
|
552
|
-
parser.
|
|
553
|
-
parser.instance_variable_set(:@known_import_aliases, @known_import_aliases.dup)
|
|
554
|
-
parser.instance_variable_set(:@known_generic_callable_names, @known_generic_callable_names.dup)
|
|
555
|
-
parser.instance_variable_set(:@current_type_param_names, @current_type_param_names.dup)
|
|
552
|
+
parser.apply_parser_context(parser_context)
|
|
556
553
|
|
|
557
554
|
expression = parser.parse_expression
|
|
558
555
|
parser.skip_newlines
|
|
@@ -147,7 +147,7 @@ module MilkTea
|
|
|
147
147
|
|
|
148
148
|
else_body = parse_block
|
|
149
149
|
else
|
|
150
|
-
|
|
150
|
+
finish_expression_statement(value)
|
|
151
151
|
end
|
|
152
152
|
else
|
|
153
153
|
raise error(name_token, "local declaration without initializer requires a type") unless var_type
|
|
@@ -366,7 +366,7 @@ module MilkTea
|
|
|
366
366
|
if match_arm_expr_form?
|
|
367
367
|
consume(:colon, "expected ':' after match expression arm pattern")
|
|
368
368
|
value = parse_expression
|
|
369
|
-
|
|
369
|
+
finish_expression_statement(value)
|
|
370
370
|
patterns.map do |pattern|
|
|
371
371
|
AST::MatchExprArm.new(
|
|
372
372
|
pattern:,
|
|
@@ -585,7 +585,7 @@ module MilkTea
|
|
|
585
585
|
token = previous
|
|
586
586
|
line = token.line
|
|
587
587
|
value = check(:newline) ? nil : parse_expression
|
|
588
|
-
|
|
588
|
+
finish_expression_statement(value)
|
|
589
589
|
AST::ReturnStmt.new(value:, line:, column: token.column, length: token.lexeme.length)
|
|
590
590
|
end
|
|
591
591
|
|
|
@@ -768,10 +768,10 @@ module MilkTea
|
|
|
768
768
|
operator = previous.lexeme
|
|
769
769
|
column = previous.column
|
|
770
770
|
value = parse_expression
|
|
771
|
-
|
|
771
|
+
finish_expression_statement(value)
|
|
772
772
|
AST::Assignment.new(target: expression, operator:, value:, line:, column:)
|
|
773
773
|
else
|
|
774
|
-
|
|
774
|
+
finish_expression_statement(expression)
|
|
775
775
|
AST::ExpressionStmt.new(expression:, line:)
|
|
776
776
|
end
|
|
777
777
|
end
|
data/lib/milk_tea/core/parser.rb
CHANGED
|
@@ -38,6 +38,8 @@ module MilkTea
|
|
|
38
38
|
def initialize(ast:, errors: []) = super
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
ParseContext = Data.define(:known_type_names, :known_import_aliases, :known_generic_callable_names, :current_type_param_names)
|
|
42
|
+
|
|
41
43
|
include Parse::Blocks
|
|
42
44
|
include Parse::Recovery
|
|
43
45
|
include Parse::Types
|
|
@@ -250,6 +252,10 @@ module MilkTea
|
|
|
250
252
|
consume(:newline, "expected end of statement")
|
|
251
253
|
end
|
|
252
254
|
|
|
255
|
+
def finish_expression_statement(value)
|
|
256
|
+
consume_end_of_statement unless block_expression?(value)
|
|
257
|
+
end
|
|
258
|
+
|
|
253
259
|
def block_expression?(expression)
|
|
254
260
|
expression.is_a?(AST::ProcExpr) || expression.is_a?(AST::MatchExpr) || expression.is_a?(AST::IfExpr)
|
|
255
261
|
end
|
|
@@ -304,6 +310,26 @@ module MilkTea
|
|
|
304
310
|
@current_type_param_names = saved_names
|
|
305
311
|
end
|
|
306
312
|
|
|
313
|
+
# Returns a ParseContext snapshot of the parser state that must propagate
|
|
314
|
+
# to nested parsers (e.g. format string interpolations). When adding
|
|
315
|
+
# parser state that must be cloned, update both this method,
|
|
316
|
+
# apply_parser_context, and the ParseContext Data class.
|
|
317
|
+
def parser_context
|
|
318
|
+
ParseContext.new(
|
|
319
|
+
known_type_names: @known_type_names.dup,
|
|
320
|
+
known_import_aliases: @known_import_aliases.dup,
|
|
321
|
+
known_generic_callable_names: @known_generic_callable_names.dup,
|
|
322
|
+
current_type_param_names: @current_type_param_names.dup,
|
|
323
|
+
)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def apply_parser_context(context)
|
|
327
|
+
@known_type_names = context.known_type_names
|
|
328
|
+
@known_import_aliases = context.known_import_aliases
|
|
329
|
+
@known_generic_callable_names = context.known_generic_callable_names
|
|
330
|
+
@current_type_param_names = context.current_type_param_names
|
|
331
|
+
end
|
|
332
|
+
|
|
307
333
|
def seed_known_names
|
|
308
334
|
MilkTea::BUILTIN_TYPE_NAMES.each { |name| @known_type_names[name] = true }
|
|
309
335
|
|