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
data/lib/jade/formatter/calls.rb
CHANGED
|
@@ -12,7 +12,10 @@ module Jade
|
|
|
12
12
|
|
|
13
13
|
callee_str = format_node(callee, source:)
|
|
14
14
|
args_strs = args.map { format_node(it, source:) }
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
Calls::Call[callee_str, args_strs, indent].then do |call|
|
|
17
|
+
Calls.render(call, trailing_comma, opened_last: Calls.render_open(args.last, source))
|
|
18
|
+
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
|
|
@@ -25,7 +28,10 @@ module Jade
|
|
|
25
28
|
|
|
26
29
|
callee_str = format_node(callee, source:)
|
|
27
30
|
field_strs = fields.map { "#{it.key}: #{format_node(it.value, source:)}" }
|
|
28
|
-
|
|
31
|
+
|
|
32
|
+
Calls::Call[callee_str, field_strs, indent].then do |call|
|
|
33
|
+
Calls.render(call, trailing_comma, opened_last: Calls.render_open_field(fields.last, source))
|
|
34
|
+
end
|
|
29
35
|
end
|
|
30
36
|
end
|
|
31
37
|
|
|
@@ -33,19 +39,70 @@ module Jade
|
|
|
33
39
|
extend self
|
|
34
40
|
extend Helper
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
# A trailing block-shaped argument reads as the call's body, the way a
|
|
43
|
+
# Ruby block does. Only these three grow a body worth hugging.
|
|
44
|
+
HUGGABLE = [AST::Lambda, AST::List, AST::RecordLiteral].freeze
|
|
45
|
+
|
|
46
|
+
# A rendered call, before the decision of how to lay it out: the callee,
|
|
47
|
+
# its already-formatted arguments, and the column they sit at.
|
|
48
|
+
Call = Data.define(:callee, :items, :indent)
|
|
49
|
+
|
|
50
|
+
def huggable?(node)
|
|
51
|
+
HUGGABLE.any? { node.is_a?(it) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_open(node, source)
|
|
55
|
+
return nil unless huggable?(node)
|
|
56
|
+
|
|
57
|
+
dispatch_for(node).format(node, indent: 0, source:, open: true)
|
|
58
|
+
end
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.join("\n")
|
|
60
|
+
def render_open_field(field, source)
|
|
61
|
+
render_open(field&.value, source)&.then { "#{field.key}: #{it}" }
|
|
62
|
+
end
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
def render(call, trailing_comma, opened_last: nil)
|
|
65
|
+
case [trailing_comma, inline(call), opened_last]
|
|
66
|
+
in [false, String => fits, _] then fits
|
|
67
|
+
in [false, nil, String => last] then hug_last(call, last) || explode(call)
|
|
68
|
+
else explode(call)
|
|
47
69
|
end
|
|
48
70
|
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def inline(call)
|
|
75
|
+
"#{call.callee}(#{call.items.join(', ')})"
|
|
76
|
+
.then { too_long?(it, call.indent) ? nil : it.then(&and_indent(call.indent)) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# An earlier multi-line argument would leave the head straddling lines.
|
|
80
|
+
def hug_last(call, last)
|
|
81
|
+
case [call.items[..-2], last.split("\n")]
|
|
82
|
+
in [[*head], [_]] then nil
|
|
83
|
+
in [[*head], _] if head.any? { it.include?("\n") } then nil
|
|
84
|
+
in [[*head], [opener, *rest]]
|
|
85
|
+
"#{call.callee}(#{(head + [opener]).join(', ')}"
|
|
86
|
+
.then { too_long?(it, call.indent) ? nil : hug_lines(it, rest, call.indent) }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def hug_lines(opening, rest, indent)
|
|
91
|
+
[opening, *rest[..-2], "#{rest.last})"]
|
|
92
|
+
.join("\n")
|
|
93
|
+
.then(&and_indent(indent))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def explode(call)
|
|
97
|
+
call.items
|
|
98
|
+
.map { "#{it.then(&and_indent(call.indent + 1))}," }
|
|
99
|
+
.join("\n")
|
|
100
|
+
.then { "#{indent_callee(call)}(\n#{it}\n#{INDENT * call.indent})" }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def indent_callee(call)
|
|
104
|
+
call.callee.then(&and_indent(call.indent))
|
|
105
|
+
end
|
|
49
106
|
end
|
|
50
107
|
end
|
|
51
108
|
end
|
|
@@ -22,12 +22,12 @@ module Jade
|
|
|
22
22
|
extend self
|
|
23
23
|
extend Helper
|
|
24
24
|
|
|
25
|
-
def format(node, indent:, source:)
|
|
25
|
+
def format(node, indent:, source:, open: false)
|
|
26
26
|
node => AST::List(items:, trailing_comma:)
|
|
27
27
|
|
|
28
28
|
format_delimited(
|
|
29
29
|
items.map { format_node(it, source:) },
|
|
30
|
-
'[', ']', trailing_comma, indent,
|
|
30
|
+
'[', ']', trailing_comma || open, indent,
|
|
31
31
|
)
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -36,13 +36,13 @@ module Jade
|
|
|
36
36
|
extend self
|
|
37
37
|
extend Helper
|
|
38
38
|
|
|
39
|
-
def format(node, indent:, source:)
|
|
39
|
+
def format(node, indent:, source:, open: false)
|
|
40
40
|
node => AST::RecordLiteral(fields:, trailing_comma:)
|
|
41
41
|
|
|
42
42
|
field_strs = fields.map { "#{it.key}: #{format_node(it.value, source:)}" }
|
|
43
43
|
inline = "{ #{field_strs.join(', ')} }"
|
|
44
44
|
|
|
45
|
-
if trailing_comma || too_long?(inline, indent)
|
|
45
|
+
if trailing_comma || open || too_long?(inline, indent)
|
|
46
46
|
inner = field_strs
|
|
47
47
|
.map { "#{it.then(&and_indent(indent + 1))}," }
|
|
48
48
|
.join("\n")
|
|
@@ -68,24 +68,22 @@ module Jade
|
|
|
68
68
|
"(#{type_params.map(&:name).join(', ')})"
|
|
69
69
|
header = "struct #{name}#{params_str} ="
|
|
70
70
|
|
|
71
|
-
record_type
|
|
72
|
-
|
|
73
|
-
if fields.size > 1
|
|
74
|
-
format_multiline(header, fields, row_var, indent)
|
|
75
|
-
else
|
|
76
|
-
"#{header} #{format_type(record_type)}".then(&and_indent(indent))
|
|
77
|
-
end
|
|
71
|
+
format_record_declaration(header, record_type, indent)
|
|
78
72
|
end
|
|
73
|
+
end
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
module TypeAliasDeclaration
|
|
76
|
+
extend self
|
|
77
|
+
extend Helper
|
|
78
|
+
|
|
79
|
+
def format(node, indent:, source:)
|
|
80
|
+
node => AST::TypeAliasDeclaration(name:, type_params:, body_type:)
|
|
81
|
+
|
|
82
|
+
params_str = type_params.empty? ?
|
|
83
|
+
"" :
|
|
84
|
+
"(#{type_params.map(&:name).join(', ')})"
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
.call("#{header} #{open_brace}")
|
|
88
|
-
.then { "#{it}\n#{fields_str}\n#{INDENT * indent}}" }
|
|
86
|
+
format_record_declaration("type alias #{name}#{params_str} =", body_type, indent)
|
|
89
87
|
end
|
|
90
88
|
end
|
|
91
89
|
|
|
@@ -30,6 +30,7 @@ module Jade
|
|
|
30
30
|
in AST::TypeDeclaration then TypeDeclaration
|
|
31
31
|
in AST::VariantDeclaration then VariantDeclaration
|
|
32
32
|
in AST::StructDeclaration then StructDeclaration
|
|
33
|
+
in AST::TypeAliasDeclaration then TypeAliasDeclaration
|
|
33
34
|
in AST::ImportDeclaration then ImportDeclaration
|
|
34
35
|
in AST::InteropImportDeclaration then InteropImportDeclaration
|
|
35
36
|
in AST::InteropFunction then InteropFunction
|
|
@@ -113,6 +114,25 @@ module Jade
|
|
|
113
114
|
Pattern.format(node, source:)
|
|
114
115
|
end
|
|
115
116
|
|
|
117
|
+
# A record body past one field goes multi-line, so a `type alias` over a
|
|
118
|
+
# record breaks the same way as the `struct` beside it.
|
|
119
|
+
def format_record_declaration(header, body, indent)
|
|
120
|
+
case body
|
|
121
|
+
in AST::TypeRecord(fields:, row_var:) if fields.size > 1
|
|
122
|
+
open_brace = row_var ? "{ #{row_var.name} |" : "{"
|
|
123
|
+
fields_str = fields
|
|
124
|
+
.map { |k, v| "#{k}: #{format_type(v)}".then(&and_indent(indent + 1)) }
|
|
125
|
+
.join(",\n")
|
|
126
|
+
|
|
127
|
+
and_indent(indent)
|
|
128
|
+
.call("#{header} #{open_brace}")
|
|
129
|
+
.then { "#{it}\n#{fields_str}\n#{INDENT * indent}}" }
|
|
130
|
+
|
|
131
|
+
else
|
|
132
|
+
"#{header} #{format_type(body)}".then(&and_indent(indent))
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
116
136
|
def format_type(node)
|
|
117
137
|
Type.format(node)
|
|
118
138
|
end
|
|
@@ -12,6 +12,11 @@ module Jade
|
|
|
12
12
|
format_pipe_chain(node, indent, source:)
|
|
13
13
|
when '++'
|
|
14
14
|
format_concat_chain(node, indent, source:)
|
|
15
|
+
# Spelled tight, the way it is in a pattern. `0 .. n` is nobody's
|
|
16
|
+
# idea of a range.
|
|
17
|
+
when '..'
|
|
18
|
+
"#{format_node(left, source:)}..#{format_node(right, source:)}"
|
|
19
|
+
.then(&and_indent(indent))
|
|
15
20
|
else
|
|
16
21
|
"#{format_node(left, source:)} #{operator.value} #{format_node(right, source:)}"
|
|
17
22
|
.then(&and_indent(indent))
|
|
@@ -58,11 +63,21 @@ module Jade
|
|
|
58
63
|
# Emit a chain ladder: head on its own line, each subsequent
|
|
59
64
|
# operand prefixed by `op` indented one level deeper.
|
|
60
65
|
def format_ladder(chain, op, indent, source:)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
chain[1..]
|
|
67
|
+
.map { format_node(it, source:) }
|
|
68
|
+
.map { continued(it, indent + 1) }
|
|
69
|
+
.map { "#{INDENT * (indent + 1)}#{op} #{it}" }
|
|
70
|
+
.then { [format_node(chain.first, indent:, source:), *it] }
|
|
71
|
+
.join("\n")
|
|
72
|
+
end
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
# What follows an operand's first line belongs under the operator that
|
|
75
|
+
# introduced it, not at column zero.
|
|
76
|
+
def continued(operand, indent)
|
|
77
|
+
operand
|
|
78
|
+
.split("\n")
|
|
79
|
+
.then { |(first, *rest)| [first, *rest.map { "#{INDENT * indent}#{it}" }] }
|
|
80
|
+
.join("\n")
|
|
66
81
|
end
|
|
67
82
|
end
|
|
68
83
|
end
|
|
@@ -15,23 +15,35 @@ module Jade
|
|
|
15
15
|
AST::RecordUpdateSugar, AST::RecordAccessSugar,
|
|
16
16
|
].freeze
|
|
17
17
|
|
|
18
|
-
def format(node, indent:, source:)
|
|
18
|
+
def format(node, indent:, source:, open: false)
|
|
19
19
|
node => AST::Lambda(params:, body:)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if inline_body?(body)
|
|
24
|
-
"#{head} { #{format_node(body.expressions.first, source:)} }"
|
|
25
|
-
.then(&and_indent(indent))
|
|
26
|
-
else
|
|
27
|
-
[
|
|
28
|
-
"#{head} {".then(&and_indent(indent)),
|
|
29
|
-
format_node(body, indent: indent + 1, source:),
|
|
30
|
-
"}".then(&and_indent(indent)),
|
|
31
|
-
].join("\n")
|
|
21
|
+
format_head(params).then do |head|
|
|
22
|
+
(inline(head, body, indent, source) unless open) || block(head, body, indent, source)
|
|
32
23
|
end
|
|
33
24
|
end
|
|
34
25
|
|
|
26
|
+
# An inline body that broke anyway leaves `-> { Expect.equal(` hanging.
|
|
27
|
+
def inline(head, body, indent, source)
|
|
28
|
+
return nil unless inline_body?(body)
|
|
29
|
+
|
|
30
|
+
body.expressions.first
|
|
31
|
+
.then { "#{head} { #{format_node(it, source:)} }" }
|
|
32
|
+
.then { fits?(it, indent) ? it.then(&and_indent(indent)) : nil }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fits?(line, indent)
|
|
36
|
+
!line.include?("\n") && !too_long?(line, indent)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def block(head, body, indent, source)
|
|
40
|
+
[
|
|
41
|
+
"#{head} {".then(&and_indent(indent)),
|
|
42
|
+
format_node(body, indent: indent + 1, source:),
|
|
43
|
+
"}".then(&and_indent(indent)),
|
|
44
|
+
].join("\n")
|
|
45
|
+
end
|
|
46
|
+
|
|
35
47
|
def format_head(params)
|
|
36
48
|
return "->" if params.empty?
|
|
37
49
|
|
data/lib/jade/formatter/type.rb
CHANGED
|
@@ -26,11 +26,9 @@ module Jade
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
in AST::TypeFunction(params:, return_type:)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"#{params_str} -> #{format_atom(return_type)}"
|
|
29
|
+
params
|
|
30
|
+
.then { it.empty? ? '()' : it.map { |p| format_atom(p) }.join(', ') }
|
|
31
|
+
.then { "#{it} -> #{format_atom(return_type)}" }
|
|
34
32
|
|
|
35
33
|
in AST::TypeRecord(fields:, row_var:)
|
|
36
34
|
fields_str = fields.map { |k, v| "#{k}: #{format(v)}" }.join(", ")
|
|
@@ -7,29 +7,52 @@ module Jade
|
|
|
7
7
|
extend self
|
|
8
8
|
extend Codegen::Helpers
|
|
9
9
|
|
|
10
|
-
def lift(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
def lift(node)
|
|
11
|
+
case node
|
|
12
|
+
in AST::FunctionCall(args:)
|
|
13
|
+
fill(node, args) { |filled| node.with(args: filled) }
|
|
14
|
+
|
|
15
|
+
in AST::RecordLiteral(fields:)
|
|
16
|
+
fill(node, fields.map(&:value)) do |filled|
|
|
17
|
+
fields
|
|
18
|
+
.zip(filled)
|
|
19
|
+
.map { |field, value| field.with(value:) }
|
|
20
|
+
.then { node.with(fields: it) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def fill(node, slots, &rebuild)
|
|
28
|
+
return node unless slots.any?(AST::Placeholder)
|
|
29
|
+
|
|
30
|
+
named(slots)
|
|
31
|
+
.then { |filled, names| [rebuild.call(filled), names] }
|
|
32
|
+
.then { |body, names| wrap_in_lambdas(body, names, node.range) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def named(slots)
|
|
36
|
+
slots.reduce([[], []]) do |(filled, names), slot|
|
|
37
|
+
case slot
|
|
38
|
+
in AST::Placeholder
|
|
39
|
+
param_synthetic_name(names.size)
|
|
40
|
+
.then { [filled + [AST::VariableReference[it, nil]], names + [it]] }
|
|
41
|
+
|
|
42
|
+
else
|
|
43
|
+
[filled + [slot], names]
|
|
44
|
+
end
|
|
24
45
|
end
|
|
46
|
+
end
|
|
25
47
|
|
|
48
|
+
def wrap_in_lambdas(body, names, range)
|
|
26
49
|
names
|
|
27
50
|
.reverse
|
|
28
|
-
.reduce(
|
|
51
|
+
.reduce(body) do |inner, name|
|
|
29
52
|
AST::Lambda[
|
|
30
53
|
[AST::Pattern::Binding[name, nil]],
|
|
31
|
-
AST::Body[[
|
|
32
|
-
|
|
54
|
+
AST::Body[[inner], nil],
|
|
55
|
+
range,
|
|
33
56
|
]
|
|
34
57
|
end
|
|
35
58
|
end
|
|
@@ -38,6 +38,20 @@ module Jade
|
|
|
38
38
|
end
|
|
39
39
|
.then { desugar(it) }
|
|
40
40
|
|
|
41
|
+
# There is no `Basics.(..)` to resolve to: the operator's result
|
|
42
|
+
# type lives in Range, and Basics is loaded before it.
|
|
43
|
+
in AST::InfixOperator(value: '..')
|
|
44
|
+
AST::FunctionCall.new(
|
|
45
|
+
callee: AST::MemberAccess[
|
|
46
|
+
AST::ConstructorReference['Range', operator.range],
|
|
47
|
+
AST::VariableReference['between', operator.range],
|
|
48
|
+
operator.range,
|
|
49
|
+
],
|
|
50
|
+
args: [desugar(left), desugar(right)],
|
|
51
|
+
infix: operator,
|
|
52
|
+
range: node.range,
|
|
53
|
+
)
|
|
54
|
+
|
|
41
55
|
else
|
|
42
56
|
AST::FunctionCall.new(
|
|
43
57
|
callee: AST::VariableReference["(#{operator.value})", operator.range],
|
|
@@ -98,7 +112,10 @@ module Jade
|
|
|
98
112
|
.with(items: items.map { desugar(it) })
|
|
99
113
|
|
|
100
114
|
in AST::RecordLiteral(fields:)
|
|
101
|
-
fields
|
|
115
|
+
fields
|
|
116
|
+
.map { desugar(it) }
|
|
117
|
+
.then { node.with(fields: it) }
|
|
118
|
+
.then { Placeholder.lift(it) }
|
|
102
119
|
|
|
103
120
|
in AST::RecordUpdate(fields:)
|
|
104
121
|
fields.map { desugar(it) }.then { node.with(fields: it) }
|
|
@@ -130,12 +147,14 @@ module Jade
|
|
|
130
147
|
end
|
|
131
148
|
|
|
132
149
|
in AST::Tuple(items:)
|
|
133
|
-
AST::FunctionCall
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
AST::FunctionCall
|
|
151
|
+
.new(
|
|
152
|
+
callee: AST::ConstructorReference["Tuple.Tuple#{items.size}", node.range],
|
|
153
|
+
args: items.map { desugar(it) },
|
|
154
|
+
infix: false,
|
|
155
|
+
range: node.range,
|
|
156
|
+
)
|
|
157
|
+
.then { Placeholder.lift(it) }
|
|
139
158
|
|
|
140
159
|
in AST::Pattern::Tuple(patterns:)
|
|
141
160
|
AST::ConstructorReference[
|
|
@@ -174,9 +193,10 @@ module Jade
|
|
|
174
193
|
in AST::Literal | AST::CharLiteral | AST::VariableReference | AST::ConstructorReference |
|
|
175
194
|
AST::TypeDeclaration | AST::ImportDeclaration |
|
|
176
195
|
AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard |
|
|
196
|
+
AST::Pattern::Range |
|
|
177
197
|
AST::Pattern::Record | AST::InteropImportDeclaration | AST::StructDeclaration |
|
|
178
|
-
AST::
|
|
179
|
-
AST::CurriedConstructor
|
|
198
|
+
AST::TypeAliasDeclaration | AST::QualifiedAccess | AST::Placeholder |
|
|
199
|
+
AST::InterfaceDeclaration | AST::CurriedConstructor
|
|
180
200
|
|
|
181
201
|
node
|
|
182
202
|
end
|
|
@@ -51,6 +51,7 @@ module Jade
|
|
|
51
51
|
'>=' => Fixity[4, :none],
|
|
52
52
|
'>' => Fixity[4, :none],
|
|
53
53
|
'<' => Fixity[4, :none],
|
|
54
|
+
'..' => Fixity[5, :none],
|
|
54
55
|
'&&' => Fixity[3, :right],
|
|
55
56
|
'||' => Fixity[2, :right],
|
|
56
57
|
}.freeze
|
|
@@ -97,8 +98,9 @@ module Jade
|
|
|
97
98
|
.then { node.with(target: it) }
|
|
98
99
|
|
|
99
100
|
in AST::CaseOf(expression:, branches:)
|
|
100
|
-
branches
|
|
101
|
-
.
|
|
101
|
+
branches
|
|
102
|
+
.map { fix(it) }
|
|
103
|
+
.then { node.with(branches: it, expression: fix(expression)) }
|
|
102
104
|
|
|
103
105
|
in AST::CaseOfBranch(body:)
|
|
104
106
|
node.with(body: fix(body))
|
|
@@ -146,8 +148,8 @@ module Jade
|
|
|
146
148
|
|
|
147
149
|
in AST::VariableReference | AST::ConstructorReference | AST::TypeDeclaration |
|
|
148
150
|
AST::ImportDeclaration | AST::Literal | AST::CharLiteral | AST::RecordAccessSugar | AST::InteropImportDeclaration |
|
|
149
|
-
AST::StructDeclaration | AST::
|
|
150
|
-
AST::CurriedConstructor |
|
|
151
|
+
AST::StructDeclaration | AST::TypeAliasDeclaration | AST::QualifiedAccess |
|
|
152
|
+
AST::Placeholder | AST::InterfaceDeclaration | AST::CurriedConstructor |
|
|
151
153
|
AST::RecordUpdateSugar
|
|
152
154
|
|
|
153
155
|
node
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module ForwardDeclaration
|
|
4
|
+
module Error
|
|
5
|
+
class AliasExpansion < Jade::Error
|
|
6
|
+
def initialize(entry, span, name:)
|
|
7
|
+
@name = name
|
|
8
|
+
super(entry:, span:)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def message
|
|
12
|
+
"`#{@name}` is a type alias, so `(..)` has nothing to expose; " \
|
|
13
|
+
'an alias names a shape and has no constructors of its own'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def label
|
|
17
|
+
"write `#{@name}` without `(..)`"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module ForwardDeclaration
|
|
4
|
+
module Error
|
|
5
|
+
# A struct and a union's variant both define a constructor, and a
|
|
6
|
+
# constructor is named without its type, so one module holds at most
|
|
7
|
+
# one of a name.
|
|
8
|
+
class DuplicateConstructorName < Jade::Error
|
|
9
|
+
def initialize(entry, span, name, declaring:, type:, owner:, first_span: nil)
|
|
10
|
+
@name = name
|
|
11
|
+
@declaring = declaring
|
|
12
|
+
@type = type
|
|
13
|
+
@owner = owner
|
|
14
|
+
@first_span = first_span
|
|
15
|
+
super(entry:, span:)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def message
|
|
19
|
+
case @owner
|
|
20
|
+
in Symbol::Struct then "`#{@name}` is already a struct in this module"
|
|
21
|
+
in Symbol::Union then "`#{@name}` is already a constructor of `#{@owner.name}`"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def label
|
|
26
|
+
case @declaring
|
|
27
|
+
in :variant then "`#{@type}` cannot also declare `#{@name}`"
|
|
28
|
+
in :struct then "cannot declare a struct `#{@name}`"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def secondary
|
|
33
|
+
return [] unless @first_span
|
|
34
|
+
|
|
35
|
+
[[@first_span, 'first declared here']]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def notes
|
|
39
|
+
[Jade::Diagnostics::Annotation[:help, 'rename one of them']]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module ForwardDeclaration
|
|
4
|
+
module Error
|
|
5
|
+
class DuplicateTypeName < Jade::Error
|
|
6
|
+
KINDS = {
|
|
7
|
+
Symbol::Interface => 'an interface',
|
|
8
|
+
Symbol::Union => 'a type',
|
|
9
|
+
Symbol::Struct => 'a struct',
|
|
10
|
+
Symbol::Alias => 'a type alias',
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def self.kind_of(symbol)
|
|
14
|
+
KINDS.fetch(symbol.class)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(entry, span, name, declaring:, existing:, first_span: nil)
|
|
18
|
+
@name = name
|
|
19
|
+
@declaring = declaring
|
|
20
|
+
@existing = existing
|
|
21
|
+
@first_span = first_span
|
|
22
|
+
super(entry:, span:)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def message
|
|
26
|
+
"`#{@name}` is already declared as #{@existing} in this module"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def label
|
|
30
|
+
"cannot declare #{@declaring} `#{@name}`"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def secondary
|
|
34
|
+
return [] unless @first_span
|
|
35
|
+
|
|
36
|
+
[[@first_span, 'first declared here']]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
require 'jade/error'
|
|
2
2
|
|
|
3
3
|
require 'jade/frontend/forward_declaration/error/bad_import'
|
|
4
|
+
require 'jade/frontend/forward_declaration/error/alias_expansion'
|
|
5
|
+
require 'jade/frontend/forward_declaration/error/duplicate_constructor_name'
|
|
6
|
+
require 'jade/frontend/forward_declaration/error/duplicate_type_name'
|
|
4
7
|
require 'jade/frontend/forward_declaration/error/exposed_type_not_found'
|
|
5
8
|
require 'jade/frontend/forward_declaration/error/exposed_value_not_found'
|
|
6
9
|
require 'jade/frontend/forward_declaration/error/module_not_found'
|