jade-lang 0.9.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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +400 -12
  3. data/docs/interop.md +18 -1
  4. data/docs/stdlib.md +3 -1
  5. data/docs/syntax.md +96 -0
  6. data/lib/jade/api.rb +9 -0
  7. data/lib/jade/ast.rb +37 -1
  8. data/lib/jade/cli/eject.rb +132 -0
  9. data/lib/jade/cli.rb +2 -0
  10. data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
  11. data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
  12. data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
  13. data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
  14. data/lib/jade/codegen/boundary/specialized.rb +13 -5
  15. data/lib/jade/codegen/boundary.rb +12 -2
  16. data/lib/jade/codegen/context.rb +17 -0
  17. data/lib/jade/codegen/error.rb +1 -0
  18. data/lib/jade/codegen/function_call.rb +23 -7
  19. data/lib/jade/codegen/function_declaration.rb +25 -13
  20. data/lib/jade/codegen/helpers.rb +27 -4
  21. data/lib/jade/codegen/implementation.rb +2 -2
  22. data/lib/jade/codegen/inline.rb +1 -1
  23. data/lib/jade/codegen/inlines.rb +6 -1
  24. data/lib/jade/codegen/names.rb +31 -0
  25. data/lib/jade/codegen/pattern/constructor.rb +2 -2
  26. data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
  27. data/lib/jade/codegen.rb +76 -15
  28. data/lib/jade/compiler.rb +2 -0
  29. data/lib/jade/entry.rb +1 -1
  30. data/lib/jade/formatter/calls.rb +68 -11
  31. data/lib/jade/formatter/collections.rb +4 -4
  32. data/lib/jade/formatter/declarations.rb +13 -15
  33. data/lib/jade/formatter/helper.rb +20 -0
  34. data/lib/jade/formatter/infix_application.rb +19 -4
  35. data/lib/jade/formatter/lambda.rb +24 -12
  36. data/lib/jade/formatter/pattern.rb +3 -0
  37. data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
  38. data/lib/jade/frontend/desugaring.rb +29 -9
  39. data/lib/jade/frontend/fixity_fixer.rb +6 -4
  40. data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
  41. data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
  42. data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
  43. data/lib/jade/frontend/forward_declaration/error.rb +3 -0
  44. data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
  45. data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
  46. data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
  47. data/lib/jade/frontend/forward_declaration/module.rb +4 -0
  48. data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
  49. data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
  50. data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
  51. data/lib/jade/frontend/forward_declaration.rb +2 -0
  52. data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
  53. data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
  54. data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
  55. data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
  56. data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
  57. data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
  58. data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
  59. data/lib/jade/frontend/pattern_analysis.rb +5 -38
  60. data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
  61. data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
  62. data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
  63. data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
  64. data/lib/jade/frontend/semantic_analysis/error.rb +3 -0
  65. data/lib/jade/frontend/semantic_analysis/helper.rb +31 -0
  66. data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -0
  67. data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
  68. data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
  69. data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
  70. data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
  71. data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
  72. data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
  73. data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
  74. data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
  75. data/lib/jade/frontend/semantic_analysis.rb +5 -0
  76. data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
  77. data/lib/jade/frontend/type_checking/cascade.rb +90 -0
  78. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
  79. data/lib/jade/frontend/type_checking/constraints.rb +18 -3
  80. data/lib/jade/frontend/type_checking/error/display.rb +49 -0
  81. data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
  82. data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
  83. data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
  84. data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
  85. data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
  86. data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
  87. data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
  88. data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
  89. data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
  90. data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
  91. data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
  92. data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
  93. data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
  94. data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
  95. data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
  96. data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
  97. data/lib/jade/frontend/type_checking/error.rb +6 -0
  98. data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
  99. data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
  100. data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
  101. data/lib/jade/frontend/type_checking/inference/function_call.rb +26 -1
  102. data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
  103. data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
  104. data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
  105. data/lib/jade/frontend/type_checking/inference.rb +2 -0
  106. data/lib/jade/frontend/type_checking/loader.rb +11 -0
  107. data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
  108. data/lib/jade/frontend/type_checking/substitution.rb +2 -2
  109. data/lib/jade/frontend/type_checking.rb +3 -1
  110. data/lib/jade/frontend/usage_analysis.rb +5 -1
  111. data/lib/jade/interop/boundary.rb +64 -24
  112. data/lib/jade/interop/error.rb +35 -7
  113. data/lib/jade/interop/lowering.rb +8 -2
  114. data/lib/jade/lsp/converters.rb +83 -2
  115. data/lib/jade/lsp/handlers.rb +122 -25
  116. data/lib/jade/lsp/snippets.rb +12 -0
  117. data/lib/jade/lsp/state.rb +11 -4
  118. data/lib/jade/module_loader/build.rb +36 -0
  119. data/lib/jade/module_loader.rb +1 -0
  120. data/lib/jade/parsing/combinators.rb +78 -2
  121. data/lib/jade/parsing/error.rb +43 -6
  122. data/lib/jade/parsing/type.rb +5 -3
  123. data/lib/jade/parsing.rb +140 -26
  124. data/lib/jade/runtime.rb +32 -13
  125. data/lib/jade/source.rb +6 -2
  126. data/lib/jade/stdlib/basics.rb +22 -3
  127. data/lib/jade/stdlib/char.rb +2 -0
  128. data/lib/jade/stdlib/clock.rb +6 -2
  129. data/lib/jade/stdlib/decimal.rb +51 -14
  130. data/lib/jade/stdlib/intrinsics.rb +59 -6
  131. data/lib/jade/stdlib/list.rb +37 -1
  132. data/lib/jade/stdlib/number.rb +47 -0
  133. data/lib/jade/stdlib/range.rb +211 -0
  134. data/lib/jade/stdlib.rb +11 -4
  135. data/lib/jade/symbol/alias.rb +11 -0
  136. data/lib/jade/symbol/parser.rb +1 -1
  137. data/lib/jade/symbol.rb +13 -1
  138. data/lib/jade/task.rb +0 -2
  139. data/lib/jade/type/anonymous_record.rb +11 -2
  140. data/lib/jade/type/application.rb +11 -2
  141. data/lib/jade/type/base.rb +26 -0
  142. data/lib/jade/type/function.rb +11 -2
  143. data/lib/jade/type.rb +36 -10
  144. data/lib/jade/version.rb +1 -1
  145. metadata +33 -2
