jade-lang 0.11.3 → 0.12.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 +112 -0
- data/lib/jade/ast.rb +1 -1
- data/lib/jade/codegen/context.rb +18 -1
- data/lib/jade/codegen/function_call.rb +9 -2
- data/lib/jade/codegen/inlines.rb +2 -0
- data/lib/jade/codegen.rb +7 -5
- data/lib/jade/diagnostics/renderer.rb +5 -1
- data/lib/jade/entry.rb +32 -10
- data/lib/jade/frontend/forward_declaration/error/ambiguous_type.rb +36 -0
- data/lib/jade/frontend/forward_declaration/error.rb +1 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +7 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +10 -2
- data/lib/jade/frontend/semantic_analysis/constructor_reference.rb +5 -0
- data/lib/jade/frontend/semantic_analysis/error/ambiguous_name.rb +36 -0
- data/lib/jade/frontend/semantic_analysis/error/top_level_statement.rb +28 -0
- data/lib/jade/frontend/semantic_analysis/error.rb +2 -0
- data/lib/jade/frontend/semantic_analysis/module_node.rb +21 -1
- data/lib/jade/frontend/semantic_analysis/variable_reference.rb +5 -0
- data/lib/jade/frontend/semantic_analysis.rb +4 -6
- data/lib/jade/frontend/type_checking/constraints/deriving/show.rb +24 -0
- data/lib/jade/frontend/type_checking/error/narrowed_method_variable.rb +36 -0
- data/lib/jade/frontend/type_checking/error/narrowed_signature.rb +53 -0
- data/lib/jade/frontend/type_checking/error.rb +2 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +7 -4
- data/lib/jade/frontend/type_checking/inference/case_of.rb +1 -6
- data/lib/jade/frontend/type_checking/inference/constructor_reference.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/function_declaration.rb +21 -0
- data/lib/jade/frontend/type_checking/inference/helpers.rb +11 -0
- data/lib/jade/frontend/type_checking/inference/implementation.rb +23 -2
- data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -4
- data/lib/jade/frontend/type_checking/inference/list.rb +16 -2
- data/lib/jade/frontend/type_checking/narrowing.rb +93 -0
- data/lib/jade/frontend/type_checking/pattern_checks.rb +30 -0
- data/lib/jade/frontend/type_checking/state.rb +9 -2
- data/lib/jade/frontend/type_checking.rb +3 -6
- data/lib/jade/frontend.rb +0 -22
- data/lib/jade/parsing/error.rb +15 -0
- data/lib/jade/registry.rb +1 -1
- data/lib/jade/stdlib/compiled.rb +8 -1
- data/lib/jade/stdlib/dict.rb +12 -0
- data/lib/jade/stdlib/intrinsics.rb +8 -1
- data/lib/jade/stdlib/set.rb +12 -0
- data/lib/jade/stdlib.rb +8 -1
- data/lib/jade/symbol/ambiguous.rb +5 -0
- data/lib/jade/symbol.rb +1 -0
- data/lib/jade/version.rb +1 -1
- metadata +10 -2
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
module Error
|
|
5
|
+
class NarrowedSignature < Jade::Error
|
|
6
|
+
def initialize(entry, span, function_name:, var:, found:)
|
|
7
|
+
@function_name = function_name
|
|
8
|
+
@var = var
|
|
9
|
+
@found = found
|
|
10
|
+
super(entry:, span:)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message
|
|
14
|
+
case @found
|
|
15
|
+
in Type::Var
|
|
16
|
+
"`#{@function_name}` says `#{@var}` and `#{@found}` can be different " \
|
|
17
|
+
'types, but its body needs them to be the same'
|
|
18
|
+
else
|
|
19
|
+
"`#{@function_name}` says it works for any `#{@var}`, but its body only " \
|
|
20
|
+
"works when `#{@var}` is #{shown}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def label
|
|
25
|
+
'narrower than its signature'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def notes
|
|
29
|
+
[Jade::Diagnostics::Annotation[
|
|
30
|
+
:help,
|
|
31
|
+
'write the type the body needs in the signature, or make the body ' \
|
|
32
|
+
'work for any type',
|
|
33
|
+
]]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def shown
|
|
39
|
+
kept, renamed = @found
|
|
40
|
+
.unbound_vars
|
|
41
|
+
.uniq(&:id)
|
|
42
|
+
.partition { it.name && it.name != @var }
|
|
43
|
+
|
|
44
|
+
renamed
|
|
45
|
+
.zip(('a'..'z').to_a - [@var, *kept.map(&:name)])
|
|
46
|
+
.to_h { |(var, letter)| [var.id, Type.var(var.id, letter)] }
|
|
47
|
+
.then { Substitution[it].apply(@found).to_s }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -7,6 +7,7 @@ require 'jade/frontend/type_checking/error/case_of_branches_type_mismatch'
|
|
|
7
7
|
require 'jade/frontend/type_checking/error/derivation_failed'
|
|
8
8
|
require 'jade/frontend/type_checking/error/recursive_derivation'
|
|
9
9
|
require 'jade/frontend/type_checking/error/function_body_type_mismatch'
|
|
10
|
+
require 'jade/frontend/type_checking/error/narrowed_signature'
|
|
10
11
|
require 'jade/frontend/type_checking/error/function_call_type_mismatch'
|
|
11
12
|
require 'jade/frontend/type_checking/error/if_branch_type_mismatch'
|
|
12
13
|
require 'jade/frontend/type_checking/error/if_branches_type_mismatch'
|
|
@@ -24,5 +25,6 @@ require 'jade/frontend/type_checking/error/unresolved_constraint'
|
|
|
24
25
|
require 'jade/frontend/type_checking/error/implementation_type_mismatch'
|
|
25
26
|
require 'jade/frontend/type_checking/error/implementation_function_constraint'
|
|
26
27
|
require 'jade/frontend/type_checking/error/cross_module_requirement'
|
|
28
|
+
require 'jade/frontend/type_checking/error/narrowed_method_variable'
|
|
27
29
|
require 'jade/frontend/type_checking/error/undeclared_requirement'
|
|
28
30
|
require 'jade/frontend/type_checking/error/missing_implementation'
|
|
@@ -33,10 +33,13 @@ module Jade
|
|
|
33
33
|
[pattern_state, expr_result.constraints]
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
.
|
|
39
|
-
|
|
36
|
+
final_state
|
|
37
|
+
.defer_patterns([pattern], pattern.range, expr_result.type)
|
|
38
|
+
.unify_result(
|
|
39
|
+
expr_result.with(constraints: residual_cs),
|
|
40
|
+
expected.type,
|
|
41
|
+
&type_mismatch(state, node)
|
|
42
|
+
)
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
private
|
|
@@ -86,12 +86,7 @@ module Jade
|
|
|
86
86
|
patterns = branches.map(&:pattern)
|
|
87
87
|
type = result.apply(state.env.substitution).type
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
PatternAnalysis::Exhaustiveness.assert(patterns, node.range, state.env, type),
|
|
91
|
-
PatternAnalysis::Redundancy.assert(patterns, state.env, type),
|
|
92
|
-
]
|
|
93
|
-
.flatten
|
|
94
|
-
.then { state.add_errors(it) }
|
|
89
|
+
state.defer_patterns(patterns, node.range, type)
|
|
95
90
|
end
|
|
96
91
|
|
|
97
92
|
def mistyped_patterns?(node, state)
|
|
@@ -41,6 +41,7 @@ module Jade
|
|
|
41
41
|
function_name: node.name,
|
|
42
42
|
)
|
|
43
43
|
end
|
|
44
|
+
.then { |st| report_narrowing(st, node, symbol, registry, fn_type) }
|
|
44
45
|
.then do |st|
|
|
45
46
|
next st if st.env.bindings[symbol.qualified_name].is_a?(Scheme) && !st.skip_constraints
|
|
46
47
|
|
|
@@ -58,6 +59,26 @@ module Jade
|
|
|
58
59
|
end
|
|
59
60
|
.then { [it, Result.init(Type.unit)] }
|
|
60
61
|
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def report_narrowing(state, node, symbol, registry, fn_type)
|
|
66
|
+
Type
|
|
67
|
+
.from_symbol(registry.lookup(symbol), registry, state.env.var_gen)
|
|
68
|
+
.first
|
|
69
|
+
.then { Narrowing.against_declaration(it, state.env.substitution.apply(fn_type)) }
|
|
70
|
+
.then { it ? state.add_errors([narrowed(it, state, node)]) : state }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def narrowed((var, found), state, node)
|
|
74
|
+
Error::NarrowedSignature.new(
|
|
75
|
+
state.env.entry_name,
|
|
76
|
+
node.range,
|
|
77
|
+
function_name: node.name,
|
|
78
|
+
var: var.name,
|
|
79
|
+
found:,
|
|
80
|
+
)
|
|
81
|
+
end
|
|
61
82
|
end
|
|
62
83
|
end
|
|
63
84
|
end
|
|
@@ -36,6 +36,17 @@ module Jade
|
|
|
36
36
|
|
|
37
37
|
result
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
def type_mismatch(state, node)
|
|
41
|
+
->(error) do
|
|
42
|
+
Error::TypeMismatch.new(
|
|
43
|
+
state.env.entry_name,
|
|
44
|
+
node.range,
|
|
45
|
+
expected: error.expected,
|
|
46
|
+
actual: error.actual,
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
39
50
|
end
|
|
40
51
|
end
|
|
41
52
|
end
|
|
@@ -58,7 +58,7 @@ module Jade
|
|
|
58
58
|
Expected.check(expected_type),
|
|
59
59
|
interface_qname,
|
|
60
60
|
concrete_type,
|
|
61
|
-
iface_fn_type,
|
|
61
|
+
[iface_fn_type, t_var],
|
|
62
62
|
)
|
|
63
63
|
end
|
|
64
64
|
.then { [it, Result.init(Type.unit)] }
|
|
@@ -151,11 +151,19 @@ module Jade
|
|
|
151
151
|
end
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
def record_requirements(state, constraints, head_type,
|
|
154
|
+
def record_requirements(state, constraints, head_type, signature, interface_qname, impl_fn, fn_name)
|
|
155
|
+
signature => [sig_type, t_var]
|
|
155
156
|
substitution = state.env.substitution
|
|
156
157
|
head_vars = substitution.apply(head_type).unbound_vars.map(&:id).to_set
|
|
157
158
|
names = signature_names(sig_type, substitution)
|
|
158
159
|
|
|
160
|
+
narrowed = Narrowing.against_method(sig_type, t_var, head_vars, substitution)
|
|
161
|
+
unless narrowed.empty?
|
|
162
|
+
return state.add_errors(
|
|
163
|
+
narrowed.map { narrowing(state, impl_fn, interface_qname, fn_name, it) },
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
159
167
|
free = constraints
|
|
160
168
|
.map { substitution.apply(it) }
|
|
161
169
|
.select { it.type.is_a?(Type::Var) && !head_vars.include?(it.type.id) }
|
|
@@ -171,6 +179,19 @@ module Jade
|
|
|
171
179
|
)
|
|
172
180
|
end
|
|
173
181
|
|
|
182
|
+
def narrowing(state, impl_fn, interface_qname, fn_name, narrowed)
|
|
183
|
+
narrowed => [var_name, bound_to]
|
|
184
|
+
|
|
185
|
+
Error::NarrowedMethodVariable.new(
|
|
186
|
+
state.env.entry_name,
|
|
187
|
+
impl_fn.range,
|
|
188
|
+
interface: interface_qname,
|
|
189
|
+
fn_name:,
|
|
190
|
+
var_name:,
|
|
191
|
+
bound_to: bound_to.to_s,
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
|
|
174
195
|
def signature_names(sig_type, substitution)
|
|
175
196
|
sig_type
|
|
176
197
|
.unbound_vars
|
|
@@ -54,10 +54,7 @@ module Jade
|
|
|
54
54
|
in AST::Pattern::Binding | AST::Pattern::Wildcard
|
|
55
55
|
acc
|
|
56
56
|
else
|
|
57
|
-
|
|
58
|
-
PatternAnalysis::Exhaustiveness
|
|
59
|
-
.assert([p], p.range, acc.env, concrete_t)
|
|
60
|
-
.then { acc.add_errors(it) }
|
|
57
|
+
acc.defer_patterns([p], p.range, t)
|
|
61
58
|
end
|
|
62
59
|
end
|
|
63
60
|
|
|
@@ -12,7 +12,14 @@ module Jade
|
|
|
12
12
|
if items.empty?
|
|
13
13
|
Type.list.apply([state.fresh])
|
|
14
14
|
.then { Result.init(it) }
|
|
15
|
-
.then
|
|
15
|
+
.then do
|
|
16
|
+
return state.unify_result(
|
|
17
|
+
it,
|
|
18
|
+
expected.type,
|
|
19
|
+
expected.rigid_vars,
|
|
20
|
+
&type_mismatch(state, node)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
16
23
|
end
|
|
17
24
|
|
|
18
25
|
head, *rest = items
|
|
@@ -29,7 +36,14 @@ module Jade
|
|
|
29
36
|
|
|
30
37
|
Result
|
|
31
38
|
.init(Type.list.apply([items_result.type]), items_result.constraints)
|
|
32
|
-
.then
|
|
39
|
+
.then do
|
|
40
|
+
items_state.unify_result(
|
|
41
|
+
it,
|
|
42
|
+
expected.type,
|
|
43
|
+
expected.rigid_vars,
|
|
44
|
+
&type_mismatch(items_state, node)
|
|
45
|
+
)
|
|
46
|
+
end
|
|
33
47
|
end
|
|
34
48
|
|
|
35
49
|
private
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
# Unification is symmetric, so pinning a variable a declaration
|
|
5
|
+
# quantifies simply binds it, and a call site instantiating that
|
|
6
|
+
# declaration afresh never sees that it did. Two places can do it: a
|
|
7
|
+
# function's own body, and an implementation against the method it
|
|
8
|
+
# implements.
|
|
9
|
+
module Narrowing
|
|
10
|
+
extend self
|
|
11
|
+
|
|
12
|
+
# Against a fresh copy of the declaration, since a narrowing collected
|
|
13
|
+
# in an earlier round is already part of the generalised type and the
|
|
14
|
+
# substitution no longer shows it.
|
|
15
|
+
def against_declaration(declared, actual)
|
|
16
|
+
pairs(declared, actual).then do |found|
|
|
17
|
+
found.find { |(_, type)| !type.is_a?(Type::Var) } || merged(found)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# An implementation is the other place a quantified variable can be
|
|
23
|
+
# pinned: the method promises a variable each call site settles, and
|
|
24
|
+
# unification will happily bind it to the implementation's own.
|
|
25
|
+
#
|
|
26
|
+
# The interface's own parameter is exempt — binding that to the
|
|
27
|
+
# implemented type is what an implementation is.
|
|
28
|
+
def against_method(sig_type, t_var, head_vars, substitution)
|
|
29
|
+
sig_type
|
|
30
|
+
.unbound_vars
|
|
31
|
+
.reject { it.id == t_var.id }
|
|
32
|
+
.filter_map { pinned(it, substitution.apply(it), head_vars) }
|
|
33
|
+
.uniq(&:first)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# Pinned to one of the implemented type's own variables is the common
|
|
39
|
+
# case, and naming it reads as nonsense when the two are spelled alike.
|
|
40
|
+
def pinned(var, applied, head_vars)
|
|
41
|
+
case applied
|
|
42
|
+
in Type::Var(id:) if !head_vars.include?(id) then nil
|
|
43
|
+
in Type::Var then [var.name, 'the type this implements']
|
|
44
|
+
else [var.name, "`#{applied}`"]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def merged(found)
|
|
49
|
+
found
|
|
50
|
+
.uniq { |(var, _)| var.id }
|
|
51
|
+
.group_by { |(_, type)| type.id }
|
|
52
|
+
.values
|
|
53
|
+
.find { it.size > 1 }
|
|
54
|
+
&.then { |((var, _), (other, _))| [var, other] }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def pairs(declared, actual)
|
|
58
|
+
case declared
|
|
59
|
+
in Type::Var
|
|
60
|
+
[[declared, actual]]
|
|
61
|
+
|
|
62
|
+
in Type::Application(constructor:, args:)
|
|
63
|
+
pairs(constructor, actual.constructor) + zipped(args, actual.args)
|
|
64
|
+
|
|
65
|
+
in Type::Function(args:, return_type:)
|
|
66
|
+
zipped(args, actual.args) + pairs(return_type, actual.return_type)
|
|
67
|
+
|
|
68
|
+
in Type::AnonymousRecord(fields:, row_var:)
|
|
69
|
+
fields.flat_map { |name, type| pairs(type, actual.fields.fetch(name)) } +
|
|
70
|
+
row_pairs(row_var, fields, actual)
|
|
71
|
+
|
|
72
|
+
else
|
|
73
|
+
[]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def zipped(declared, actual)
|
|
78
|
+
declared
|
|
79
|
+
.zip(actual)
|
|
80
|
+
.flat_map { |(d, a)| pairs(d, a) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def row_pairs(row_var, fields, actual)
|
|
84
|
+
return [] unless row_var
|
|
85
|
+
|
|
86
|
+
(actual.fields.keys - fields.keys).empty? && actual.row_var ?
|
|
87
|
+
pairs(row_var, actual.row_var) :
|
|
88
|
+
[[row_var, actual]]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
# The coverage and redundancy of every `case` in a definition, decided
|
|
5
|
+
# against the substitution inference ended with rather than the one in
|
|
6
|
+
# scope where the patterns were written.
|
|
7
|
+
module PatternChecks
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def run(state)
|
|
11
|
+
state
|
|
12
|
+
.pattern_checks
|
|
13
|
+
.flat_map { |(patterns, range, type)| errors_for(patterns, range, type, state.env) }
|
|
14
|
+
.then { state.add_errors(it) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def errors_for(patterns, range, type, env)
|
|
20
|
+
resolved = env.substitution.apply(type)
|
|
21
|
+
|
|
22
|
+
[
|
|
23
|
+
PatternAnalysis::Exhaustiveness.assert(patterns, range, env, resolved),
|
|
24
|
+
PatternAnalysis::Redundancy.assert(patterns, env, resolved),
|
|
25
|
+
].flatten
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
module Jade
|
|
2
2
|
module Frontend
|
|
3
3
|
module TypeChecking
|
|
4
|
-
State = Data.define(:env, :errors, :skip_constraints, :impl_requirements) do
|
|
4
|
+
State = Data.define(:env, :errors, :skip_constraints, :impl_requirements, :pattern_checks) do
|
|
5
5
|
def self.init(env, skip_constraints: false)
|
|
6
|
-
new(env, [], skip_constraints, {})
|
|
6
|
+
new(env, [], skip_constraints, {}, [])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Coverage is decided once inference is done: a pattern in a lambda is
|
|
10
|
+
# checked before the call that gives the lambda's parameter its type,
|
|
11
|
+
# so the subject can still be a variable here.
|
|
12
|
+
def defer_patterns(patterns, range, type)
|
|
13
|
+
with(pattern_checks: pattern_checks + [[patterns, range, type]])
|
|
7
14
|
end
|
|
8
15
|
|
|
9
16
|
def require_impl(key, constraints)
|
|
@@ -7,6 +7,8 @@ require 'jade/frontend/type_checking/error'
|
|
|
7
7
|
require 'jade/frontend/type_checking/expected'
|
|
8
8
|
require 'jade/frontend/type_checking/inference'
|
|
9
9
|
require 'jade/frontend/type_checking/loader'
|
|
10
|
+
require 'jade/frontend/type_checking/narrowing'
|
|
11
|
+
require 'jade/frontend/type_checking/pattern_checks'
|
|
10
12
|
require 'jade/frontend/type_checking/port_resolution'
|
|
11
13
|
require 'jade/frontend/type_checking/requirements'
|
|
12
14
|
require 'jade/frontend/type_checking/result'
|
|
@@ -27,7 +29,7 @@ module Jade
|
|
|
27
29
|
.load(entry, registry)
|
|
28
30
|
.then { collect_constraints(entry, registry, it) }
|
|
29
31
|
.then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
|
|
30
|
-
.then { |state, _| Requirements.reconcile(entry, registry, state) }
|
|
32
|
+
.then { |state, _| Requirements.reconcile(entry, registry, PatternChecks.run(state)) }
|
|
31
33
|
.then { |amended, state| complete(amended, state, registry) }
|
|
32
34
|
.and_then { PortResolution.resolve(it, registry) }
|
|
33
35
|
end
|
|
@@ -126,11 +128,6 @@ module Jade
|
|
|
126
128
|
.is_a?(Symbol::InteropFunction)
|
|
127
129
|
end
|
|
128
130
|
|
|
129
|
-
def check_repl(node, registry, env = Env.new)
|
|
130
|
-
check_node(node, registry, env, Expected.infer(env.fresh))
|
|
131
|
-
.to_result
|
|
132
|
-
end
|
|
133
|
-
|
|
134
131
|
def check_node(node, registry, state, expected_type)
|
|
135
132
|
case node
|
|
136
133
|
in AST::Body then Inference::Body
|
data/lib/jade/frontend.rb
CHANGED
|
@@ -46,28 +46,6 @@ module Jade
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def run_repl(ast, registry, current_entry, scope, env, var_gen)
|
|
50
|
-
registry ||= registry_with_basics
|
|
51
|
-
current_entry ||= entry_with_basics('JadeRepl')
|
|
52
|
-
scope ||= SemanticAnalysis::Scope.new
|
|
53
|
-
env ||= TypeChecking::Env.new
|
|
54
|
-
var_gen ||= TypeChecking::VarGen.new
|
|
55
|
-
|
|
56
|
-
ForwardDeclaration
|
|
57
|
-
.declare(ast, registry, current_entry)
|
|
58
|
-
.then { |entry| FixityFixer.fix(ast).then { [it, entry] } }
|
|
59
|
-
.then do |fixed_ast, updated_entry|
|
|
60
|
-
updated_registry = registry.update_module(updated_entry)
|
|
61
|
-
SemanticAnalysis
|
|
62
|
-
.analyze_repl(fixed_ast, updated_registry, scope, updated_entry)
|
|
63
|
-
.and_then do |(enhanced_ast, new_scope)|
|
|
64
|
-
enhanced_ast = Desugaring.desugar_resolved(enhanced_ast, updated_registry)
|
|
65
|
-
TypeChecking.check_repl(enhanced_ast, updated_registry, env, var_gen)
|
|
66
|
-
.map { |type, new_env| [enhanced_ast, type, updated_registry, updated_entry, new_scope, new_env] }
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
49
|
def run_up_to_semantic_analysis(ast)
|
|
72
50
|
registry, current_entry = entry_with_basics(ast)
|
|
73
51
|
|
data/lib/jade/parsing/error.rb
CHANGED
|
@@ -52,6 +52,21 @@ module Jade
|
|
|
52
52
|
def message
|
|
53
53
|
"#{context_prefix}Unexpected end of input, expected #{expected}"
|
|
54
54
|
end
|
|
55
|
+
|
|
56
|
+
def to_diagnostic(registry = nil, source: nil)
|
|
57
|
+
super.then do |diagnostic|
|
|
58
|
+
diagnostic.primary => { source: labelled, span: }
|
|
59
|
+
next diagnostic unless labelled && span.nil?
|
|
60
|
+
|
|
61
|
+
diagnostic.with(primary: diagnostic.primary.with(span: last_character(labelled.text)))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def last_character(text)
|
|
68
|
+
text.b.rstrip.bytesize.then { it.zero? ? nil : (it - 1)...it }
|
|
69
|
+
end
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
class UnexpectedTokenError < Error
|
data/lib/jade/registry.rb
CHANGED
data/lib/jade/stdlib/compiled.rb
CHANGED
|
@@ -39,7 +39,14 @@ module Jade
|
|
|
39
39
|
def resolve_imports(entry)
|
|
40
40
|
imports
|
|
41
41
|
.reduce(entry) do |acc, stdlib|
|
|
42
|
-
ImportEntry
|
|
42
|
+
ImportEntry
|
|
43
|
+
.new(
|
|
44
|
+
module_name: stdlib.entry.name,
|
|
45
|
+
alias: stdlib.entry.name,
|
|
46
|
+
unqualified_symbols: stdlib.default_imports,
|
|
47
|
+
qualified_symbols: stdlib.entry.exposes,
|
|
48
|
+
implicit: true,
|
|
49
|
+
)
|
|
43
50
|
.then { acc.import(it) }
|
|
44
51
|
end
|
|
45
52
|
end
|
data/lib/jade/stdlib/dict.rb
CHANGED
|
@@ -69,6 +69,18 @@ module Jade
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
function(
|
|
73
|
+
:show_with,
|
|
74
|
+
{ show_key: 'k -> String', show_value: 'v -> String', dict: 'Dict(k, v)' },
|
|
75
|
+
'String',
|
|
76
|
+
private: true,
|
|
77
|
+
) do |show_key, show_value, dict|
|
|
78
|
+
dict.hash
|
|
79
|
+
.map { |k, v| "#{show_key.call(k)}: #{show_value.call(v)}" }
|
|
80
|
+
.join(', ')
|
|
81
|
+
.then { "Dict(#{it})" }
|
|
82
|
+
end
|
|
83
|
+
|
|
72
84
|
function(:keys, { dict: 'Dict(k, v)' }, 'List(k)')
|
|
73
85
|
function(:values, { dict: 'Dict(k, v)' }, 'List(v)')
|
|
74
86
|
function(:to_list, { dict: 'Dict(k, v)' }, 'List(Tuple2(k, v))')
|
|
@@ -285,7 +285,14 @@ module Jade
|
|
|
285
285
|
# TODO: This is the same code from stdlib that auto imports stuff.
|
|
286
286
|
imports
|
|
287
287
|
.reduce(entry) do |acc, stdlib|
|
|
288
|
-
ImportEntry
|
|
288
|
+
ImportEntry
|
|
289
|
+
.new(
|
|
290
|
+
module_name: stdlib.entry.name,
|
|
291
|
+
alias: stdlib.entry.name,
|
|
292
|
+
unqualified_symbols: stdlib.default_imports,
|
|
293
|
+
qualified_symbols: stdlib.entry.exposes,
|
|
294
|
+
implicit: true,
|
|
295
|
+
)
|
|
289
296
|
.then { acc.import(it) }
|
|
290
297
|
end
|
|
291
298
|
end
|
data/lib/jade/stdlib/set.rb
CHANGED
|
@@ -24,6 +24,18 @@ module Jade
|
|
|
24
24
|
constraints: [['Basics.Eq', 'a']],
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
function(
|
|
28
|
+
:show_with,
|
|
29
|
+
{ show_value: 'a -> String', set: 'Set(a)' },
|
|
30
|
+
'String',
|
|
31
|
+
private: true,
|
|
32
|
+
) do |show_value, set|
|
|
33
|
+
set.hash.keys
|
|
34
|
+
.map { show_value.call(it) }
|
|
35
|
+
.join(', ')
|
|
36
|
+
.then { "Set(#{it})" }
|
|
37
|
+
end
|
|
38
|
+
|
|
27
39
|
function(:"empty?", { set: 'Set(a)' }, 'Bool')
|
|
28
40
|
function(:size, { set: 'Set(a)' }, 'Int')
|
|
29
41
|
|
data/lib/jade/stdlib.rb
CHANGED
|
@@ -107,7 +107,14 @@ module Jade
|
|
|
107
107
|
def add_imports(entry)
|
|
108
108
|
(STDLIBS - EXTENSIONS)
|
|
109
109
|
.reduce(entry) do |acc, stdlib|
|
|
110
|
-
ImportEntry
|
|
110
|
+
ImportEntry
|
|
111
|
+
.new(
|
|
112
|
+
module_name: stdlib.entry.name,
|
|
113
|
+
alias: stdlib.entry.name,
|
|
114
|
+
unqualified_symbols: stdlib.default_imports,
|
|
115
|
+
qualified_symbols: stdlib.entry.exposes,
|
|
116
|
+
implicit: true,
|
|
117
|
+
)
|
|
111
118
|
.then { acc.import(it) }
|
|
112
119
|
end
|
|
113
120
|
end
|
data/lib/jade/symbol.rb
CHANGED
data/lib/jade/version.rb
CHANGED