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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +442 -12
  3. data/README.md +1 -0
  4. data/docs/interop.md +18 -1
  5. data/docs/json.md +34 -3
  6. data/docs/stdlib.md +3 -1
  7. data/docs/syntax.md +96 -0
  8. data/lib/jade/api.rb +9 -0
  9. data/lib/jade/ast.rb +37 -1
  10. data/lib/jade/cli/eject.rb +132 -0
  11. data/lib/jade/cli/init.rb +86 -0
  12. data/lib/jade/cli.rb +4 -0
  13. data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
  14. data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
  15. data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
  16. data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
  17. data/lib/jade/codegen/boundary/specialized.rb +13 -5
  18. data/lib/jade/codegen/boundary.rb +12 -2
  19. data/lib/jade/codegen/context.rb +17 -0
  20. data/lib/jade/codegen/error.rb +1 -0
  21. data/lib/jade/codegen/function_call.rb +38 -7
  22. data/lib/jade/codegen/function_declaration.rb +25 -13
  23. data/lib/jade/codegen/helpers.rb +27 -4
  24. data/lib/jade/codegen/implementation.rb +2 -2
  25. data/lib/jade/codegen/inline.rb +1 -1
  26. data/lib/jade/codegen/inlines.rb +6 -1
  27. data/lib/jade/codegen/names.rb +31 -0
  28. data/lib/jade/codegen/pattern/constructor.rb +2 -2
  29. data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
  30. data/lib/jade/codegen.rb +76 -15
  31. data/lib/jade/compiler.rb +2 -0
  32. data/lib/jade/entry.rb +1 -1
  33. data/lib/jade/formatter/calls.rb +68 -11
  34. data/lib/jade/formatter/collections.rb +4 -4
  35. data/lib/jade/formatter/declarations.rb +13 -15
  36. data/lib/jade/formatter/helper.rb +20 -0
  37. data/lib/jade/formatter/infix_application.rb +19 -4
  38. data/lib/jade/formatter/lambda.rb +24 -12
  39. data/lib/jade/formatter/pattern.rb +3 -0
  40. data/lib/jade/formatter/type.rb +3 -5
  41. data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
  42. data/lib/jade/frontend/desugaring.rb +29 -9
  43. data/lib/jade/frontend/fixity_fixer.rb +6 -4
  44. data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
  45. data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
  46. data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
  47. data/lib/jade/frontend/forward_declaration/error.rb +3 -0
  48. data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
  49. data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
  50. data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
  51. data/lib/jade/frontend/forward_declaration/module.rb +4 -0
  52. data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
  53. data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
  54. data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
  55. data/lib/jade/frontend/forward_declaration.rb +2 -0
  56. data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
  57. data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
  58. data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
  59. data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
  60. data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
  61. data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
  62. data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
  63. data/lib/jade/frontend/pattern_analysis.rb +5 -38
  64. data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
  65. data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
  66. data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
  67. data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
  68. data/lib/jade/frontend/semantic_analysis/error.rb +3 -0
  69. data/lib/jade/frontend/semantic_analysis/helper.rb +31 -0
  70. data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -0
  71. data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
  72. data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
  73. data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
  74. data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
  75. data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
  76. data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
  77. data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
  78. data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
  79. data/lib/jade/frontend/semantic_analysis.rb +5 -0
  80. data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
  81. data/lib/jade/frontend/type_checking/cascade.rb +90 -0
  82. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
  83. data/lib/jade/frontend/type_checking/constraints.rb +18 -3
  84. data/lib/jade/frontend/type_checking/error/display.rb +49 -0
  85. data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
  86. data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
  87. data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
  88. data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
  89. data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
  90. data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
  91. data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
  92. data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
  93. data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
  94. data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
  95. data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
  96. data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
  97. data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
  98. data/lib/jade/frontend/type_checking/error/record_update_type_mismatch.rb +21 -0
  99. data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
  100. data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
  101. data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
  102. data/lib/jade/frontend/type_checking/error.rb +7 -0
  103. data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
  104. data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
  105. data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
  106. data/lib/jade/frontend/type_checking/inference/function_call.rb +26 -1
  107. data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
  108. data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
  109. data/lib/jade/frontend/type_checking/inference/record_update.rb +8 -1
  110. data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
  111. data/lib/jade/frontend/type_checking/inference.rb +2 -0
  112. data/lib/jade/frontend/type_checking/loader.rb +11 -0
  113. data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
  114. data/lib/jade/frontend/type_checking/substitution.rb +2 -2
  115. data/lib/jade/frontend/type_checking.rb +3 -1
  116. data/lib/jade/frontend/usage_analysis.rb +5 -1
  117. data/lib/jade/interop/boundary.rb +64 -24
  118. data/lib/jade/interop/error.rb +35 -7
  119. data/lib/jade/interop/lowering.rb +8 -2
  120. data/lib/jade/lsp/converters.rb +83 -2
  121. data/lib/jade/lsp/handlers.rb +122 -25
  122. data/lib/jade/lsp/snippets.rb +12 -0
  123. data/lib/jade/lsp/state.rb +11 -4
  124. data/lib/jade/module_loader/build.rb +36 -0
  125. data/lib/jade/module_loader.rb +1 -0
  126. data/lib/jade/parsing/combinators.rb +78 -2
  127. data/lib/jade/parsing/error.rb +43 -6
  128. data/lib/jade/parsing/type.rb +6 -4
  129. data/lib/jade/parsing.rb +140 -26
  130. data/lib/jade/runtime.rb +32 -13
  131. data/lib/jade/source.rb +6 -2
  132. data/lib/jade/stdlib/basics.rb +22 -3
  133. data/lib/jade/stdlib/char.rb +2 -0
  134. data/lib/jade/stdlib/clock.rb +6 -2
  135. data/lib/jade/stdlib/decimal.rb +51 -14
  136. data/lib/jade/stdlib/decode.rb +11 -0
  137. data/lib/jade/stdlib/intrinsics.rb +59 -6
  138. data/lib/jade/stdlib/list.rb +37 -1
  139. data/lib/jade/stdlib/number.rb +47 -0
  140. data/lib/jade/stdlib/range.rb +211 -0
  141. data/lib/jade/stdlib.rb +11 -4
  142. data/lib/jade/symbol/alias.rb +11 -0
  143. data/lib/jade/symbol/parser.rb +1 -1
  144. data/lib/jade/symbol/stdlib_function.rb +1 -1
  145. data/lib/jade/symbol.rb +13 -1
  146. data/lib/jade/task.rb +0 -2
  147. data/lib/jade/type/anonymous_record.rb +11 -2
  148. data/lib/jade/type/application.rb +11 -2
  149. data/lib/jade/type/base.rb +26 -0
  150. data/lib/jade/type/function.rb +11 -3
  151. data/lib/jade/type.rb +36 -10
  152. data/lib/jade/version.rb +1 -1
  153. metadata +35 -2