@@ -36,6 +36,7 @@ module Jade
36
36
  import Maybe
37
37
  import Decode exposing (Decodable, Decoder)
38
38
  import Encode exposing (Encodable)
39
+ import Number exposing (non_zero)
39
40
 
40
41
 
41
42
  # An exact base-10 decimal: value = coefficient * 10 ^ exponent.
@@ -81,24 +82,45 @@ module Jade
81
82
 
82
83
 
83
84
  # Integer division, rounding ties half away from zero (Java HALF_UP /
84
- # Ruby BigDecimal default: 2.5 -> 3, -2.5 -> -3). A zero `den` raises
85
- # through Int `/`.
85
+ # Ruby BigDecimal default: 2.5 -> 3, -2.5 -> -3).
86
86
  def round_div(num: Int, den: Int) -> Int
87
87
  negative = not ((num < 0) == (den < 0))
88
- n = abs(num)
89
- d = abs(den)
90
- q = n / d
88
+ magnitude = round_div_abs(abs(num), abs(den))
89
+
90
+ negative ? 0 - magnitude : magnitude
91
+ end
92
+
93
+
94
+ # Every caller divides by a scale or by a checked divisor, so the
95
+ # Nothing arm is unreachable rather than a rounding decision.
96
+ def round_div_abs(n: Int, d: Int) -> Int
97
+ case non_zero(d)
98
+ in Just(nz) then rounded_quotient(n, nz, d)
99
+ in Nothing then 0
100
+ end
101
+ end
102
+
103
+
104
+ def rounded_quotient(n: Int, nz: NonZero(Int), d: Int) -> Int
105
+ q = n / nz
91
106
  r = n - q * d
