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.
- checksums.yaml +4 -4
- data/docs/language-design.md +3 -4
- data/docs/language-manual.md +3 -3
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/{binding_types.rb → bindings.rb} +1 -1
- data/lib/milk_tea/core/c_backend/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 +63 -54
- data/lib/milk_tea/core/compile_time.rb +279 -74
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
- data/lib/milk_tea/core/{compatibility_helpers.rb → intrinsics.rb} +9 -2
- data/lib/milk_tea/core/lexer.rb +55 -48
- 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/lowering.rb +18 -18
- data/lib/milk_tea/core/module_binder.rb +9 -10
- data/lib/milk_tea/core/module_loader.rb +64 -57
- 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 +43 -17
- 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 +57 -38
- data/lib/milk_tea/core.rb +6 -2
- data/lib/milk_tea/dap/lldb_dap_backend.rb +153 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +1 -1
- data/lib/milk_tea/dap.rb +1 -1
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +6 -0
- data/lib/milk_tea/lsp.rb +1 -7
- data/lib/milk_tea/{bindings/cli.rb → tooling/bindgen_cli.rb} +1 -1
- data/lib/milk_tea/tooling/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
- data/lib/milk_tea/tooling.rb +1 -1
- metadata +29 -9
- data/lib/milk_tea/core/compile_time/const_eval.rb +0 -182
- data/lib/milk_tea/core/module_loader/errors.rb +0 -23
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -156
- /data/lib/milk_tea/core/lowering/{async.rb → async/frame_builder.rb} +0 -0
|
@@ -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?
|
|
@@ -31,7 +31,7 @@ require_relative "lowering/functions"
|
|
|
31
31
|
require_relative "lowering/async/analysis"
|
|
32
32
|
require_relative "lowering/async/normalization"
|
|
33
33
|
require_relative "lowering/async/lowering"
|
|
34
|
-
require_relative "lowering/async"
|
|
34
|
+
require_relative "lowering/async/frame_builder"
|
|
35
35
|
require_relative "lowering/block"
|
|
36
36
|
require_relative "lowering/proc"
|
|
37
37
|
require_relative "lowering/loops"
|
|
@@ -85,11 +85,27 @@ module MilkTea
|
|
|
85
85
|
OrderResolution = Data.define(:target_type, :binding, :callee_name)
|
|
86
86
|
|
|
87
87
|
class Lowerer
|
|
88
|
-
include
|
|
88
|
+
include Intrinsics
|
|
89
89
|
|
|
90
90
|
attr_accessor :bypass_sema_type_cache
|
|
91
91
|
attr_reader :recorded_expr_types
|
|
92
92
|
|
|
93
|
+
include LowererScans
|
|
94
|
+
include LowererDeclarations
|
|
95
|
+
include LowererEvents
|
|
96
|
+
include LowererFunctions
|
|
97
|
+
include LowererAsync
|
|
98
|
+
include LowererBlock
|
|
99
|
+
include LowererProc
|
|
100
|
+
include LowererLoops
|
|
101
|
+
include LowererExpressions
|
|
102
|
+
include LowererCalls
|
|
103
|
+
include LowererForeignCstr
|
|
104
|
+
include LowererStrBuffer
|
|
105
|
+
include LowererResolve
|
|
106
|
+
include LowererDyn
|
|
107
|
+
include LowererUtils
|
|
108
|
+
|
|
93
109
|
def initialize(program)
|
|
94
110
|
@program = program
|
|
95
111
|
@ctx = ModuleContext.new
|
|
@@ -358,21 +374,5 @@ module MilkTea
|
|
|
358
374
|
ordered.concat(modules.values - ordered)
|
|
359
375
|
ordered
|
|
360
376
|
end
|
|
361
|
-
|
|
362
|
-
include LowererScans
|
|
363
|
-
include LowererDeclarations
|
|
364
|
-
include LowererEvents
|
|
365
|
-
include LowererFunctions
|
|
366
|
-
include LowererAsync
|
|
367
|
-
include LowererBlock
|
|
368
|
-
include LowererProc
|
|
369
|
-
include LowererLoops
|
|
370
|
-
include LowererExpressions
|
|
371
|
-
include LowererCalls
|
|
372
|
-
include LowererForeignCstr
|
|
373
|
-
include LowererStrBuffer
|
|
374
|
-
include LowererResolve
|
|
375
|
-
include LowererDyn
|
|
376
|
-
include LowererUtils
|
|
377
377
|
end
|
|
378
378
|
end
|
|
@@ -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)
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "module_loader/errors"
|
|
4
|
-
require_relative "module_path_resolver"
|
|
5
|
-
require_relative "module_binder"
|
|
6
|
-
require_relative "async_runtime_installer"
|
|
7
|
-
require_relative "prelude_installer"
|
|
8
|
-
|
|
9
3
|
module MilkTea
|
|
4
|
+
class ModuleLoadError < StandardError
|
|
5
|
+
attr_reader :path, :line, :column
|
|
6
|
+
|
|
7
|
+
def initialize(message, path:, line: nil, column: nil)
|
|
8
|
+
@path = path
|
|
9
|
+
@line = line
|
|
10
|
+
@column = column
|
|
11
|
+
super("#{message}: #{path}")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def code
|
|
15
|
+
"module/error"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
10
19
|
class ModuleLoader
|
|
20
|
+
ImportResolution = Data.define(:modules, :errors)
|
|
21
|
+
ImportResolutionError = Data.define(:import, :error)
|
|
11
22
|
Program = Data.define(:root_path, :root_analysis, :analyses_by_path, :analyses_by_module_name)
|
|
12
23
|
PLATFORM_SUFFIXES = {
|
|
13
24
|
"linux" => :linux,
|
|
@@ -90,6 +101,15 @@ module MilkTea
|
|
|
90
101
|
MilkTea.host_platform
|
|
91
102
|
end
|
|
92
103
|
|
|
104
|
+
def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
|
|
105
|
+
if error_class == ModuleLoadError
|
|
106
|
+
raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
|
|
110
|
+
raise(error_class || ArgumentError, message)
|
|
111
|
+
end
|
|
112
|
+
|
|
93
113
|
def initialize(module_roots: [MilkTea.root], package_graph: nil, shared_cache: nil, source_overrides: nil, platform: nil)
|
|
94
114
|
@module_roots = module_roots.map { |root| File.expand_path(root.to_s) }
|
|
95
115
|
@ast_cache = {}
|
|
@@ -297,48 +317,14 @@ module MilkTea
|
|
|
297
317
|
end
|
|
298
318
|
|
|
299
319
|
def imported_modules_for_ast(ast, importer_path: nil)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
ast.imports.each do |import|
|
|
303
|
-
import_path = @path_resolver.resolve_module_path(import.path.to_s, importer_path:, importer_module_name: ast.module_name.to_s)
|
|
304
|
-
|
|
305
|
-
if @forward_bindings.key?(import_path)
|
|
306
|
-
modules[import.path.to_s] = @forward_bindings[import_path]
|
|
307
|
-
else
|
|
308
|
-
import_analysis = check_path(import_path)
|
|
309
|
-
modules[import.path.to_s] = @binder.module_binding(import_analysis)
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
@async_runtime_installer.install_async_runtime_dependency!(ast, modules, importer_path:, collecting_errors: false)
|
|
314
|
-
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors: false)
|
|
315
|
-
modules.freeze
|
|
320
|
+
resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
316
321
|
end
|
|
317
322
|
|
|
318
|
-
def
|
|
319
|
-
|
|
320
|
-
current_imports = ast.imports.map { |import| import.path.to_s }.to_set
|
|
321
|
-
|
|
322
|
-
@analysis_cache.each_value do |analysis|
|
|
323
|
-
next unless analysis
|
|
324
|
-
next unless analysis.module_name
|
|
325
|
-
|
|
326
|
-
mod_name = analysis.module_name.to_s
|
|
327
|
-
next if current_imports.include?(mod_name)
|
|
328
|
-
next if mod_name == ast.module_name.to_s
|
|
329
|
-
|
|
330
|
-
types = analysis.respond_to?(:types) ? analysis.types : {}
|
|
331
|
-
types.each_key do |type_name|
|
|
332
|
-
type_str = type_name.to_s
|
|
333
|
-
index[type_str] ||= []
|
|
334
|
-
index[type_str] << mod_name unless index[type_str].include?(mod_name)
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
index
|
|
323
|
+
def imported_modules_for_ast_collecting_errors(ast, importer_path: nil)
|
|
324
|
+
resolve_imports_for_ast(ast, importer_path:, collecting: true)
|
|
339
325
|
end
|
|
340
326
|
|
|
341
|
-
def
|
|
327
|
+
def resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
342
328
|
modules = {}
|
|
343
329
|
errors = []
|
|
344
330
|
|
|
@@ -349,27 +335,55 @@ module MilkTea
|
|
|
349
335
|
if @forward_bindings.key?(import_path)
|
|
350
336
|
modules[import.path.to_s] = @forward_bindings[import_path]
|
|
351
337
|
else
|
|
352
|
-
import_analysis = check_path_collecting_errors(import_path)
|
|
338
|
+
import_analysis = collecting ? check_path_collecting_errors(import_path) : check_path(import_path)
|
|
353
339
|
modules[import.path.to_s] = @binder.module_binding(import_analysis)
|
|
354
340
|
end
|
|
355
341
|
rescue ModuleLoadError, PackageLockError, SemanticError => e
|
|
342
|
+
raise unless collecting
|
|
356
343
|
errors << ImportResolutionError.new(import:, error: e)
|
|
357
344
|
end
|
|
358
345
|
end
|
|
359
346
|
|
|
360
347
|
begin
|
|
361
|
-
@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)
|
|
362
349
|
rescue ModuleLoadError, PackageLockError => e
|
|
350
|
+
raise unless collecting
|
|
363
351
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
364
352
|
end
|
|
365
353
|
|
|
366
354
|
begin
|
|
367
|
-
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors:
|
|
355
|
+
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors: collecting)
|
|
368
356
|
rescue ModuleLoadError, PackageLockError => e
|
|
357
|
+
raise unless collecting
|
|
369
358
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
370
359
|
end
|
|
371
360
|
|
|
372
|
-
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
|
|
373
387
|
end
|
|
374
388
|
|
|
375
389
|
# Errors collected per analyzed import path during collecting-mode checks.
|
|
@@ -380,15 +394,6 @@ module MilkTea
|
|
|
380
394
|
@collecting_path_errors
|
|
381
395
|
end
|
|
382
396
|
|
|
383
|
-
def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
|
|
384
|
-
if error_class == ModuleLoadError
|
|
385
|
-
raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
|
|
389
|
-
raise(error_class || ArgumentError, message)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
397
|
def check_path(path)
|
|
393
398
|
resolved_path, ast, cached = check_module_cache(path)
|
|
394
399
|
return cached if cached
|
|
@@ -532,6 +537,8 @@ module MilkTea
|
|
|
532
537
|
types[decl.name] = Types::Flags.new(decl.name, module_name:)
|
|
533
538
|
when AST::OpaqueDecl
|
|
534
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:)
|
|
535
542
|
end
|
|
536
543
|
end
|
|
537
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
|
|
@@ -155,6 +157,23 @@ module MilkTea
|
|
|
155
157
|
next_token.nil? || %i[newline eof].include?(next_token.type)
|
|
156
158
|
end
|
|
157
159
|
|
|
160
|
+
def advance
|
|
161
|
+
@current += 1 unless eof?
|
|
162
|
+
previous
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def eof?
|
|
166
|
+
peek.type == :eof
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def peek
|
|
170
|
+
@tokens[@current]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def previous
|
|
174
|
+
@tokens[@current - 1]
|
|
175
|
+
end
|
|
176
|
+
|
|
158
177
|
def match(*types)
|
|
159
178
|
return false unless types.any? { |type| check(type) }
|
|
160
179
|
|
|
@@ -219,23 +238,6 @@ module MilkTea
|
|
|
219
238
|
true
|
|
220
239
|
end
|
|
221
240
|
|
|
222
|
-
def advance
|
|
223
|
-
@current += 1 unless eof?
|
|
224
|
-
previous
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def eof?
|
|
228
|
-
peek.type == :eof
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def peek
|
|
232
|
-
@tokens[@current]
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def previous
|
|
236
|
-
@tokens[@current - 1]
|
|
237
|
-
end
|
|
238
|
-
|
|
239
241
|
def error(token, message)
|
|
240
242
|
ParseError.new(message, token:, path: @path)
|
|
241
243
|
end
|
|
@@ -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
|
|