@@ -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
- Calls.render(callee_str, args_strs, trailing_comma, indent)
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
- Calls.render(callee_str, field_strs, trailing_comma, indent)
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
- def render(callee_str, item_strs, trailing_comma, indent)
37
- inline = "#{callee_str}(#{item_strs.join(', ')})"
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
- if trailing_comma || too_long?(inline, indent)
40
- inner = item_strs
41
- .map { "#{it.then(&and_indent(indent + 1))}," }
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
- "#{callee_str.then(&and_indent(indent))}(\n#{inner}\n#{INDENT * indent})"
45
- else
46
- inline.then(&and_indent(indent))
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 => AST::TypeRecord(fields:, row_var:)
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
- def format_multiline(header, fields, row_var, indent)
81
- open_brace = row_var ? "{ #{row_var.name} |" : "{"
82
- fields_str = fields
83
- .map { |k, v| "#{k}: #{format_type(v)}".then(&and_indent(indent + 1)) }
84
- .join(",\n")
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
- and_indent(indent)
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
- cont = INDENT * (indent + 1)
62
- head = format_node(chain.first, indent:, source:)
63
- tail = chain[1..].map { "#{cont}#{op} #{format_node(it, source:)}" }
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
- ([head] + tail).join("\n")
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
- head = format_head(params)
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
 
@@ -12,6 +12,9 @@ module Jade
12
12
  in AST::Pattern::Literal(literal:)
13
13
  format_node(literal, source:)
14
14
 
15
+ in AST::Pattern::Range(from:, to:)
16
+ "#{from&.value}..#{to&.value}"
17
+
15
18
  in AST::Pattern::Binding(name:)
16
19
  name
17
20
 
@@ -26,11 +26,9 @@ module Jade
26
26
  end
27
27
 
28
28
  in AST::TypeFunction(params:, return_type:)
29
- params_str = params.empty? ?
30
- "()" :
31
- params.map { format_atom(it) }.join(', ')
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(call)
11
- return call unless call.args.any?(AST::Placeholder)
12
-
13
- filled_args, names = call
14
- .args
15
- .reduce([[], []]) do |(args, names), arg|
16
- case arg
17
- in AST::Placeholder
18
- param_synthetic_name(names.size)
19
- .then { [args + [AST::VariableReference[it, nil]], names + [it]] }
20
-
21
- else
22
- [args + [arg], names]
23
- end
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(call.with(args: filled_args)) do |body, name|
51
+ .reduce(body) do |inner, name|
29
52
  AST::Lambda[
30
53
  [AST::Pattern::Binding[name, nil]],
31
- AST::Body[[body], nil],
32
- call.range,
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.map { desugar(it) }.then { node.with(fields: it) }
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.new(
134
- callee: AST::ConstructorReference["Tuple.Tuple#{items.size}", node.range],
135
- args: items.map { desugar(it) },
136
- infix: false,
137
- range: node.range,
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::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration |
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.map { fix(it) }
101
- .then { node.with(branches:, expression: fix(expression)) }
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::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration |
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'