92
- rounded = (2 * r) >= d ? q + 1 : q
93
107
 
94
- negative ? 0 - rounded : rounded
108
+ (2 * r) >= d ? q + 1 : q
95
109
  end
96
110
 
97
111
 
98
112
  def trunc_div(num: Int, den: Int) -> Int
99
- q = abs(num) / abs(den)
113
+ magnitude = trunc_div_abs(abs(num), abs(den))
114
+
115
+ num < 0 ? 0 - magnitude : magnitude
116
+ end
100
117
 
101
- num < 0 ? 0 - q : q
118
+
119
+ def trunc_div_abs(n: Int, d: Int) -> Int
120
+ case non_zero(d)
121
+ in Just(nz) then n / nz
122
+ in Nothing then 0
123
+ end
102
124
  end
103
125
 
104
126
 
@@ -132,9 +154,9 @@ module Jade
132
154
 
133
155
  # a / b rounded half-up to `scale` decimal places, like Java's
134
156
  # BigDecimal.divide(divisor, scale, HALF_UP). Division by zero raises.
135
- def div(a: Decimal, b: Decimal, scale: Int) -> Decimal
157
+ def div(a: Decimal, b: NonZero(Decimal), scale: Int) -> Decimal
136
158
  Decimal(ma, ea) = a
137
- Decimal(mb, eb) = b
159
+ Decimal(mb, eb) = Number.unwrap(b)
138
160
  k = (ea - eb) + scale
139
161
  num = k >= 0 ? ma * pow10(k) : ma
140
162
  den = k >= 0 ? mb : mb * pow10(0 - k)
@@ -145,16 +167,22 @@ module Jade
145
167
 
146
168
  # The Numeric `/`: like Ruby's BigDecimal `/`, it can't be exact for a
147
169
  # repeating quotient (1/3), so it rounds half-up to a generous scale.
148
- def divide(a: Decimal, b: Decimal) -> Decimal
170
+ def divide(a: Decimal, b: NonZero(Decimal)) -> Decimal
149
171
  div(a, b, 32)
150
172
  end
151
173
 
152
174
 
175
+ def from_int(n: Int) -> Decimal
176
+ Decimal(n, 0)
177
+ end
178
+
179
+
153
180
  implements Numeric(Decimal) with
154
181
  (+): add,
155
182
  (-): sub,
156
183
  (*): mul,
157
- (/): divide
184
+ (/): divide,
185
+ from_int: from_int
158
186
  end
159
187
 
160
188
 
@@ -181,7 +209,16 @@ module Jade
181
209
 
182
210
  e >= 0
183
211
  ? Basics.to_float(m * pow10(e))
184
- : Basics.to_float(m) / Basics.to_float(pow10(0 - e))
212
+ : scaled_down(Basics.to_float(m), Basics.to_float(pow10(0 - e)))
213
+ end
214
+
215
+
216
+ # `pow10` is never zero, so the Nothing arm cannot be reached.
217
+ def scaled_down(m: Float, scale: Float) -> Float
218
+ case non_zero(scale)
219
+ in Just(nz) then m / nz
220
+ in Nothing then m
221
+ end
185
222
  end
186
223
 
187
224
 
@@ -5,12 +5,45 @@ require 'jade/codegen/inlines'
5
5
  module Jade
6
6
  module Stdlib
7
7
  module Intrinsics
