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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +400 -12
- data/docs/interop.md +18 -1
- data/docs/stdlib.md +3 -1
- data/docs/syntax.md +96 -0
- data/lib/jade/api.rb +9 -0
- data/lib/jade/ast.rb +37 -1
- data/lib/jade/cli/eject.rb +132 -0
- data/lib/jade/cli.rb +2 -0
- data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
- data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
- data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
- data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
- data/lib/jade/codegen/boundary/specialized.rb +13 -5
- data/lib/jade/codegen/boundary.rb +12 -2
- data/lib/jade/codegen/context.rb +17 -0
- data/lib/jade/codegen/error.rb +1 -0
- data/lib/jade/codegen/function_call.rb +23 -7
- data/lib/jade/codegen/function_declaration.rb +25 -13
- data/lib/jade/codegen/helpers.rb +27 -4
- data/lib/jade/codegen/implementation.rb +2 -2
- data/lib/jade/codegen/inline.rb +1 -1
- data/lib/jade/codegen/inlines.rb +6 -1
- data/lib/jade/codegen/names.rb +31 -0
- data/lib/jade/codegen/pattern/constructor.rb +2 -2
- data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
- data/lib/jade/codegen.rb +76 -15
- data/lib/jade/compiler.rb +2 -0
- data/lib/jade/entry.rb +1 -1
- data/lib/jade/formatter/calls.rb +68 -11
- data/lib/jade/formatter/collections.rb +4 -4
- data/lib/jade/formatter/declarations.rb +13 -15
- data/lib/jade/formatter/helper.rb +20 -0
- data/lib/jade/formatter/infix_application.rb +19 -4
- data/lib/jade/formatter/lambda.rb +24 -12
- data/lib/jade/formatter/pattern.rb +3 -0
- data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
- data/lib/jade/frontend/desugaring.rb +29 -9
- data/lib/jade/frontend/fixity_fixer.rb +6 -4
- data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
- data/lib/jade/frontend/forward_declaration/error.rb +3 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
- data/lib/jade/frontend/forward_declaration/module.rb +4 -0
- data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
- data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration.rb +2 -0
- data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
- data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
- data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
- data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
- data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
- data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
- data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
- data/lib/jade/frontend/pattern_analysis.rb +5 -38
- data/lib/jade/frontend/semantic_analysis/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/recursive_derivation.rb +25 -0
- data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
- data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
- data/lib/jade/frontend/type_checking/error.rb +6 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
- data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
- data/lib/jade/frontend/type_checking/inference/function_call.rb +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/type_alias_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference.rb +2 -0
- data/lib/jade/frontend/type_checking/loader.rb +11 -0
- data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
- data/lib/jade/frontend/type_checking/substitution.rb +2 -2
- data/lib/jade/frontend/type_checking.rb +3 -1
- data/lib/jade/frontend/usage_analysis.rb +5 -1
- data/lib/jade/interop/boundary.rb +64 -24
- data/lib/jade/interop/error.rb +35 -7
- data/lib/jade/interop/lowering.rb +8 -2
- data/lib/jade/lsp/converters.rb +83 -2
- data/lib/jade/lsp/handlers.rb +122 -25
- data/lib/jade/lsp/snippets.rb +12 -0
- data/lib/jade/lsp/state.rb +11 -4
- data/lib/jade/module_loader/build.rb +36 -0
- data/lib/jade/module_loader.rb +1 -0
- data/lib/jade/parsing/combinators.rb +78 -2
- data/lib/jade/parsing/error.rb +43 -6
- data/lib/jade/parsing/type.rb +5 -3
- data/lib/jade/parsing.rb +140 -26
- data/lib/jade/runtime.rb +32 -13
- data/lib/jade/source.rb +6 -2
- data/lib/jade/stdlib/basics.rb +22 -3
- data/lib/jade/stdlib/char.rb +2 -0
- data/lib/jade/stdlib/clock.rb +6 -2
- data/lib/jade/stdlib/decimal.rb +51 -14
- data/lib/jade/stdlib/intrinsics.rb +59 -6
- data/lib/jade/stdlib/list.rb +37 -1
- data/lib/jade/stdlib/number.rb +47 -0
- data/lib/jade/stdlib/range.rb +211 -0
- data/lib/jade/stdlib.rb +11 -4
- data/lib/jade/symbol/alias.rb +11 -0
- data/lib/jade/symbol/parser.rb +1 -1
- data/lib/jade/symbol.rb +13 -1
- data/lib/jade/task.rb +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 -2
- data/lib/jade/type.rb +36 -10
- data/lib/jade/version.rb +1 -1
- metadata +33 -2
data/lib/jade/stdlib/decimal.rb
CHANGED
|
@@ -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).
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
magnitude = trunc_div_abs(abs(num), abs(den))
|
|
114
|
+
|
|
115
|
+
num < 0 ? 0 - magnitude : magnitude
|
|
116
|
+
end
|
|
100
117
|
|
|
101
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
data/lib/jade/stdlib/list.rb
CHANGED
|
@@ -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(
|
|
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 = [
|
data/lib/jade/symbol/parser.rb
CHANGED
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) }
|