jade-lang 0.9.0 → 0.10.1
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/CHANGELOG.md +442 -12
- data/README.md +1 -0
- data/docs/interop.md +18 -1
- data/docs/json.md +34 -3
- data/docs/stdlib.md +3 -1
- data/docs/syntax.md +96 -0
- data/lib/jade/api.rb +9 -0
- data/lib/jade/ast.rb +37 -1
- data/lib/jade/cli/eject.rb +132 -0
- data/lib/jade/cli/init.rb +86 -0
- data/lib/jade/cli.rb +4 -0
- data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
- data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
- data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
- data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
- data/lib/jade/codegen/boundary/specialized.rb +13 -5
- data/lib/jade/codegen/boundary.rb +12 -2
- data/lib/jade/codegen/context.rb +17 -0
- data/lib/jade/codegen/error.rb +1 -0
- data/lib/jade/codegen/function_call.rb +38 -7
- data/lib/jade/codegen/function_declaration.rb +25 -13
- data/lib/jade/codegen/helpers.rb +27 -4
- data/lib/jade/codegen/implementation.rb +2 -2
- data/lib/jade/codegen/inline.rb +1 -1
- data/lib/jade/codegen/inlines.rb +6 -1
- data/lib/jade/codegen/names.rb +31 -0
- data/lib/jade/codegen/pattern/constructor.rb +2 -2
- data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
- data/lib/jade/codegen.rb +76 -15
- data/lib/jade/compiler.rb +2 -0
- data/lib/jade/entry.rb +1 -1
- data/lib/jade/formatter/calls.rb +68 -11
- data/lib/jade/formatter/collections.rb +4 -4
- data/lib/jade/formatter/declarations.rb +13 -15
- data/lib/jade/formatter/helper.rb +20 -0
- data/lib/jade/formatter/infix_application.rb +19 -4
- data/lib/jade/formatter/lambda.rb +24 -12
- data/lib/jade/formatter/pattern.rb +3 -0
- data/lib/jade/formatter/type.rb +3 -5
- data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
- data/lib/jade/frontend/desugaring.rb +29 -9
- data/lib/jade/frontend/fixity_fixer.rb +6 -4
- data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
- data/lib/jade/frontend/forward_declaration/error.rb +3 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
- data/lib/jade/frontend/forward_declaration/module.rb +4 -0
- data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
- data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration.rb +2 -0
- data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
- data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
- data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
- data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
- data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
- data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
- data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
- data/lib/jade/frontend/pattern_analysis.rb +5 -38
- data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
- data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
- data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error.rb +3 -0
- data/lib/jade/frontend/semantic_analysis/helper.rb +31 -0
- data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -0
- data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
- data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
- data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
- data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
- data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
- data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis.rb +5 -0
- data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
- data/lib/jade/frontend/type_checking/cascade.rb +90 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
- data/lib/jade/frontend/type_checking/constraints.rb +18 -3
- data/lib/jade/frontend/type_checking/error/display.rb +49 -0
- data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
- data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
- data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
- data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
- data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
- data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
- data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
- data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
- data/lib/jade/frontend/type_checking/error/record_update_type_mismatch.rb +21 -0
- data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
- data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
- data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
- data/lib/jade/frontend/type_checking/error.rb +7 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
- data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
- data/lib/jade/frontend/type_checking/inference/function_call.rb +26 -1
- data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
- data/lib/jade/frontend/type_checking/inference/record_update.rb +8 -1
- data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference.rb +2 -0
- data/lib/jade/frontend/type_checking/loader.rb +11 -0
- data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
- data/lib/jade/frontend/type_checking/substitution.rb +2 -2
- data/lib/jade/frontend/type_checking.rb +3 -1
- data/lib/jade/frontend/usage_analysis.rb +5 -1
- data/lib/jade/interop/boundary.rb +64 -24
- data/lib/jade/interop/error.rb +35 -7
- data/lib/jade/interop/lowering.rb +8 -2
- data/lib/jade/lsp/converters.rb +83 -2
- data/lib/jade/lsp/handlers.rb +122 -25
- data/lib/jade/lsp/snippets.rb +12 -0
- data/lib/jade/lsp/state.rb +11 -4
- data/lib/jade/module_loader/build.rb +36 -0
- data/lib/jade/module_loader.rb +1 -0
- data/lib/jade/parsing/combinators.rb +78 -2
- data/lib/jade/parsing/error.rb +43 -6
- data/lib/jade/parsing/type.rb +6 -4
- data/lib/jade/parsing.rb +140 -26
- data/lib/jade/runtime.rb +32 -13
- data/lib/jade/source.rb +6 -2
- data/lib/jade/stdlib/basics.rb +22 -3
- data/lib/jade/stdlib/char.rb +2 -0
- data/lib/jade/stdlib/clock.rb +6 -2
- data/lib/jade/stdlib/decimal.rb +51 -14
- data/lib/jade/stdlib/decode.rb +11 -0
- data/lib/jade/stdlib/intrinsics.rb +59 -6
- data/lib/jade/stdlib/list.rb +37 -1
- data/lib/jade/stdlib/number.rb +47 -0
- data/lib/jade/stdlib/range.rb +211 -0
- data/lib/jade/stdlib.rb +11 -4
- data/lib/jade/symbol/alias.rb +11 -0
- data/lib/jade/symbol/parser.rb +1 -1
- data/lib/jade/symbol/stdlib_function.rb +1 -1
- data/lib/jade/symbol.rb +13 -1
- data/lib/jade/task.rb +0 -2
- data/lib/jade/type/anonymous_record.rb +11 -2
- data/lib/jade/type/application.rb +11 -2
- data/lib/jade/type/base.rb +26 -0
- data/lib/jade/type/function.rb +11 -3
- data/lib/jade/type.rb +36 -10
- data/lib/jade/version.rb +1 -1
- metadata +35 -2
|
@@ -10,6 +10,64 @@ module Jade
|
|
|
10
10
|
ForwardDeclaration.deep_declare_node(ast, entry, registry)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# Only the shallow pass still has the declaration being replaced;
|
|
14
|
+
# by the deep pass the entry holds the winner alone. The deep pass
|
|
15
|
+
# keeps its kind check to bail out, not to report.
|
|
16
|
+
def duplicate_type_error(entry, name, span, declaring:)
|
|
17
|
+
entry.defined_types[name].then do |existing|
|
|
18
|
+
next nil if existing.nil? || existing.decl_span == span
|
|
19
|
+
|
|
20
|
+
Error::DuplicateTypeName.new(
|
|
21
|
+
entry.name,
|
|
22
|
+
span,
|
|
23
|
+
name,
|
|
24
|
+
declaring:,
|
|
25
|
+
existing: Error::DuplicateTypeName.kind_of(existing),
|
|
26
|
+
first_span: existing.decl_span,
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# A constructor is named without its type, so one module cannot hold
|
|
32
|
+
# two of a name — a union's variant and a struct both define one, and
|
|
33
|
+
# the second shadows the first. `introduced` is what this declaration
|
|
34
|
+
# is about to define, each with the span to point at.
|
|
35
|
+
def duplicate_constructor_errors(entry, name, introduced, declaring:)
|
|
36
|
+
declared_constructors(entry).then do |seen|
|
|
37
|
+
introduced.filter_map { duplicate_constructor_error(entry, name, it, seen, declaring) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def declared_constructors(entry)
|
|
42
|
+
entry
|
|
43
|
+
.defined_types
|
|
44
|
+
.values
|
|
45
|
+
.flat_map do |symbol|
|
|
46
|
+
case symbol
|
|
47
|
+
in Symbol::Union then symbol.variants.map { [it.name, symbol] }
|
|
48
|
+
in Symbol::Struct then [[symbol.name, symbol]]
|
|
49
|
+
else []
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
.to_h
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def duplicate_constructor_error(entry, name, (constructor, span), seen, declaring)
|
|
56
|
+
seen[constructor].then do |owner|
|
|
57
|
+
next nil if owner.nil? || owner.name == name
|
|
58
|
+
|
|
59
|
+
Error::DuplicateConstructorName.new(
|
|
60
|
+
entry.name,
|
|
61
|
+
span,
|
|
62
|
+
constructor,
|
|
63
|
+
declaring:,
|
|
64
|
+
type: name,
|
|
65
|
+
owner:,
|
|
66
|
+
first_span: owner.decl_span,
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
13
71
|
def figure_out_type(entry, node)
|
|
14
72
|
case node
|
|
15
73
|
in AST::TypeVar(type:)
|
|
@@ -83,6 +83,10 @@ module Jade
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def handling_exposing_type_expansion(type, name, span, current_entry, importing_module)
|
|
86
|
+
if importing_module.types[name].is_a?(Symbol::Alias)
|
|
87
|
+
return Err[Error::AliasExpansion.new(current_entry.name, span, name:)]
|
|
88
|
+
end
|
|
89
|
+
|
|
86
90
|
importing_module
|
|
87
91
|
.exposed_type_variants(name)
|
|
88
92
|
.then do |variants|
|
|
@@ -8,6 +8,9 @@ module Jade
|
|
|
8
8
|
def shallow(node, _registry, entry)
|
|
9
9
|
node => AST::InterfaceDeclaration(name:, type_param:, functions:)
|
|
10
10
|
|
|
11
|
+
duplicate_type_error(entry, name, node.range, declaring: 'an interface')
|
|
12
|
+
.then { return Result[entry, [it]] if it }
|
|
13
|
+
|
|
11
14
|
interface_ref = Symbol.type_ref(entry.name, name)
|
|
12
15
|
type_var = Symbol.var(type_param.name, type_param.range)
|
|
13
16
|
|
|
@@ -28,6 +31,9 @@ module Jade
|
|
|
28
31
|
node => AST::InterfaceDeclaration(name:, functions:)
|
|
29
32
|
|
|
30
33
|
symbol = entry.lookup_type(name)
|
|
34
|
+
|
|
35
|
+
return Result[entry, []] unless symbol.is_a?(Symbol::Interface)
|
|
36
|
+
|
|
31
37
|
interface_ref = symbol.to_ref
|
|
32
38
|
|
|
33
39
|
functions
|
|
@@ -8,11 +8,18 @@ module Jade
|
|
|
8
8
|
def shallow(node, registry, entry)
|
|
9
9
|
node => AST::StructDeclaration(name:, type_params:)
|
|
10
10
|
|
|
11
|
+
duplicate_type_error(entry, name, node.range, declaring: 'a struct')
|
|
12
|
+
.then { return Result[entry, [it]] if it }
|
|
13
|
+
|
|
14
|
+
# The struct is still declared, so a signature naming it does not
|
|
15
|
+
# report a second, misleading error.
|
|
16
|
+
errors = duplicate_constructor_errors(entry, name, [[name, node.range]], declaring: :struct)
|
|
17
|
+
|
|
11
18
|
type_params
|
|
12
19
|
.map { Symbol.var(it.name, it.range) }
|
|
13
20
|
.then { Symbol.predeclared_struct(name, it, node.range) }
|
|
14
21
|
.then { entry.define(it) }
|
|
15
|
-
.then { Result[it,
|
|
22
|
+
.then { Result[it, errors] }
|
|
16
23
|
end
|
|
17
24
|
|
|
18
25
|
def deep(node, entry, _)
|
|
@@ -20,6 +27,8 @@ module Jade
|
|
|
20
27
|
|
|
21
28
|
symbol = entry.lookup_type(name)
|
|
22
29
|
|
|
30
|
+
return Result[entry, []] unless symbol.is_a?(Symbol::Struct)
|
|
31
|
+
|
|
23
32
|
figure_out_type(entry, record_type)
|
|
24
33
|
.map do |record_type_symbol|
|
|
25
34
|
constructor_fn_symbol = Symbol.constructor(
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module ForwardDeclaration
|
|
4
|
+
module TypeAliasDeclaration
|
|
5
|
+
extend self
|
|
6
|
+
extend Helper
|
|
7
|
+
|
|
8
|
+
def shallow(node, registry, entry)
|
|
9
|
+
node => AST::TypeAliasDeclaration(name:, type_params:)
|
|
10
|
+
|
|
11
|
+
duplicate_type_error(entry, name, node.range, declaring: 'a type alias')
|
|
12
|
+
.then { return Result[entry, [it]] if it }
|
|
13
|
+
|
|
14
|
+
type_params
|
|
15
|
+
.map { Symbol.var(it.name, it.range) }
|
|
16
|
+
.then { Symbol.predeclared_alias(name, it, node.range) }
|
|
17
|
+
.then { entry.define(it) }
|
|
18
|
+
.then { Result[it, []] }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def deep(node, entry, _)
|
|
22
|
+
node => AST::TypeAliasDeclaration(name:, body_type:)
|
|
23
|
+
|
|
24
|
+
symbol = entry.lookup_type(name)
|
|
25
|
+
|
|
26
|
+
return Result[entry, []] unless symbol.is_a?(Symbol::Alias)
|
|
27
|
+
|
|
28
|
+
figure_out_type(entry, body_type)
|
|
29
|
+
.map do |body_symbol|
|
|
30
|
+
entry.define(symbol.with(body: body_symbol))
|
|
31
|
+
end
|
|
32
|
+
.then { to_declaration_result(entry, it) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -8,13 +8,20 @@ module Jade
|
|
|
8
8
|
def shallow(node, registry, entry)
|
|
9
9
|
node => AST::TypeDeclaration(name:, type_params:, variants:)
|
|
10
10
|
|
|
11
|
+
duplicate_type_error(entry, name, node.range, declaring: 'a type')
|
|
12
|
+
.then { return Result[entry, [it]] if it }
|
|
13
|
+
|
|
14
|
+
# The type is still declared, so a signature naming it does not
|
|
15
|
+
# report a second, misleading error.
|
|
16
|
+
errors = duplicate_constructor_errors(entry, name, variants.map { [it.name, it.range] }, declaring: :variant)
|
|
17
|
+
|
|
11
18
|
predeclared_variants = variants
|
|
12
19
|
.map { |var| Symbol.predeclared_constructor(var.name, var.range) }
|
|
13
20
|
|
|
14
21
|
type_params.map { Symbol.var(it.name, it.range) }
|
|
15
22
|
.then { Symbol.union(name, it, predeclared_variants, node.range) }
|
|
16
23
|
.then { entry.define(it) }
|
|
17
|
-
.then { Result[it,
|
|
24
|
+
.then { Result[it, errors] }
|
|
18
25
|
end
|
|
19
26
|
|
|
20
27
|
def deep(node, entry, _)
|
|
@@ -22,6 +29,8 @@ module Jade
|
|
|
22
29
|
|
|
23
30
|
symbol = entry.lookup_type(name)
|
|
24
31
|
|
|
32
|
+
return Result[entry, []] unless symbol.is_a?(Symbol::Union)
|
|
33
|
+
|
|
25
34
|
variants
|
|
26
35
|
.map do |var|
|
|
27
36
|
var.args.map { figure_out_type(entry, it) }
|
|
@@ -11,6 +11,7 @@ require 'jade/frontend/forward_declaration/import_declaration'
|
|
|
11
11
|
require 'jade/frontend/forward_declaration/interop_import_declaration'
|
|
12
12
|
require 'jade/frontend/forward_declaration/module'
|
|
13
13
|
require 'jade/frontend/forward_declaration/struct_declaration'
|
|
14
|
+
require 'jade/frontend/forward_declaration/type_alias_declaration'
|
|
14
15
|
require 'jade/frontend/forward_declaration/type_declaration'
|
|
15
16
|
require 'jade/frontend/forward_declaration/interface_declaration'
|
|
16
17
|
|
|
@@ -60,6 +61,7 @@ module Jade
|
|
|
60
61
|
in AST::InteropImportDeclaration then InteropImportDeclaration
|
|
61
62
|
in AST::Module then Module
|
|
62
63
|
in AST::StructDeclaration then StructDeclaration
|
|
64
|
+
in AST::TypeAliasDeclaration then TypeAliasDeclaration
|
|
63
65
|
in AST::TypeDeclaration then TypeDeclaration
|
|
64
66
|
in AST::InterfaceDeclaration then InterfaceDeclaration
|
|
65
67
|
else
|
|
@@ -4,17 +4,58 @@ module Jade
|
|
|
4
4
|
module Exhaustiveness
|
|
5
5
|
extend self
|
|
6
6
|
|
|
7
|
-
def assert(patterns, range, env,
|
|
8
|
-
Matrix[
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
]
|
|
12
|
-
.missing_patterns(env)
|
|
7
|
+
def assert(patterns, range, env, expected)
|
|
8
|
+
Matrix[patterns.map { [Pattern.from_node(it)] }, [expected]]
|
|
9
|
+
.then { Witnesses.missing(it, env) }
|
|
10
|
+
.then { it.with(rows: coalesced(it.rows)) }
|
|
13
11
|
.then { to_errors(it, range, env) }
|
|
14
12
|
end
|
|
15
13
|
|
|
16
14
|
private
|
|
17
15
|
|
|
16
|
+
# Splitting a column cuts it at every bound any pattern mentions, so
|
|
17
|
+
# one hole can come back as several touching pieces. Report the hole.
|
|
18
|
+
def coalesced(rows)
|
|
19
|
+
joined = rows
|
|
20
|
+
.combination(2)
|
|
21
|
+
.lazy
|
|
22
|
+
.filter_map { |(left, right)| join(left, right)&.then { [left, right, it] } }
|
|
23
|
+
.first
|
|
24
|
+
|
|
25
|
+
return rows if joined.nil?
|
|
26
|
+
|
|
27
|
+
joined => [left, right, combined]
|
|
28
|
+
|
|
29
|
+
coalesced(rows - [left, right] + [combined])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def join(left, right)
|
|
33
|
+
return if left.size != right.size
|
|
34
|
+
|
|
35
|
+
differing = left.each_index.reject { left[it] == right[it] }
|
|
36
|
+
|
|
37
|
+
return if differing.size != 1
|
|
38
|
+
|
|
39
|
+
index = differing.first
|
|
40
|
+
|
|
41
|
+
span(left[index], right[index])
|
|
42
|
+
&.then { |joined| left.take(index) + [joined] + left.drop(index + 1) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def span(left, right)
|
|
46
|
+
case [left, right]
|
|
47
|
+
in [Interval(from:, to: Integer => last), Interval(from: ^(last + 1), to:)]
|
|
48
|
+
Interval[from, to]
|
|
49
|
+
|
|
50
|
+
in [Constructor(constructor: name, args: left_args),
|
|
51
|
+
Constructor(constructor: ^name, args: right_args)]
|
|
52
|
+
join(left_args, right_args)&.then { Constructor[name, it] }
|
|
53
|
+
|
|
54
|
+
else
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
18
59
|
def to_errors(matrix, range, env)
|
|
19
60
|
return [] if matrix.empty?
|
|
20
61
|
|
|
@@ -22,43 +63,6 @@ module Jade
|
|
|
22
63
|
.new(env.entry_name, range, missing_patterns: matrix.rows)
|
|
23
64
|
.then { [it] }
|
|
24
65
|
end
|
|
25
|
-
|
|
26
|
-
def node_to_matrix_pattern(pattern_node)
|
|
27
|
-
case pattern_node
|
|
28
|
-
in AST::Pattern::Record(fields:)
|
|
29
|
-
Record[
|
|
30
|
-
fields
|
|
31
|
-
.map(&:name)
|
|
32
|
-
.zip(fields.map(&:pattern))
|
|
33
|
-
.to_h
|
|
34
|
-
.transform_values { node_to_matrix_pattern(it) },
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
in AST::Pattern::Constructor(constructor:, patterns:)
|
|
38
|
-
Constructor[
|
|
39
|
-
constructor.symbol.qualified_name,
|
|
40
|
-
patterns.map { node_to_matrix_pattern(it) },
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
in AST::Pattern::List(patterns:, rest:)
|
|
44
|
-
# Rewrite [x, y | xs] / [x, y] into nested Cons/Nil constructors
|
|
45
|
-
tail = rest ? Wildcard[] : Constructor['List.Nil', []]
|
|
46
|
-
patterns
|
|
47
|
-
.map { node_to_matrix_pattern(it) }
|
|
48
|
-
.reverse
|
|
49
|
-
.reduce(tail) { |acc, head| Constructor['List.Cons', [head, acc]] }
|
|
50
|
-
|
|
51
|
-
in AST::Pattern::Binding | AST::Pattern::Wildcard
|
|
52
|
-
Wildcard[]
|
|
53
|
-
|
|
54
|
-
in AST::Pattern::Literal(literal: { value:, symbol: })
|
|
55
|
-
Literal[
|
|
56
|
-
value,
|
|
57
|
-
symbol.qualified_name,
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
end
|
|
@@ -5,12 +5,11 @@ module Jade
|
|
|
5
5
|
def self.wildcard(types)
|
|
6
6
|
types
|
|
7
7
|
.map { Wildcard[] }
|
|
8
|
-
.then { [it] }
|
|
9
|
-
.then { Matrix[it, types] }
|
|
8
|
+
.then { Matrix[[it], types] }
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
def self.empty(types = [])
|
|
13
|
-
|
|
12
|
+
Matrix[[], types]
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def map
|
|
@@ -19,248 +18,58 @@ module Jade
|
|
|
19
18
|
.then { with(rows: it) }
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
def include?(row)
|
|
23
|
-
rows.include?(row)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def size
|
|
27
|
-
rows.size
|
|
28
|
-
end
|
|
29
|
-
|
|
30
21
|
def empty?
|
|
31
22
|
rows.empty?
|
|
32
23
|
end
|
|
33
24
|
|
|
34
|
-
def any?
|
|
35
|
-
rows.any?
|
|
36
|
-
end
|
|
37
|
-
|
|
38
25
|
def concat(other)
|
|
39
26
|
with(rows: rows + other.rows)
|
|
40
27
|
end
|
|
41
28
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
return rows.empty? ? Matrix[[[]], types] : Matrix.empty
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
type = types.first
|
|
48
|
-
|
|
49
|
-
return Matrix.empty if never?(type)
|
|
50
|
-
return Matrix.wildcard(types) if rows.empty?
|
|
51
|
-
|
|
52
|
-
heads = head_constructors
|
|
53
|
-
|
|
54
|
-
if infinite?(type) || type_var?(type)
|
|
55
|
-
unmatched_column(env)
|
|
56
|
-
|
|
57
|
-
elsif expandable?(type, env)
|
|
58
|
-
expand(env).missing_patterns(env)
|
|
59
|
-
|
|
60
|
-
elsif heads.empty?
|
|
61
|
-
unmatched_column(env)
|
|
62
|
-
|
|
63
|
-
else
|
|
64
|
-
constructors_of(type, env)
|
|
65
|
-
.reduce(Matrix.empty.with(types:)) do |acc, constructor|
|
|
66
|
-
witnesses(constructor, env, heads.include?(constructor.name))
|
|
67
|
-
.then { acc.concat(it) }
|
|
68
|
-
end
|
|
69
|
-
end
|
|
29
|
+
def heads
|
|
30
|
+
rows.map(&:first)
|
|
70
31
|
end
|
|
71
32
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
# the columns after it.
|
|
77
|
-
def unmatched_column(env)
|
|
78
|
-
default
|
|
79
|
-
.missing_patterns(env)
|
|
80
|
-
.map { [Wildcard[]] + it }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# A constructor the first column matches is explored by specializing on
|
|
84
|
-
# it. One it never matches can only be missing as a whole, so its
|
|
85
|
-
# witnesses come from the rows that would have covered it — the ones
|
|
86
|
-
# headed by a wildcard. Recursing there instead of into the constructor
|
|
87
|
-
# is what keeps a recursive type from expanding forever.
|
|
88
|
-
def witnesses(constructor, env, matched)
|
|
89
|
-
return Matrix.empty if constructor.args.any? { never?(it) }
|
|
90
|
-
|
|
91
|
-
arity = constructor.args.size
|
|
92
|
-
|
|
93
|
-
if matched
|
|
94
|
-
specialize(constructor)
|
|
95
|
-
.missing_patterns(env)
|
|
96
|
-
.map { [Constructor[constructor.name, it.take(arity)]] + it.drop(arity) }
|
|
97
|
-
|
|
98
|
-
else
|
|
99
|
-
default
|
|
100
|
-
.missing_patterns(env)
|
|
101
|
-
.map { [Constructor[constructor.name, Array.new(arity) { Wildcard[] }]] + it }
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def head_constructors
|
|
106
|
-
rows.filter_map { head_constructor(it.first) }
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def head_constructor(pattern)
|
|
110
|
-
case pattern
|
|
111
|
-
in Constructor(constructor: name) then name
|
|
112
|
-
in Literal(value: true) then 'Basics.True'
|
|
113
|
-
in Literal(value: false) then 'Basics.False'
|
|
114
|
-
in Literal | Record | Wildcard then nil
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def expandable?(type, env)
|
|
119
|
-
case type
|
|
120
|
-
in Type::AnonymousRecord
|
|
121
|
-
true
|
|
122
|
-
|
|
123
|
-
in Type::Application(constructor:)
|
|
124
|
-
env.lookup_def(constructor.name).is_a?(TypeChecking::StructDef)
|
|
125
|
-
|
|
126
|
-
else
|
|
127
|
-
false
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def expand(env)
|
|
132
|
-
type_fields =
|
|
133
|
-
case types.first
|
|
134
|
-
in Type::AnonymousRecord(fields:)
|
|
135
|
-
fields
|
|
136
|
-
|
|
137
|
-
in Type::Application(constructor:)
|
|
138
|
-
env.lookup_def(constructor.name).body.fields
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
map do |row|
|
|
142
|
-
case row.first
|
|
143
|
-
in Record(fields:)
|
|
144
|
-
type_fields
|
|
145
|
-
.map do |(k, v)|
|
|
146
|
-
fields[k] || Wildcard[]
|
|
147
|
-
end + row.drop(1)
|
|
148
|
-
|
|
149
|
-
in Wildcard
|
|
150
|
-
type_fields.map { Wildcard[] } + row.drop(1)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
.with(types: type_fields.values + types.drop(1))
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def constructors_of(type, env)
|
|
157
|
-
case type
|
|
158
|
-
in Type::Application(constructor: Type::Constructor(name: 'Basics.Bool'))
|
|
159
|
-
[
|
|
160
|
-
TypeChecking::ConstructorDef['Basics.True', 'Basics.Bool', []],
|
|
161
|
-
TypeChecking::ConstructorDef['Basics.False', 'Basics.Bool', []],
|
|
162
|
-
]
|
|
163
|
-
in Type::Application(constructor: Type::Constructor(name: /^Tuple\.Tuple[2-4]$/ => name), args:)
|
|
164
|
-
[TypeChecking::Definition.constructor(name, name, args)]
|
|
165
|
-
|
|
166
|
-
in Type::Application(constructor: Type::Constructor(name: 'List.List'), args: [elem_type])
|
|
167
|
-
[
|
|
168
|
-
TypeChecking::ConstructorDef['List.Nil', 'List.List', []],
|
|
169
|
-
TypeChecking::ConstructorDef['List.Cons', 'List.List', [elem_type, type]],
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
else
|
|
173
|
-
type_def = env.lookup_def(type.constructor.name)
|
|
174
|
-
|
|
175
|
-
# Type the user can't destructure from here (transitively reached
|
|
176
|
-
# through an imported struct field, or opaque intrinsic) —
|
|
177
|
-
# any pattern they can write is trivially exhaustive.
|
|
178
|
-
return [] if type_def.nil? || type_def.opaque?
|
|
179
|
-
|
|
180
|
-
type_def
|
|
181
|
-
.constructors
|
|
182
|
-
.map do |con|
|
|
183
|
-
con
|
|
184
|
-
.args
|
|
185
|
-
.map do |arg|
|
|
186
|
-
if type_var?(arg)
|
|
187
|
-
type_def
|
|
188
|
-
.type_params
|
|
189
|
-
.find_index { arg.name == it.name }
|
|
190
|
-
.then { type.args.at(it) }
|
|
191
|
-
|
|
192
|
-
else
|
|
193
|
-
arg
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
.then { con.with(args: it) }
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def specialize(constructor)
|
|
33
|
+
# Rows the case can match, each opened up into the columns the case
|
|
34
|
+
# carries. A row headed by a wildcard matches every case, and says
|
|
35
|
+
# nothing about the columns it opens.
|
|
36
|
+
def specialize(kase)
|
|
202
37
|
rows
|
|
203
|
-
.filter_map do |
|
|
204
|
-
|
|
205
|
-
in Literal(value:)
|
|
206
|
-
next [] + cols.drop(1) if value == false && "Basics.False" == constructor.name
|
|
207
|
-
next [] + cols.drop(1) if value == true && "Basics.True" == constructor.name
|
|
208
|
-
|
|
209
|
-
in Constructor(constructor: cons_name, args:)
|
|
210
|
-
args + cols.drop(1) if cons_name == constructor.name
|
|
211
|
-
|
|
212
|
-
in Wildcard
|
|
213
|
-
constructor.args.map { Wildcard[] } + cols.drop(1)
|
|
38
|
+
.filter_map do |row|
|
|
39
|
+
head = row.first
|
|
214
40
|
|
|
41
|
+
if kase.matches?(head)
|
|
42
|
+
head.args + row.drop(1)
|
|
43
|
+
elsif head.wildcard?
|
|
44
|
+
Array.new(kase.arity) { Wildcard[] } + row.drop(1)
|
|
215
45
|
end
|
|
216
46
|
end
|
|
217
|
-
.then {
|
|
218
|
-
.then { it.with(types: constructor.args + types.drop(1)) }
|
|
47
|
+
.then { Matrix[it, kase.arg_types + types.drop(1)] }
|
|
219
48
|
end
|
|
220
49
|
|
|
50
|
+
# The rows that survive when the first column is decided by no case at
|
|
51
|
+
# all — only the ones that were not asking about it.
|
|
221
52
|
def default
|
|
222
53
|
rows
|
|
223
54
|
.select { it.first.wildcard? }
|
|
224
55
|
.map { it.drop(1) }
|
|
225
|
-
.then {
|
|
226
|
-
.with(types: types.drop(1))
|
|
56
|
+
.then { Matrix[it, types.drop(1)] }
|
|
227
57
|
end
|
|
228
58
|
|
|
229
|
-
def
|
|
230
|
-
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
private
|
|
234
|
-
|
|
235
|
-
def never?(type)
|
|
236
|
-
case type
|
|
237
|
-
in Type::Application(constructor: Type::Constructor(name: 'Basics.Never'))
|
|
238
|
-
true
|
|
239
|
-
else
|
|
240
|
-
false
|
|
241
|
-
end
|
|
242
|
-
end
|
|
59
|
+
def expand(env)
|
|
60
|
+
fields = Signature.fields(types.first, env)
|
|
243
61
|
|
|
244
|
-
|
|
245
|
-
|
|
62
|
+
map { |row| expand_row(row, fields) }
|
|
63
|
+
.with(types: fields.values + types.drop(1))
|
|
246
64
|
end
|
|
247
65
|
|
|
248
|
-
def
|
|
249
|
-
case
|
|
250
|
-
in
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
in Type::Application(constructor:)
|
|
254
|
-
case constructor
|
|
255
|
-
in Type::Constructor(name: 'Basics.Int') then true
|
|
256
|
-
in Type::Constructor(name: 'Basics.Float') then true
|
|
257
|
-
in Type::Constructor(name: 'String.String') then true
|
|
258
|
-
else
|
|
259
|
-
false
|
|
260
|
-
end
|
|
66
|
+
def expand_row(row, fields)
|
|
67
|
+
case row.first
|
|
68
|
+
in Record(fields: matched)
|
|
69
|
+
fields.keys.map { matched[it] || Wildcard[] } + row.drop(1)
|
|
261
70
|
|
|
262
|
-
|
|
263
|
-
|
|
71
|
+
in Wildcard
|
|
72
|
+
fields.keys.map { Wildcard[] } + row.drop(1)
|
|
264
73
|
end
|
|
265
74
|
end
|
|
266
75
|
end
|