8
+ # A running app wants the implementations these files carry and not
9
+ # the declarations, which are what read type annotations.
10
+ def self.runtime_only!
11
+ @runtime_only = true
12
+ end
13
+
14
+ def self.declaring!
15
+ @runtime_only = false
16
+ end
17
+
18
+ def self.runtime_only?
19
+ @runtime_only ||= false
20
+ end
21
+
22
+ # A second load of the same file would otherwise store every symbol
23
+ # twice.
24
+ def self.extended(base)
25
+ base.instance_variable_set(:@symbols, nil)
26
+ end
27
+
28
+ def self.reload_for_declarations(files)
29
+ return unless runtime_only?
30
+
31
+ declaring!
32
+ files.each { load "#{__dir__}/#{it}.rb" }
33
+ end
34
+
35
+ def runtime_only?
36
+ Intrinsics.runtime_only?
37
+ end
38
+
8
39
  def generate_entry(registry)
9
40
  @entry = entry
10
41
  .then { load_env(it, registry) }
11
42
  end
12
43
 
13
44
  def variant(name, of:, args: [])
45
+ return if runtime_only?
46
+
14
47
  parent = @symbols
15
48
  &.find { it.is_a?(Symbol::Union) && it.name == of.to_s }
16
49
 
@@ -44,12 +77,14 @@ module Jade
44
77
  end
45
78
 
46
79
  def union(name, *type_params, constructor: false)
80
+ return if runtime_only?
81
+
47
82
  union_symbol = Symbol
48
83
  .union(name.to_s, type_params.map { Symbol.var(it, nil) }, [], nil)
49
84
  .with(module_name:)
50
85
  .tap { store(it) }
51
86
 
52
- constructor_symbol = if constructor
87
+ if constructor
53
88
  Symbol.constructor(
54
89
  name.to_s,
55
90
  type_params.map { Symbol.var(it, nil) },
@@ -67,6 +102,8 @@ module Jade
67
102
  codegen = body || "Jade::Runtime.intr('#{qualified_fn_name}')"
68
103
  impl = runtime_impl(qualified_fn_name, params.size, block, body)
69
104
 
105
+ return Runtime.register(qualified_fn_name, &impl) if runtime_only?
106
+
70
107
  Symbol
71
108
  .stdlib_function(
72
109
  name.to_s,
@@ -118,6 +155,8 @@ module Jade
118
155
  end
119
156
 
120
157
  def interface(name, type_param, functions, default: {})
158
+ return if runtime_only?
159
+
121
160
  functions
122
161
  .map { |k, v| Symbol.parse(v).then { to_interface_function(name, k, it) }.with(module_name:) }
123
162
  .then { Symbol.interface(name, Symbol.parse(type_param), it, default, nil) }
@@ -144,6 +183,8 @@ module Jade
144
183
  end
145
184
 
146
185
  def implementation(interface_name, type, functions)
186
+ return register_native_impls(interface_name, type, functions) if runtime_only?
187
+
147
188
  interface = imports
148
189
  .map(&:symbols)
149
190
  .then { [symbols, *it] }
@@ -168,11 +209,17 @@ module Jade
168
209
  )
169
210
  .then { store(it) }
170
211
 
171
- if (ruby_classes = @native_types&.[](type))
172
- qualified_iface = "#{interface_to_ref(interface_name).module_name}.#{interface_name}"
173
- qualified_fns = functions.transform_values { "#{module_name}.#{it}" }
174
- ruby_classes.each { Runtime.register_impl(qualified_iface, it, qualified_fns) }
175
- end
212
+ register_native_impls(interface_name, type, functions)
213
+ end
214
+
215
+ # Dispatch on a native Ruby class is the only part of an
216
+ # implementation a running app can use.
217
+ def register_native_impls(interface_name, type, functions)
218
+ return unless (ruby_classes = @native_types&.[](type))
219
+
220
+ qualified_iface = "#{interface_to_ref(interface_name).module_name}.#{interface_name}"
221
+ qualified_fns = functions.transform_values { "#{module_name}.#{it}" }
222
+ ruby_classes.each { Runtime.register_impl(qualified_iface, it, qualified_fns) }
176
223
  end
177
224
 
178
225
  # Accepts both bare/qualified names ('Int', 'Basics.Int') and full type
@@ -198,6 +245,8 @@ module Jade
198
245
  end
199
246
 
200
247
  def import(module_name)
248
+ return if runtime_only?
249
+
201
250
  @imports = imports + [module_name]
202
251
  end
203
252
 
@@ -206,6 +255,8 @@ module Jade
206
255
  end
207
256
 
208
257
  def default_importing(imports)
258
+ return if runtime_only?
259
+
209
260
  @default_imports = if imports == :*
210
261
  exposes
211
262
  else
@@ -244,6 +295,8 @@ module Jade
244
295
  @symbols.concat << symbol
245
296
  end
246
297
 
298
+
299
+
247
300
  def module_name
248
301
  "#{self.name.split('::').last}"
249
302
  end
@@ -12,7 +12,7 @@ module Jade
12
12
 
13
13
  function(:singleton, { element: 'a' }, 'List(a)')
14
14
  function(:repeat, { element: 'a', times: 'Int' }, 'List(a)')
15
- function(:range, { begin_: 'Int', end_: 'Int' }, 'List(a)')
15
+ function(:range, { begin_: 'Int', end_: 'Int' }, 'List(Int)')
16
16
  function(:"empty?", { list: 'List(a)' }, 'Bool')
17
17
  function(:head, { list: 'List(a)' }, 'Maybe(a)')
18
18
  function(:tail, { list: 'List(a)' }, 'List(a)')
@@ -170,6 +170,42 @@ module Jade
170
170
  ),
