mt-lang 0.2.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 +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module Parse
|
|
5
|
+
module Types
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def parse_params(allow_variadic: false)
|
|
9
|
+
parse_parameter_list(allow_variadic:) { parse_param }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def parse_signature_params
|
|
13
|
+
parse_parameter_list { parse_param }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def parse_foreign_params(allow_variadic: false)
|
|
17
|
+
parse_parameter_list(allow_variadic:) { parse_foreign_param }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_parameter_list(allow_variadic: false)
|
|
21
|
+
consume(:lparen, "expected '('")
|
|
22
|
+
params = []
|
|
23
|
+
variadic = false
|
|
24
|
+
|
|
25
|
+
unless check(:rparen)
|
|
26
|
+
loop do
|
|
27
|
+
if allow_variadic && match(:ellipsis)
|
|
28
|
+
variadic = true
|
|
29
|
+
break
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
params << yield
|
|
33
|
+
break unless match(:comma)
|
|
34
|
+
break if check(:rparen)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
consume(:rparen, "expected ')' after parameters")
|
|
39
|
+
return [params, variadic] if allow_variadic
|
|
40
|
+
|
|
41
|
+
params
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def parse_param
|
|
45
|
+
name_token = consume_name("expected parameter name")
|
|
46
|
+
raise error(name_token, "expected ':' and parameter type") unless match(:colon)
|
|
47
|
+
|
|
48
|
+
param_type = parse_type_ref
|
|
49
|
+
|
|
50
|
+
AST::Param.new(name: name_token.lexeme, type: param_type, line: name_token.line, column: name_token.column)
|
|
51
|
+
rescue ParseError => e
|
|
52
|
+
raise unless @recovery_errors
|
|
53
|
+
|
|
54
|
+
@recovery_errors << e
|
|
55
|
+
param_name = name_token&.lexeme || "error"
|
|
56
|
+
error_type = recovery_error_expr(e)
|
|
57
|
+
AST::Param.new(name: param_name, type: error_type, line: name_token&.line || 1, column: name_token&.column || 1)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_foreign_param
|
|
61
|
+
mode = if foreign_param_qualifier_mode?
|
|
62
|
+
advance.type
|
|
63
|
+
else
|
|
64
|
+
:plain
|
|
65
|
+
end
|
|
66
|
+
name_token = consume_name("expected parameter name")
|
|
67
|
+
raise error(name_token, "expected ':' and parameter type") unless match(:colon)
|
|
68
|
+
|
|
69
|
+
param_type = parse_type_ref
|
|
70
|
+
boundary_type = match(:as) ? parse_type_ref : nil
|
|
71
|
+
|
|
72
|
+
AST::ForeignParam.new(name: name_token.lexeme, type: param_type, mode:, boundary_type:)
|
|
73
|
+
rescue ParseError => e
|
|
74
|
+
raise unless @recovery_errors
|
|
75
|
+
|
|
76
|
+
@recovery_errors << e
|
|
77
|
+
param_name = name_token&.lexeme || "error"
|
|
78
|
+
error_type = recovery_error_expr(e)
|
|
79
|
+
AST::ForeignParam.new(name: param_name, type: error_type, mode: mode || :plain, boundary_type: nil)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def parse_type_ref
|
|
83
|
+
return parse_function_type_ref if match(:fn)
|
|
84
|
+
return parse_proc_type_ref if match(:proc)
|
|
85
|
+
return parse_dyn_type_ref if match(:dyn)
|
|
86
|
+
return parse_tuple_type_ref if check(:lparen)
|
|
87
|
+
|
|
88
|
+
first_token = peek
|
|
89
|
+
if match(:at)
|
|
90
|
+
lt_token = consume_name("expected lifetime name after @")
|
|
91
|
+
return AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["@#{lt_token.lexeme}"]), arguments: [], nullable: false, lifetime: nil, line: first_token.line, column: first_token.column, length: lt_token.lexeme.length + 1)
|
|
92
|
+
end
|
|
93
|
+
name = parse_qualified_name
|
|
94
|
+
arguments = []
|
|
95
|
+
lifetime = nil
|
|
96
|
+
if match(:lbracket)
|
|
97
|
+
if check(:at) && name.to_s == "ref"
|
|
98
|
+
match(:at)
|
|
99
|
+
lt_token = consume_name("expected lifetime name after @")
|
|
100
|
+
lifetime = "@#{lt_token.lexeme}"
|
|
101
|
+
consume(:comma, "expected ',' after lifetime in type arguments")
|
|
102
|
+
end
|
|
103
|
+
arguments = parse_comma_separated_until(:rbracket) do
|
|
104
|
+
AST::TypeArgument.new(value: parse_type_argument)
|
|
105
|
+
end
|
|
106
|
+
consume(:rbracket, "expected ']' after type arguments")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
nullable = match(:question)
|
|
110
|
+
type_name = name.to_s
|
|
111
|
+
length = type_name.length + (nullable ? 1 : 0)
|
|
112
|
+
AST::TypeRef.new(name:, arguments:, nullable:, lifetime:, line: first_token.line, column: first_token.column, length:)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def parse_function_type_ref
|
|
116
|
+
parse_callable_type_ref(keyword: "fn", param_context: "function type parameters") do |params, return_type|
|
|
117
|
+
AST::FunctionType.new(params:, return_type:)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def parse_proc_type_ref
|
|
122
|
+
parse_callable_type_ref(keyword: "proc", param_context: "proc type parameters") do |params, return_type|
|
|
123
|
+
AST::ProcType.new(params:, return_type:)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def parse_dyn_type_ref
|
|
128
|
+
first_token = previous
|
|
129
|
+
consume(:lbracket, "expected '[' after dyn")
|
|
130
|
+
interface = parse_qualified_name_with_type_arguments
|
|
131
|
+
consume(:rbracket, "expected ']' after interface name")
|
|
132
|
+
nullable = match(:question)
|
|
133
|
+
AST::DynType.new(interface:, nullable:, line: first_token.line, column: first_token.column, length: 3)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def parse_tuple_type_ref
|
|
137
|
+
match(:lparen)
|
|
138
|
+
first_type = parse_type_ref
|
|
139
|
+
if match(:comma)
|
|
140
|
+
element_types = [first_type]
|
|
141
|
+
loop do
|
|
142
|
+
element_types << parse_type_ref
|
|
143
|
+
break unless match(:comma)
|
|
144
|
+
end
|
|
145
|
+
consume(:rparen, "expected ')' after tuple type elements")
|
|
146
|
+
nullable = match(:question)
|
|
147
|
+
AST::TupleType.new(element_types:, nullable:)
|
|
148
|
+
else
|
|
149
|
+
consume(:rparen, "expected ')' after type")
|
|
150
|
+
first_type
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def parse_callable_type_ref(keyword:, param_context:)
|
|
155
|
+
consume(:lparen, "expected '(' after #{keyword}")
|
|
156
|
+
params = parse_comma_separated_until(:rparen) { parse_function_type_param }
|
|
157
|
+
|
|
158
|
+
consume(:rparen, "expected ')' after #{param_context}")
|
|
159
|
+
consume(:arrow, "expected '->' after #{param_context}")
|
|
160
|
+
return_type = parse_type_ref
|
|
161
|
+
yield params, return_type
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def parse_function_type_param
|
|
165
|
+
name_token = consume_name("expected function type parameter name")
|
|
166
|
+
name = name_token.lexeme
|
|
167
|
+
consume(:colon, "expected ':' after function type parameter name")
|
|
168
|
+
type = parse_type_ref
|
|
169
|
+
AST::Param.new(name:, type:, line: name_token.line, column: name_token.column)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def parse_type_argument
|
|
173
|
+
if match(:integer)
|
|
174
|
+
token = previous
|
|
175
|
+
AST::IntegerLiteral.new(lexeme: token.lexeme, value: token.literal)
|
|
176
|
+
elsif match(:float)
|
|
177
|
+
token = previous
|
|
178
|
+
AST::FloatLiteral.new(lexeme: token.lexeme, value: token.literal)
|
|
179
|
+
else
|
|
180
|
+
parse_type_ref
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def parse_declaration_type_params
|
|
185
|
+
return [] unless match(:lbracket)
|
|
186
|
+
|
|
187
|
+
params = parse_comma_separated_until(:rbracket) do
|
|
188
|
+
name_token = consume_name("expected type parameter name")
|
|
189
|
+
name = name_token.lexeme
|
|
190
|
+
if match(:colon)
|
|
191
|
+
value_type = parse_type_ref
|
|
192
|
+
AST::ValueTypeParam.new(
|
|
193
|
+
name:,
|
|
194
|
+
type: value_type,
|
|
195
|
+
line: name_token.line,
|
|
196
|
+
column: name_token.column,
|
|
197
|
+
length: name.length,
|
|
198
|
+
)
|
|
199
|
+
else
|
|
200
|
+
constraints = parse_type_param_constraints
|
|
201
|
+
AST::TypeParam.new(
|
|
202
|
+
name:,
|
|
203
|
+
constraints:,
|
|
204
|
+
line: name_token.line,
|
|
205
|
+
column: name_token.column,
|
|
206
|
+
length: name.length,
|
|
207
|
+
)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
consume(:rbracket, "expected ']' after type parameters")
|
|
212
|
+
params
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def parse_type_param_constraints
|
|
216
|
+
constraints = []
|
|
217
|
+
interface_constraint_mode = false
|
|
218
|
+
|
|
219
|
+
constraint, interface_constraint_mode = parse_type_param_constraint(interface_constraint_mode)
|
|
220
|
+
return constraints unless constraint
|
|
221
|
+
|
|
222
|
+
constraints << constraint
|
|
223
|
+
|
|
224
|
+
while match(:and)
|
|
225
|
+
constraint, interface_constraint_mode = parse_type_param_constraint(interface_constraint_mode)
|
|
226
|
+
raise error(peek, "expected constraint after 'and'") unless constraint
|
|
227
|
+
|
|
228
|
+
constraints << constraint
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
constraints
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def parse_type_param_constraint(interface_constraint_mode)
|
|
235
|
+
if match(:implements)
|
|
236
|
+
return [AST::TypeParamConstraint.new(kind: :interface, interface_ref: parse_qualified_name_with_type_arguments), true]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
return [AST::TypeParamConstraint.new(kind: :interface, interface_ref: parse_qualified_name_with_type_arguments), true] if interface_constraint_mode
|
|
240
|
+
|
|
241
|
+
[nil, interface_constraint_mode]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def parse_implements_clause
|
|
245
|
+
return [] unless match(:implements)
|
|
246
|
+
|
|
247
|
+
implements = [parse_qualified_name_with_type_arguments]
|
|
248
|
+
implements << parse_qualified_name_with_type_arguments while match(:comma)
|
|
249
|
+
implements
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def parse_qualified_name_with_type_arguments
|
|
253
|
+
first_token = consume_path_component("expected identifier")
|
|
254
|
+
parts = [first_token.lexeme]
|
|
255
|
+
while match(:dot)
|
|
256
|
+
parts << consume_path_component("expected identifier after '.'").lexeme
|
|
257
|
+
end
|
|
258
|
+
type_arguments = if match(:lbracket)
|
|
259
|
+
args = parse_comma_separated_until(:rbracket) do
|
|
260
|
+
parse_type_ref
|
|
261
|
+
end
|
|
262
|
+
consume(:rbracket, "expected ']' after type arguments")
|
|
263
|
+
args
|
|
264
|
+
else
|
|
265
|
+
[]
|
|
266
|
+
end
|
|
267
|
+
AST::QualifiedName.new(parts:, type_arguments:, line: first_token.line, column: first_token.column)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "parser/blocks"
|
|
4
|
+
require_relative "parser/recovery"
|
|
5
|
+
require_relative "parser/types"
|
|
6
|
+
require_relative "parser/attributes"
|
|
7
|
+
require_relative "parser/expressions"
|
|
8
|
+
require_relative "parser/declarations"
|
|
9
|
+
require_relative "parser/statements"
|
|
10
|
+
|
|
11
|
+
module MilkTea
|
|
12
|
+
class ParseError < StandardError
|
|
13
|
+
attr_reader :token, :path
|
|
14
|
+
|
|
15
|
+
def initialize(message, token:, path: nil)
|
|
16
|
+
@token = token
|
|
17
|
+
@path = path
|
|
18
|
+
|
|
19
|
+
location = [path, "#{token.line}:#{token.column}"].compact.join(":")
|
|
20
|
+
super(location.empty? ? message : "#{message} at #{location}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def line
|
|
24
|
+
@token.line
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def column
|
|
28
|
+
@token.column
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def code
|
|
32
|
+
"parse/error"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Parser
|
|
37
|
+
ParseRecoveryResult = Data.define(:ast, :errors) do
|
|
38
|
+
def initialize(ast:, errors: []) = super
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
include Parse::Blocks
|
|
42
|
+
include Parse::Recovery
|
|
43
|
+
include Parse::Types
|
|
44
|
+
include Parse::Attributes
|
|
45
|
+
include Parse::Expressions
|
|
46
|
+
include Parse::Declarations
|
|
47
|
+
include Parse::Statements
|
|
48
|
+
|
|
49
|
+
def self.parse(source = nil, path: nil, tokens: nil)
|
|
50
|
+
token_stream = tokens || Lexer.lex(source, path: path)
|
|
51
|
+
new(token_stream, path: path).parse
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.parse_collecting_errors(source = nil, path: nil, tokens: nil)
|
|
55
|
+
if tokens
|
|
56
|
+
return new(tokens, path: path).parse_collecting_errors
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
lex_errors = []
|
|
60
|
+
token_stream = Lexer.lex(source, path: path, recovery_errors: lex_errors)
|
|
61
|
+
parse_result = new(token_stream, path: path).parse_collecting_errors
|
|
62
|
+
ParseRecoveryResult.new(ast: parse_result.ast, errors: lex_errors + parse_result.errors)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def initialize(tokens, path: nil)
|
|
66
|
+
@tokens = tokens.is_a?(SyntaxTokenStream) ? tokens : SyntaxTokenStream.new(tokens)
|
|
67
|
+
@path = path
|
|
68
|
+
@current = 0
|
|
69
|
+
@known_type_names = {}
|
|
70
|
+
@known_import_aliases = {}
|
|
71
|
+
@known_generic_callable_names = {}
|
|
72
|
+
@current_type_param_names = []
|
|
73
|
+
@in_inline_block_body = false
|
|
74
|
+
seed_known_names
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse
|
|
78
|
+
parse_source_file
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def parse_collecting_errors
|
|
82
|
+
errors = @recovery_errors ? @recovery_errors.dup : []
|
|
83
|
+
previous_recovery_errors = @recovery_errors
|
|
84
|
+
@recovery_errors = errors
|
|
85
|
+
ast = parse_source_file(errors:)
|
|
86
|
+
ParseRecoveryResult.new(ast:, errors:)
|
|
87
|
+
rescue ParseError => e
|
|
88
|
+
errors ||= []
|
|
89
|
+
errors << e
|
|
90
|
+
ParseRecoveryResult.new(ast: nil, errors:)
|
|
91
|
+
ensure
|
|
92
|
+
@recovery_errors = previous_recovery_errors
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
BUILTIN_ATTRIBUTE_NAME_LEXEMES = %w[packed align].freeze
|
|
98
|
+
|
|
99
|
+
def parse_source_file(errors: nil)
|
|
100
|
+
skip_newlines
|
|
101
|
+
|
|
102
|
+
module_name = nil
|
|
103
|
+
module_kind = :module
|
|
104
|
+
module_line = nil
|
|
105
|
+
imports = []
|
|
106
|
+
directives = []
|
|
107
|
+
declarations = []
|
|
108
|
+
|
|
109
|
+
if external_file_header?
|
|
110
|
+
advance
|
|
111
|
+
module_line = previous.line
|
|
112
|
+
module_kind = :raw_module
|
|
113
|
+
consume(:newline, "expected newline after external") unless eof?
|
|
114
|
+
skip_newlines
|
|
115
|
+
imports, directives, declarations = parse_raw_module_body(errors:)
|
|
116
|
+
skip_newlines
|
|
117
|
+
raise error(peek, "expected end of file after external declarations") unless eof?
|
|
118
|
+
|
|
119
|
+
return AST.assign_node_ids(AST::SourceFile.new(module_name:, module_kind:, imports:, directives:, declarations:, line: module_line))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
while match(:import)
|
|
123
|
+
if errors
|
|
124
|
+
begin
|
|
125
|
+
imports << parse_import
|
|
126
|
+
rescue ParseError => e
|
|
127
|
+
errors << e
|
|
128
|
+
synchronize_to_top_level_boundary
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
imports << parse_import
|
|
132
|
+
end
|
|
133
|
+
skip_newlines
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
until eof?
|
|
137
|
+
if errors
|
|
138
|
+
begin
|
|
139
|
+
declarations << parse_declaration
|
|
140
|
+
rescue ParseError => e
|
|
141
|
+
errors << e
|
|
142
|
+
synchronize_to_top_level_boundary
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
declarations << parse_declaration
|
|
146
|
+
end
|
|
147
|
+
skip_newlines
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
AST.assign_node_ids(AST::SourceFile.new(module_name:, module_kind:, imports:, directives:, declarations:, line: module_line))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def external_file_header?
|
|
154
|
+
return false unless check(:external)
|
|
155
|
+
|
|
156
|
+
next_token = @tokens[@current + 1]
|
|
157
|
+
next_token.nil? || %i[newline eof].include?(next_token.type)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def match(*types)
|
|
161
|
+
return false unless types.any? { |type| check(type) }
|
|
162
|
+
|
|
163
|
+
advance
|
|
164
|
+
true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def consume(type, message)
|
|
168
|
+
return advance if check(type)
|
|
169
|
+
|
|
170
|
+
raise error(%i[rparen rbracket rbrace].include?(type) ? previous : peek, message)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def consume_name(message)
|
|
174
|
+
if keyword_token?(peek)
|
|
175
|
+
name_role = message.sub(/\A(?:expected|required)\s+/, "")
|
|
176
|
+
clearer_message = if name_role == message
|
|
177
|
+
message
|
|
178
|
+
else
|
|
179
|
+
"keyword '#{peek.lexeme}' cannot be used as #{name_role}"
|
|
180
|
+
end
|
|
181
|
+
raise error(peek, clearer_message)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
consume(:identifier, message)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def consume_name_allowing_keywords(message)
|
|
188
|
+
if check(:identifier)
|
|
189
|
+
advance
|
|
190
|
+
elsif keyword_token?(peek)
|
|
191
|
+
advance
|
|
192
|
+
else
|
|
193
|
+
raise error(peek, message)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def keyword_token?(token)
|
|
198
|
+
token && Token::KEYWORDS.key?(token.lexeme)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def check(type)
|
|
202
|
+
return false if eof?
|
|
203
|
+
|
|
204
|
+
peek.type == type
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def check_name
|
|
208
|
+
!eof? && peek.type == :identifier
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def check_next(type)
|
|
212
|
+
return false if (@current + 1) >= @tokens.length
|
|
213
|
+
|
|
214
|
+
@tokens[@current + 1].type == type
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def match_name
|
|
218
|
+
return false unless check_name
|
|
219
|
+
|
|
220
|
+
advance
|
|
221
|
+
true
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def advance
|
|
225
|
+
@current += 1 unless eof?
|
|
226
|
+
previous
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def eof?
|
|
230
|
+
peek.type == :eof
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def peek
|
|
234
|
+
@tokens[@current]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def previous
|
|
238
|
+
@tokens[@current - 1]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def error(token, message)
|
|
242
|
+
ParseError.new(message, token:, path: @path)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def skip_newlines
|
|
246
|
+
advance while check(:newline)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def consume_end_of_statement
|
|
250
|
+
return if @in_inline_block_body
|
|
251
|
+
|
|
252
|
+
consume(:newline, "expected end of statement")
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def block_expression?(expression)
|
|
256
|
+
expression.is_a?(AST::ProcExpr) || expression.is_a?(AST::MatchExpr)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def parse_qualified_name
|
|
260
|
+
parts = [consume_path_component("expected identifier").lexeme]
|
|
261
|
+
while match(:dot)
|
|
262
|
+
parts << consume_path_component("expected identifier after '.'").lexeme
|
|
263
|
+
end
|
|
264
|
+
AST::QualifiedName.new(parts:)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def consume_path_component(message)
|
|
268
|
+
return advance if !eof? && (peek.type == :identifier || Token::KEYWORDS.value?(peek.type))
|
|
269
|
+
|
|
270
|
+
raise error(peek, message)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def parse_comma_separated_until(closing_type)
|
|
274
|
+
items = []
|
|
275
|
+
|
|
276
|
+
unless check(closing_type)
|
|
277
|
+
loop do
|
|
278
|
+
items << yield
|
|
279
|
+
break unless match(:comma)
|
|
280
|
+
break if check(closing_type)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
items
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def foreign_param_qualifier_mode?
|
|
288
|
+
return false unless %i[out in inout consuming].include?(peek.type)
|
|
289
|
+
|
|
290
|
+
@tokens[@current + 1]&.type == :identifier
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def legacy_layout_modifier_start?(token)
|
|
294
|
+
token&.type == :identifier && BUILTIN_ATTRIBUTE_NAME_LEXEMES.include?(token.lexeme)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def known_type_like_name?(name)
|
|
298
|
+
@known_type_names.key?(name) || @known_import_aliases.key?(name) || @current_type_param_names.include?(name)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def with_type_param_names(names)
|
|
302
|
+
saved_names = @current_type_param_names
|
|
303
|
+
@current_type_param_names = @current_type_param_names + names
|
|
304
|
+
yield
|
|
305
|
+
ensure
|
|
306
|
+
@current_type_param_names = saved_names
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def seed_known_names
|
|
310
|
+
MilkTea::BUILTIN_TYPE_NAMES.each { |name| @known_type_names[name] = true }
|
|
311
|
+
|
|
312
|
+
depth = 0
|
|
313
|
+
index = 0
|
|
314
|
+
while index < @tokens.length
|
|
315
|
+
token = @tokens[index]
|
|
316
|
+
case token.type
|
|
317
|
+
when :indent
|
|
318
|
+
depth += 1
|
|
319
|
+
when :dedent
|
|
320
|
+
depth -= 1 if depth.positive?
|
|
321
|
+
when :import
|
|
322
|
+
index = seed_import_alias(index + 1) if depth.zero?
|
|
323
|
+
when :function
|
|
324
|
+
if depth.zero?
|
|
325
|
+
name_token = @tokens[index + 1]
|
|
326
|
+
type_param_token = @tokens[index + 2]
|
|
327
|
+
if type_name_token?(name_token) && type_param_token&.type == :lbracket
|
|
328
|
+
@known_generic_callable_names[name_token.lexeme] = true
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
when :async
|
|
332
|
+
if depth.zero? && @tokens[index + 1]&.type == :function
|
|
333
|
+
name_token = @tokens[index + 2]
|
|
334
|
+
type_param_token = @tokens[index + 3]
|
|
335
|
+
if type_name_token?(name_token) && type_param_token&.type == :lbracket
|
|
336
|
+
@known_generic_callable_names[name_token.lexeme] = true
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
when :foreign
|
|
340
|
+
if depth.zero? && @tokens[index + 1]&.type == :function
|
|
341
|
+
name_token = @tokens[index + 2]
|
|
342
|
+
type_param_token = @tokens[index + 3]
|
|
343
|
+
if type_name_token?(name_token) && type_param_token&.type == :lbracket
|
|
344
|
+
@known_generic_callable_names[name_token.lexeme] = true
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
when :struct, :union, :enum, :flags, :opaque, :type, :variant
|
|
348
|
+
if depth.zero?
|
|
349
|
+
name_token = @tokens[index + 1]
|
|
350
|
+
@known_type_names[name_token.lexeme] = true if type_name_token?(name_token)
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
index += 1
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def seed_import_alias(start_index)
|
|
359
|
+
cursor = start_index
|
|
360
|
+
last_part = nil
|
|
361
|
+
|
|
362
|
+
while cursor < @tokens.length && @tokens[cursor].type != :newline
|
|
363
|
+
token = @tokens[cursor]
|
|
364
|
+
if token.type == :as
|
|
365
|
+
alias_token = @tokens[cursor + 1]
|
|
366
|
+
@known_import_aliases[alias_token.lexeme] = true if type_name_token?(alias_token)
|
|
367
|
+
return cursor
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
last_part = token.lexeme if type_name_token?(token)
|
|
371
|
+
cursor += 1
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
@known_import_aliases[last_part] = true if last_part
|
|
375
|
+
cursor
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def type_name_token?(token)
|
|
379
|
+
token&.type == :identifier
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def matching_rbracket_index(start_index)
|
|
383
|
+
depth = 0
|
|
384
|
+
index = start_index
|
|
385
|
+
|
|
386
|
+
while index < @tokens.length
|
|
387
|
+
case @tokens[index].type
|
|
388
|
+
when :lbracket
|
|
389
|
+
depth += 1
|
|
390
|
+
when :rbracket
|
|
391
|
+
depth -= 1
|
|
392
|
+
return index if depth.zero?
|
|
393
|
+
end
|
|
394
|
+
index += 1
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
nil
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class PreludeInstaller
|
|
5
|
+
PRELUDE_MODULE_PATHS = %w[std.option std.result].freeze
|
|
6
|
+
|
|
7
|
+
def initialize(resolve_module_path:, check_block:, bind_block:)
|
|
8
|
+
@resolve_module_path = resolve_module_path
|
|
9
|
+
@check_block = check_block
|
|
10
|
+
@bind_block = bind_block
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def install_prelude_modules!(ast, modules, importer_path: nil, collecting_errors: false)
|
|
14
|
+
current_module_name = ast.module_name&.to_s
|
|
15
|
+
return if PRELUDE_MODULE_PATHS.include?(current_module_name)
|
|
16
|
+
|
|
17
|
+
PRELUDE_MODULE_PATHS.each do |module_path|
|
|
18
|
+
next if modules.key?(module_path)
|
|
19
|
+
|
|
20
|
+
begin
|
|
21
|
+
import_path = @resolve_module_path.call(module_path, importer_path:)
|
|
22
|
+
import_analysis = @check_block.call(import_path, collecting_errors)
|
|
23
|
+
modules[module_path] = @bind_block.call(import_analysis)
|
|
24
|
+
rescue ModuleLoadError
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|