jade-lang 0.8.0 → 0.10.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 +4 -4
- data/CHANGELOG.md +439 -2
- data/docs/interop.md +18 -1
- 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.rb +2 -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 +23 -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/error.rb +5 -0
- data/lib/jade/extensions.rb +70 -0
- 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/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/body.rb +29 -1
- data/lib/jade/frontend/semantic_analysis/error/duplicate_implementation.rb +39 -0
- 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 +4 -0
- data/lib/jade/frontend/semantic_analysis/helper.rb +41 -0
- data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -10
- 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/deriving.rb +5 -4
- 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/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 +6 -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 +47 -2
- 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/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 +5 -3
- 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/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.rb +13 -1
- data/lib/jade/task.rb +74 -56
- 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 -2
- data/lib/jade/type.rb +36 -10
- data/lib/jade/version.rb +1 -1
- data/lib/jade.rb +1 -0
- metadata +35 -3
- data/lib/jade/frontend/type_checking/constraints/deriving/assignable.rb +0 -170
|
@@ -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
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module PatternAnalysis
|
|
4
|
+
Wildcard = Data.define do
|
|
5
|
+
def wildcard?
|
|
6
|
+
true
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def args
|
|
10
|
+
[]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
'_'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Literal = Data.define(:value) do
|
|
19
|
+
def wildcard?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def args
|
|
24
|
+
[]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_s
|
|
28
|
+
value.inspect
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Constructor = Data.define(:constructor, :args) do
|
|
33
|
+
def wildcard?
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_s
|
|
38
|
+
if constructor.start_with?('Tuple.')
|
|
39
|
+
"(#{args.map(&:to_s).join(', ')})"
|
|
40
|
+
else
|
|
41
|
+
name = constructor.split('.').last
|
|
42
|
+
args.empty? ? name : "#{name}(#{args.map(&:to_s).join(', ')})"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Record = Data.define(:fields) do
|
|
48
|
+
def wildcard?
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def args
|
|
53
|
+
[]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_s
|
|
57
|
+
fields
|
|
58
|
+
.map { |k, v| "#{k}: #{v}" }
|
|
59
|
+
.join(', ')
|
|
60
|
+
.then { "{ #{it} }" }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# An inclusive span of integers. A nil bound is unbounded on that side.
|
|
65
|
+
Interval = Data.define(:from, :to) do
|
|
66
|
+
def wildcard?
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def args
|
|
71
|
+
[]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def covers?(other)
|
|
75
|
+
(from.nil? || (other.from && other.from >= from)) &&
|
|
76
|
+
(to.nil? || (other.to && other.to <= to))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def to_s
|
|
80
|
+
case [from, to]
|
|
81
|
+
in [nil, nil] then '_'
|
|
82
|
+
in [n, ^n] then n.to_s
|
|
83
|
+
else "#{from}..#{to}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
module Pattern
|
|
89
|
+
extend self
|
|
90
|
+
|
|
91
|
+
def from_node(node)
|
|
92
|
+
case node
|
|
93
|
+
in AST::Pattern::Record(fields:)
|
|
94
|
+
fields
|
|
95
|
+
.to_h { [it.name, from_node(it.pattern)] }
|
|
96
|
+
.then { Record[it] }
|
|
97
|
+
|
|
98
|
+
in AST::Pattern::Constructor(constructor:, patterns:)
|
|
99
|
+
patterns
|
|
100
|
+
.map { from_node(it) }
|
|
101
|
+
.then { Constructor[constructor.symbol.qualified_name, it] }
|
|
102
|
+
|
|
103
|
+
# `[x, y | xs]` and `[x, y]` are the same Cons/Nil spine the checker
|
|
104
|
+
# already knows how to split.
|
|
105
|
+
in AST::Pattern::List(patterns:, rest:)
|
|
106
|
+
patterns
|
|
107
|
+
.map { from_node(it) }
|
|
108
|
+
.reverse
|
|
109
|
+
.reduce(rest ? Wildcard[] : Constructor['List.Nil', []]) do |acc, head|
|
|
110
|
+
Constructor['List.Cons', [head, acc]]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
in AST::Pattern::Range(from:, to:)
|
|
114
|
+
Interval[from&.value, to&.value]
|
|
115
|
+
|
|
116
|
+
in AST::Pattern::Binding | AST::Pattern::Wildcard
|
|
117
|
+
Wildcard[]
|
|
118
|
+
|
|
119
|
+
in AST::Pattern::Literal(literal: { value: true })
|
|
120
|
+
Constructor['Basics.True', []]
|
|
121
|
+
|
|
122
|
+
in AST::Pattern::Literal(literal: { value: false })
|
|
123
|
+
Constructor['Basics.False', []]
|
|
124
|
+
|
|
125
|
+
in AST::Pattern::Literal(literal: { value: })
|
|
126
|
+
Literal[value]
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module PatternAnalysis
|
|
4
|
+
module Redundancy
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def assert(patterns, env, expected)
|
|
8
|
+
rows = patterns.map { [Pattern.from_node(it)] }
|
|
9
|
+
|
|
10
|
+
rows
|
|
11
|
+
.each_with_index
|
|
12
|
+
.filter_map do |row, index|
|
|
13
|
+
above = Matrix[rows.take(index), [expected]]
|
|
14
|
+
|
|
15
|
+
next if Usefulness.useful?(above, row, env)
|
|
16
|
+
|
|
17
|
+
TypeChecking::Error::UnreachableBranch
|
|
18
|
+
.new(env.entry_name, patterns[index].range)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|