171
171
  )
172
172
 
173
+ function(
174
+ :sum,
175
+ { list: 'List(a)' },
176
+ 'a',
177
+ constraints: [['Basics.Numeric', 'a']],
178
+ body: Symbol::DerivedFunction.new(
179
+ params: ['list'],
180
+ body: [:call,
181
+ [:stdlib_fn, 'List.fold'],
182
+ [
183
+ [:var, 'list'],
184
+ [:call, [:impl_arg, 0, 'from_int'], [0]],
185
+ [:impl_arg, 0, '(+)'],
186
+ ],
187
+ ],
188
+ ),
189
+ )
190
+
191
+ function(
192
+ :product,
193
+ { list: 'List(a)' },
194
+ 'a',
195
+ constraints: [['Basics.Numeric', 'a']],
196
+ body: Symbol::DerivedFunction.new(
197
+ params: ['list'],
198
+ body: [:call,
199
+ [:stdlib_fn, 'List.fold'],
200
+ [
201
+ [:var, 'list'],
202
+ [:call, [:impl_arg, 0, 'from_int'], [1]],
203
+ [:impl_arg, 0, '(*)'],
204
+ ],
205
+ ],
206
+ ),
207
+ )
208
+
173
209
  function(
174
210
  'maximum_with',
175
211
  { list: 'List(a)', cmp: 'a, a -> Ordering' },
@@ -0,0 +1,47 @@
1
+ require 'jade/stdlib/intrinsics'
2
+
3
+ module Jade
4
+ module Stdlib
5
+ # `non_zero` cannot live in Basics beside the `NonZero` type it builds,
6
+ # because its `Maybe` comes from a module that needs Basics first.
7
+ module Number
8
+ extend Intrinsics
9
+
10
+ import Basics
11
+
12
+ # What counts as zero is the type's business, so this dispatches
13
+ # rather than asking Ruby whether the value is `0`.
14
+ function(
15
+ :non_zero,
16
+ { n: 'a' },
17
+ 'Maybe(NonZero(a))',
18
+ constraints: [['Basics.Numeric', 'a'], ['Basics.Eq', 'a']],
19
+ body: Symbol::DerivedFunction.new(
20
+ params: ['n'],
21
+ body: [:call,
22
+ [:stdlib_fn, 'Number.checked'],
23
+ [
24
+ [:var, 'n'],
25
+ [:call, [:impl_arg, 0, 'from_int'], [0]],
26
+ [:impl_arg, 1, '(==)'],
27
+ ]],
28
+ ),
29
+ )
30
+
31
+ # For anyone implementing `Numeric` themselves: the divisor arrives
32
+ # wrapped and the arithmetic needs it bare.
33
+ function(:unwrap, { n: 'NonZero(a)' }, 'a')
34
+
35
+ function(
36
+ :checked,
37
+ { n: 'a', zero: 'a', eq: 'a, a -> Bool' },
38
+ 'Maybe(NonZero(a))',
39
+ private: true,
40
+ ) do |n, zero, eq|
41
+ eq.call(n, zero) ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[n]
42
+ end
43
+
44
+ default_importing(['non_zero'])
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,211 @@
1
+ require 'jade/stdlib/compiled'
2
+
3
+ module Jade
4
+ module Stdlib
5
+ module Range
6
+ extend self
7
+ extend Compiled
8
+
9
+ def uri
10
+ 'range.jd'
11
+ end
12
+
13
+ def imports
14
+ [Basics, Maybe, Tuple]
15
+ end
16
+
17
+ # `..` desugars to `Range.between`, so the module has to be in scope
18
+ # wherever the operator is legal, which is everywhere.
19
+ def default_imports
20
+ [Symbol.type_ref('Range', 'Range')]
21
+ end
22
+
23
+ def code
24
+ <<~JADE
25
+ module Range exposing (
26
+ Range,
27
+ all,
28
+ between,
29
+ bounded?,
30
+ clamp,
31
+ contains?,
32
+ empty,
33
+ empty?,
34
+ from,
35
+ intersect,
36
+ lower,
37
+ overlaps?,
38
+ to,
39
+ upper,
40
+ )
41
+
42
+ type Range(a)
43
+ = Empty
44
+ | Between(a, a)
45
+ | From(a)
46
+ | To(a)
47
+ | All
48
+
49
+
50
+ def empty -> Range(a)
51
+ Empty
52
+ end
53
+
54
+
55
+ def all -> Range(a)
56
+ All
57
+ end
58
+
59
+
60
+ def from(low: a) -> Range(a)
61
+ From(low)
62
+ end
63
+
64
+
65
+ def to(high: a) -> Range(a)
66
+ To(high)
67
+ end
68
+
69
+
70
+ def between(low: a, high: a) -> Range(a)
71
+ if low <= high then
72
+ Between(low, high)
73
+ else
74
+ Empty
75
+ end
76
+ end
77
+
78
+
79
+ def contains?(range: Range(a), value: a) -> Bool
80
+ case range
81
+ in Empty then False
82
+ in Between(low, high) then low <= value && value <= high
83
+ in From(low) then low <= value
84
+ in To(high) then value <= high
85
+ in All then True
86
+ end
87
+ end
88
+
89
+
90
+ def empty?(range: Range(a)) -> Bool
91
+ case range
92
+ in Empty then True
93
+ in Between(low, high) then high < low
94
+ in From(_) then False
95
+ in To(_) then False
96
+ in All then False
97
+ end
98
+ end
99
+
100
+
101
+ def bounded?(range: Range(a)) -> Bool
102
+ case range
103
+ in Empty then True
104
+ in Between(_, _) then True
105
+ in From(_) then False
106
+ in To(_) then False
107
+ in All then False
108
+ end
109
+ end
110
+
111
+
112
+ def lower(range: Range(a)) -> Maybe(a)
113
+ case range
114
+ in Empty then Nothing
115
+ in Between(low, _) then Just(low)
116
+ in From(low) then Just(low)
117
+ in To(_) then Nothing
118
+ in All then Nothing
119
+ end
120
+ end
121
+
122
+
123
+ def upper(range: Range(a)) -> Maybe(a)
124
+ case range
125
+ in Empty then Nothing
126
+ in Between(_, high) then Just(high)
127
+ in From(_) then Nothing
128
+ in To(high) then Just(high)
129
+ in All then Nothing
130
+ end
131
+ end
132
+
133
+
134
+ def intersect(left: Range(a), right: Range(a)) -> Range(a)
135
+ if empty?(left) || empty?(right) then
136
+ Empty
137
+ else
138
+ rebuild(
139
+ highest(lower(left), lower(right)),
140
+ lowest(upper(left), upper(right)),
141
+ )
142
+ end
143
+ end
144
+
145
+
146
+ def overlaps?(left: Range(a), right: Range(a)) -> Bool
147
+ intersect(left, right)
148
+ |> empty?
149
+ |> not
150
+ end
151
+
152
+
153
+ def clamp(range: Range(a), value: a) -> Maybe(a)
154
+ if empty?(range) then
155
+ Nothing
156
+ else
157
+ value
158
+ |> at_least(lower(range))
159
+ |> at_most(upper(range))
160
+ |> Just
161
+ end
162
+ end
163
+
164
+
165
+ def rebuild(low: Maybe(a), high: Maybe(a)) -> Range(a)
166
+ case (low, high)
167
+ in (Nothing, Nothing) then All
168
+ in (Just(l), Nothing) then From(l)
169
+ in (Nothing, Just(h)) then To(h)
170
+ in (Just(l), Just(h)) then between(l, h)
171
+ end
172
+ end
173
+
174
+
175
+ def highest(left: Maybe(a), right: Maybe(a)) -> Maybe(a)
176
+ case (left, right)
177
+ in (Nothing, _) then right
178
+ in (_, Nothing) then left
179
+ in (Just(l), Just(r)) then if l <= r then Just(r) else Just(l) end
180
+ end
181
+ end
182
+
183
+
184
+ def lowest(left: Maybe(a), right: Maybe(a)) -> Maybe(a)
185
+ case (left, right)
186
+ in (Nothing, _) then right
187
+ in (_, Nothing) then left
188
+ in (Just(l), Just(r)) then if l <= r then Just(l) else Just(r) end
189
+ end
190
+ end
191
+
192
+
193
+ def at_least(value: a, bound: Maybe(a)) -> a
194
+ case bound
195
+ in Nothing then value
196
+ in Just(low) then if value <= low then low else value end
197
+ end
198
+ end
199
+
200
+
201
+ def at_most(value: a, bound: Maybe(a)) -> a
202
+ case bound
203
+ in Nothing then value
204
+ in Just(high) then if high <= value then high else value end
205
+ end
206
+ end
207
+ JADE
208
+ end
209
+ end
210
+ end
211
+ end
data/lib/jade/stdlib.rb CHANGED
@@ -1,9 +1,15 @@
1
1
  require 'set'
2
+ require 'jade/stdlib/intrinsics'
3
+
4
+ # A runtime-only load skipped the declarations; the compiler needs them.
5
+ Jade::Stdlib::Intrinsics.reload_for_declarations(Jade::Runtime::STDLIB)
6
+
2
7
  require 'jade/stdlib/basics'
3
8
  require 'jade/stdlib/maybe'
4
9
  require 'jade/stdlib/tuple'
5
10
  require 'jade/stdlib/list'
6
11
  require 'jade/stdlib/char'
12
+ require 'jade/stdlib/number'
7
13
  require 'jade/stdlib/string'
8
14
  require 'jade/stdlib/result'
9
15
  require 'jade/stdlib/task'
@@ -16,6 +22,7 @@ require 'jade/stdlib/bytes'
16
22
  require 'jade/stdlib/calendar'
17
23
  require 'jade/stdlib/clock'
18
24
  require 'jade/stdlib/decimal'
25
+ require 'jade/stdlib/range'
19
26
  require 'jade/stdlib/show'
20
27
  require 'jade/stdlib/debug'
21
28
 
@@ -24,21 +31,21 @@ module Jade
24
31
  extend self
25
32
 
26
33
  INTRINSICS = %w[
27
- Basics String List Tuple Char Task
34
+ Basics Number String List Tuple Char Task
28
35
  Decode Encode
29
36
  Bytes
30
37
  Dict Set
31
38
  Show Debug
32
39
  ].freeze
33
- COMPILED = %w[Maybe Result Decode.Params Calendar Clock Decimal].freeze
40
+ COMPILED = %w[Maybe Result Decode.Params Calendar Clock Decimal Range].freeze
34
41
  TOPLEVELS = (INTRINSICS + COMPILED).map { it.split('.', 2).first }.to_set.freeze
35
42
  STDLIBS = [
36
- Stdlib::Basics, Stdlib::Maybe, Stdlib::Tuple, Stdlib::List, Stdlib::Char,
43
+ Stdlib::Basics, Stdlib::Maybe, Stdlib::Number, Stdlib::Tuple, Stdlib::List, Stdlib::Char,
37
44
  Stdlib::String, Stdlib::Result, Stdlib::Task,
38
45
  Stdlib::Dict, Stdlib::Set,
39
46
  Stdlib::Decode, Stdlib::Decode::Params, Stdlib::Encode,
40
47
  Stdlib::Bytes,
41
- Stdlib::Calendar, Stdlib::Clock, Stdlib::Decimal, Stdlib::Show, Stdlib::Debug,
48
+ Stdlib::Calendar, Stdlib::Clock, Stdlib::Decimal, Stdlib::Range, Stdlib::Show, Stdlib::Debug,
42
49
  ]
43
50
  # Loaded into the registry but not auto-imported into user modules.
44
51
  EXTENSIONS = [
@@ -0,0 +1,11 @@
1
+ module Jade
2
+ module Symbol
3
+ Alias = Data.define(:module_name, :name, :type_params, :body, :decl_span) do
4
+ include Base
5
+
6
+ def to_ref
7
+ TypeRef[module_name, name]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -44,7 +44,7 @@ module Jade
44
44
 
45
45
  def qualify(type)
46
46
  case type
47
- in 'Int' | 'Float' | 'Bool' | 'Ordering' | 'Never'
47
+ in 'Int' | 'Float' | 'Bool' | 'Ordering' | 'Never' | 'NonZero'
48
48
  'Basics'
49
49
 
50
50
  in 'String' | 'Maybe' | 'List' | 'Result' | 'Task' | 'Char'
data/lib/jade/symbol.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'jade/symbol/base'
2
- require 'jade/symbol/parser'
3
2
 
4
3
  require 'jade/symbol/anonymous_record'
5
4
  require 'jade/symbol/constructor'
@@ -16,6 +15,7 @@ require 'jade/symbol/record_type'
16
15
  require 'jade/symbol/stdlib_function'
17
16
  require 'jade/symbol/stdlib_implementation'
18
17
  require 'jade/symbol/struct'
18
+ require 'jade/symbol/alias'
19
19
  require 'jade/symbol/type_application'
20
20
  require 'jade/symbol/partial_application'
21
21
  require 'jade/symbol/type_ref'
@@ -122,6 +122,14 @@ module Jade
122
122
  Struct[nil, name, type_params, nil, span]
123
123
  end
124
124
 
125
+ def predeclared_alias(name, type_params, span)
126
+ Alias[nil, name, type_params, nil, span]
127
+ end
128
+
129
+ def alias(name, type_params, body, span)
130
+ Alias[nil, name, type_params, body, span]
131
+ end
132
+
125
133
  def interface(name, type_var, functions, default, span)
126
134
  Interface[nil, name, type_var, functions, default, span]
127
135
  end
@@ -153,7 +161,11 @@ module Jade
153
161
  ]
154
162
  end
155
163
 
164
+ # The parser is the compiler's, and a running app never reads a type
165
+ # annotation, so it loads here rather than with the symbol classes.
156
166
  def parse(annotation)
167
+ require_relative 'symbol/parser'
168
+
157
169
  Lexer
158
170
  .tokenize(Source.new(uri: nil, text: annotation))
159
171
  .then { Parser.parse(it) }
data/lib/jade/task.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'jade/tasks'
2
-
3
1
  module Jade
4
2
  module Task
5
3
  # Every node is a value describing work, and `run` drives them from one