jade-lang 0.4.0 → 0.6.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/AGENTS.md +164 -0
- data/CHANGELOG.md +180 -1
- data/README.md +5 -1
- data/docs/interop.md +208 -0
- data/docs/json.md +163 -0
- data/docs/lsp.md +105 -0
- data/docs/stdlib.md +69 -0
- data/docs/syntax.md +458 -0
- data/docs/testing.md +70 -0
- data/lib/jade/api.rb +244 -0
- data/lib/jade/cli/check.rb +97 -0
- data/lib/jade/cli/q.rb +47 -1
- data/lib/jade/cli.rb +4 -2
- data/lib/jade/codegen/boundary/cache.rb +1 -2
- data/lib/jade/codegen/boundary/specialized/list.rb +9 -4
- data/lib/jade/codegen/boundary/specialized/maybe.rb +15 -3
- data/lib/jade/codegen/boundary/specialized/record.rb +7 -3
- data/lib/jade/codegen/boundary/specialized.rb +7 -3
- data/lib/jade/codegen/emitter.rb +11 -2
- data/lib/jade/codegen/function_declaration.rb +7 -6
- data/lib/jade/codegen/helpers.rb +6 -0
- data/lib/jade/codegen/inlines.rb +5 -0
- data/lib/jade/decode.rb +332 -212
- data/lib/jade/frontend/fixity_fixer.rb +3 -2
- data/lib/jade/frontend/semantic_analysis/error/variable_not_found.rb +9 -2
- data/lib/jade/frontend/semantic_analysis/member_access.rb +11 -1
- data/lib/jade/frontend/type_checking/constraints/deriving/decodable.rb +10 -15
- data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +7 -16
- data/lib/jade/interop/boundary.rb +12 -4
- data/lib/jade/interop/error.rb +25 -5
- data/lib/jade/lsp/converters.rb +2 -15
- data/lib/jade/lsp/snippets.rb +21 -3
- data/lib/jade/module_loader/module_name.rb +46 -0
- data/lib/jade/module_loader.rb +5 -0
- data/lib/jade/signature.rb +47 -0
- data/lib/jade/stdlib/calendar.rb +20 -19
- data/lib/jade/stdlib/clock.rb +7 -13
- data/lib/jade/stdlib/decimal.rb +3 -16
- data/lib/jade/stdlib/decode.rb +35 -12
- data/lib/jade/stdlib/encode.rb +9 -0
- data/lib/jade/stdlib/intrinsics.rb +9 -1
- data/lib/jade/stdlib/result.rb +1 -1
- data/lib/jade/stdlib/text.rb +131 -0
- data/lib/jade/task.rb +2 -3
- data/lib/jade/version.rb +1 -1
- metadata +14 -2
data/lib/jade/decode.rb
CHANGED
|
@@ -11,294 +11,414 @@ module Jade
|
|
|
11
11
|
|
|
12
12
|
Decoder = Data.define(:desc)
|
|
13
13
|
|
|
14
|
+
# Decoding returns the value itself, or one of these. Never escapes
|
|
15
|
+
# this file: `Runner` turns it into the Result callers see.
|
|
16
|
+
Failure = Struct.new(:error)
|
|
17
|
+
|
|
18
|
+
# Distinguishes a key that is absent from one whose value is nil.
|
|
19
|
+
ABSENT = Object.new
|
|
20
|
+
|
|
14
21
|
module Desc
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Flt = Data.define()
|
|
18
|
-
Bool = Data.define()
|
|
19
|
-
Pass = Data.define()
|
|
20
|
-
Nullable = Data.define(:inner)
|
|
21
|
-
Field = Data.define(:key, :inner)
|
|
22
|
-
OptField = Data.define(:key, :inner)
|
|
23
|
-
Optional = Data.define(:key, :inner, :default)
|
|
24
|
-
Idx = Data.define(:index, :inner)
|
|
25
|
-
Lst = Data.define(:inner)
|
|
26
|
-
Dct = Data.define(:k_inner, :v_inner)
|
|
27
|
-
Map = Data.define(:fn, :d)
|
|
28
|
-
Succeed = Data.define(:value)
|
|
29
|
-
AndMap = Data.define(:wrapped, :value_d)
|
|
30
|
-
Sequence = Data.define(:decoders)
|
|
31
|
-
OneOf = Data.define(:decoders)
|
|
32
|
-
AndThen = Data.define(:fn, :d)
|
|
33
|
-
Fail = Data.define(:msg)
|
|
34
|
-
Variant = Data.define(:cases)
|
|
35
|
-
end
|
|
22
|
+
module Node
|
|
23
|
+
def decoded?(v) = !(Failure === v)
|
|
36
24
|
|
|
37
|
-
|
|
38
|
-
extend self
|
|
25
|
+
private
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
def type_err(expected, got)
|
|
28
|
+
Failure.new(WrongType[expected, Decode.type_name(got)])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def coerce_hash(value)
|
|
32
|
+
case value
|
|
33
|
+
when ::Hash then value
|
|
34
|
+
when ::Data then value.to_h
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Ports hand back Symbol-keyed hashes and String-keyed ones, so
|
|
39
|
+
# both are read before deciding a key is absent.
|
|
40
|
+
def at_key(h, sym, key)
|
|
41
|
+
value = h[sym]
|
|
42
|
+
return value unless value.nil?
|
|
43
|
+
|
|
44
|
+
value = h[key]
|
|
45
|
+
return value unless value.nil?
|
|
46
|
+
|
|
47
|
+
h.key?(sym) || h.key?(key) ? nil : ABSENT
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def at_field(key, result)
|
|
51
|
+
decoded?(result) ? result : Failure.new(AtField[key, result.error])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def wrap_errors(errors)
|
|
55
|
+
errors.length == 1 ? errors.first : Multiple[errors]
|
|
45
56
|
end
|
|
46
|
-
run(decoder, parsed)
|
|
47
57
|
end
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
Str = Data.define() do
|
|
60
|
+
include Node
|
|
61
|
+
|
|
62
|
+
def decode(value)
|
|
63
|
+
::String === value ? value.dup : type_err('String', value)
|
|
64
|
+
end
|
|
51
65
|
end
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
Int = Data.define() do
|
|
68
|
+
include Node
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
def decode(value)
|
|
71
|
+
::Integer === value ? value : type_err('Int', value)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
Flt = Data.define() do
|
|
76
|
+
include Node
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
def decode(value)
|
|
79
|
+
::Numeric === value ? value.to_f : type_err('Float', value)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
Bool = Data.define() do
|
|
84
|
+
include Node
|
|
68
85
|
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
def decode(value)
|
|
87
|
+
value == true || value == false ? value : type_err('Bool', value)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
ok(nothing)
|
|
75
|
-
else
|
|
76
|
-
case interp(inner, value)
|
|
77
|
-
in Jade::Result::Ok[v] then ok(just(v))
|
|
78
|
-
in Jade::Result::Err => e then e
|
|
79
|
-
end
|
|
80
|
-
end
|
|
91
|
+
Pass = Data.define() do
|
|
92
|
+
include Node
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
def decode(value) = value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Nullable = Data.define(:inner) do
|
|
98
|
+
include Node
|
|
99
|
+
|
|
100
|
+
def decode(value)
|
|
101
|
+
return Jade::Maybe::Nothing[] if value.nil?
|
|
102
|
+
|
|
103
|
+
decoded = inner.decode(value)
|
|
104
|
+
decoded?(decoded) ? Jade::Maybe::Just[decoded] : decoded
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
Field = Data.define(:key, :inner) do
|
|
109
|
+
include Node
|
|
110
|
+
|
|
111
|
+
def decode(value)
|
|
83
112
|
h = coerce_hash(value)
|
|
84
|
-
return type_err(
|
|
113
|
+
return type_err('Object', value) unless h
|
|
85
114
|
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
wrap_at_field(key, interp(inner, h[sym]))
|
|
89
|
-
elsif h.key?(key)
|
|
90
|
-
wrap_at_field(key, interp(inner, h[key]))
|
|
91
|
-
else
|
|
92
|
-
err(Jade::Decode::MissingField[key.to_s])
|
|
93
|
-
end
|
|
115
|
+
raw = at_key(h, key.to_sym, key)
|
|
116
|
+
return Failure.new(MissingField[key.to_s]) if ABSENT.equal?(raw)
|
|
94
117
|
|
|
95
|
-
|
|
118
|
+
at_field(key.to_s, inner.decode(raw))
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
OptField = Data.define(:key, :inner) do
|
|
123
|
+
include Node
|
|
124
|
+
|
|
125
|
+
def decode(value)
|
|
96
126
|
h = coerce_hash(value)
|
|
97
|
-
return type_err(
|
|
127
|
+
return type_err('Object', value) unless h
|
|
98
128
|
|
|
99
|
-
|
|
100
|
-
if
|
|
101
|
-
wrap_opt_at_field(key, interp(inner, h[sym]))
|
|
102
|
-
elsif h.key?(key)
|
|
103
|
-
wrap_opt_at_field(key, interp(inner, h[key]))
|
|
104
|
-
else
|
|
105
|
-
ok(nothing)
|
|
106
|
-
end
|
|
129
|
+
raw = at_key(h, key.to_sym, key)
|
|
130
|
+
return Jade::Maybe::Nothing[] if ABSENT.equal?(raw)
|
|
107
131
|
|
|
108
|
-
|
|
132
|
+
decoded = inner.decode(raw)
|
|
133
|
+
decoded?(decoded) ? Jade::Maybe::Just[decoded] : Failure.new(AtField[key.to_s, decoded.error])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Optional = Data.define(:key, :inner, :default) do
|
|
138
|
+
include Node
|
|
139
|
+
|
|
140
|
+
def decode(value)
|
|
109
141
|
h = coerce_hash(value)
|
|
110
|
-
return type_err(
|
|
142
|
+
return type_err('Object', value) unless h
|
|
111
143
|
|
|
112
|
-
|
|
113
|
-
|
|
144
|
+
raw = at_key(h, key.to_sym, key)
|
|
145
|
+
return default if ABSENT.equal?(raw) || raw.nil?
|
|
114
146
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
end
|
|
122
|
-
end
|
|
147
|
+
at_field(key.to_s, inner.decode(raw))
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
Idx = Data.define(:index, :inner) do
|
|
152
|
+
include Node
|
|
123
153
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return
|
|
127
|
-
return err(Jade::Decode::MissingField["[#{index}]"]) if index >= arr.length
|
|
154
|
+
def decode(value)
|
|
155
|
+
return type_err('Array', value) unless ::Array === value
|
|
156
|
+
return Failure.new(MissingField["[#{index}]"]) if index >= value.length
|
|
128
157
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
158
|
+
decoded = inner.decode(value[index])
|
|
159
|
+
decoded?(decoded) ? decoded : Failure.new(AtIndex[index, decoded.error])
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
Lst = Data.define(:inner) do
|
|
164
|
+
include Node
|
|
165
|
+
|
|
166
|
+
def decode(value)
|
|
167
|
+
return type_err('Array', value) unless ::Array === value
|
|
168
|
+
|
|
169
|
+
size = value.length
|
|
170
|
+
values = ::Array.new(size)
|
|
171
|
+
errors = nil
|
|
172
|
+
i = 0
|
|
173
|
+
|
|
174
|
+
while i < size
|
|
175
|
+
decoded = inner.decode(value[i])
|
|
176
|
+
decoded?(decoded) ? values[i] = decoded : (errors ||= []) << AtIndex[i, decoded.error]
|
|
177
|
+
i += 1
|
|
132
178
|
end
|
|
133
179
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
180
|
+
errors ? Failure.new(wrap_errors(errors)) : values
|
|
181
|
+
end
|
|
182
|
+
end
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
184
|
+
RecordField = Data.define(:key, :sym, :inner)
|
|
185
|
+
|
|
186
|
+
Record = Data.define(:fields, :ctor) do
|
|
187
|
+
include Node
|
|
188
|
+
|
|
189
|
+
# Spells out `at_key` and `decoded?` rather than calling them:
|
|
190
|
+
# this runs once per field of every decoded row.
|
|
191
|
+
def decode(value)
|
|
192
|
+
h = coerce_hash(value)
|
|
193
|
+
return type_err('Object', value) unless h
|
|
194
|
+
|
|
195
|
+
size = fields.length
|
|
196
|
+
args = ::Array.new(size)
|
|
197
|
+
errors = nil
|
|
198
|
+
i = 0
|
|
199
|
+
|
|
200
|
+
while i < size
|
|
201
|
+
field = fields[i]
|
|
202
|
+
key = field.key
|
|
203
|
+
sym = field.sym
|
|
204
|
+
raw = h[sym]
|
|
205
|
+
raw = h[key] if raw.nil?
|
|
206
|
+
|
|
207
|
+
if raw.nil? && !h.key?(sym) && !h.key?(key)
|
|
208
|
+
(errors ||= []) << MissingField[key]
|
|
209
|
+
else
|
|
210
|
+
decoded = field.inner.decode(raw)
|
|
211
|
+
Failure === decoded ? (errors ||= []) << AtField[key, decoded.error] : args[i] = decoded
|
|
144
212
|
end
|
|
213
|
+
i += 1
|
|
145
214
|
end
|
|
146
|
-
errors.empty? ? ok(values) : err(wrap_errors(errors))
|
|
147
215
|
|
|
148
|
-
|
|
149
|
-
|
|
216
|
+
errors ? Failure.new(wrap_errors(errors)) : ctor.new(*args)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# `Record` by position: N indices of an array into an N-argument
|
|
221
|
+
# constructor. What the tuple decoders are built from.
|
|
222
|
+
Indexed = Data.define(:inners, :ctor) do
|
|
223
|
+
include Node
|
|
224
|
+
|
|
225
|
+
def decode(value)
|
|
226
|
+
return type_err('Array', value) unless ::Array === value
|
|
150
227
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
228
|
+
size = inners.length
|
|
229
|
+
given = value.length
|
|
230
|
+
args = ::Array.new(size)
|
|
231
|
+
errors = nil
|
|
232
|
+
i = 0
|
|
233
|
+
|
|
234
|
+
while i < size
|
|
235
|
+
if i >= given
|
|
236
|
+
(errors ||= []) << MissingField["[#{i}]"]
|
|
237
|
+
else
|
|
238
|
+
decoded = inners[i].decode(value[i])
|
|
239
|
+
Failure === decoded ? (errors ||= []) << AtIndex[i, decoded.error] : args[i] = decoded
|
|
240
|
+
end
|
|
241
|
+
i += 1
|
|
155
242
|
end
|
|
156
243
|
|
|
157
|
-
|
|
158
|
-
|
|
244
|
+
errors ? Failure.new(wrap_errors(errors)) : ctor.new(*args)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
159
247
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
fn.call(a)
|
|
164
|
-
}
|
|
248
|
+
# `parse` returns the value, or nil when the text is malformed.
|
|
249
|
+
FromString = Data.define(:label, :parse) do
|
|
250
|
+
include Node
|
|
165
251
|
|
|
166
|
-
|
|
167
|
-
|
|
252
|
+
def decode(value)
|
|
253
|
+
return type_err(label, value) unless ::String === value
|
|
168
254
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
255
|
+
parsed = parse.call(value)
|
|
256
|
+
parsed.nil? ? Failure.new(Custom["invalid #{label}: #{value}"]) : parsed
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
Dct = Data.define(:k_inner, :v_inner) do
|
|
261
|
+
include Node
|
|
262
|
+
|
|
263
|
+
# Two accepted wire shapes for Dict: a Hash (the natural Ruby form
|
|
264
|
+
# and what String-keyed JSON parses to) and an Array of [k, v]
|
|
265
|
+
# pairs (the form Encode.dict emits — survives non-String keys).
|
|
266
|
+
def decode(value)
|
|
267
|
+
if (h = coerce_hash(value))
|
|
268
|
+
entries(h.each_pair.map { |k, v| [[k, v], k.to_s] }, :at_field)
|
|
269
|
+
elsif ::Array === value
|
|
270
|
+
entries(value.each_with_index.map { |pair, i| [pair, i] }, :at_index)
|
|
271
|
+
else
|
|
272
|
+
type_err('Object or Array', value)
|
|
176
273
|
end
|
|
177
|
-
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
private
|
|
277
|
+
|
|
278
|
+
def entries(pairs, position)
|
|
279
|
+
h = {}
|
|
280
|
+
errors = []
|
|
281
|
+
|
|
282
|
+
pairs.each do |pair, pos|
|
|
283
|
+
case pair
|
|
284
|
+
in [k_raw, v_raw]
|
|
285
|
+
k = k_inner.decode(k_raw)
|
|
286
|
+
v = v_inner.decode(v_raw)
|
|
178
287
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
288
|
+
if decoded?(k) && decoded?(v)
|
|
289
|
+
h[k] = v
|
|
290
|
+
else
|
|
291
|
+
[k, v].each { errors << wrap_pos(position, pos, it.error) unless decoded?(it) }
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
else
|
|
295
|
+
errors << wrap_pos(position, pos, WrongType['Array[2]', Decode.type_name(pair)])
|
|
296
|
+
end
|
|
183
297
|
end
|
|
184
298
|
|
|
185
|
-
|
|
186
|
-
|
|
299
|
+
errors.empty? ? Jade::Dict::Dict[h] : Failure.new(wrap_errors(errors))
|
|
300
|
+
end
|
|
187
301
|
|
|
188
|
-
|
|
189
|
-
|
|
302
|
+
def wrap_pos(position, pos, inner)
|
|
303
|
+
position == :at_index ? AtIndex[pos, inner] : AtField[pos, inner]
|
|
190
304
|
end
|
|
191
305
|
end
|
|
192
306
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return err(Custom["empty variant array"]) if value.empty?
|
|
307
|
+
Map = Data.define(:fn, :d) do
|
|
308
|
+
include Node
|
|
196
309
|
|
|
197
|
-
|
|
198
|
-
|
|
310
|
+
def decode(value)
|
|
311
|
+
decoded = d.decode(value)
|
|
312
|
+
decoded?(decoded) ? fn.call(decoded) : decoded
|
|
313
|
+
end
|
|
314
|
+
end
|
|
199
315
|
|
|
200
|
-
|
|
201
|
-
|
|
316
|
+
Succeed = Data.define(:value) do
|
|
317
|
+
include Node
|
|
202
318
|
|
|
203
|
-
|
|
319
|
+
def decode(_value) = value
|
|
204
320
|
end
|
|
205
321
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def nothing = Jade::Maybe::Nothing[]
|
|
209
|
-
def just(v) = Jade::Maybe::Just[v]
|
|
322
|
+
AndMap = Data.define(:wrapped, :value_d) do
|
|
323
|
+
include Node
|
|
210
324
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
325
|
+
def decode(value)
|
|
326
|
+
fn = wrapped.decode(value)
|
|
327
|
+
arg = value_d.decode(value)
|
|
214
328
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
329
|
+
return fn.call(arg) if decoded?(fn) && decoded?(arg)
|
|
330
|
+
|
|
331
|
+
[fn, arg]
|
|
332
|
+
.reject { decoded?(it) }
|
|
333
|
+
.map(&:error)
|
|
334
|
+
.then { Failure.new(wrap_errors(it)) }
|
|
219
335
|
end
|
|
220
336
|
end
|
|
221
337
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
338
|
+
Sequence = Data.define(:decoders) do
|
|
339
|
+
include Node
|
|
340
|
+
|
|
341
|
+
def decode(value)
|
|
342
|
+
results = decoders.map { it.decode(value) }
|
|
343
|
+
errors = results.reject { decoded?(it) }
|
|
344
|
+
|
|
345
|
+
errors.empty? ? results : Failure.new(wrap_errors(errors.map(&:error)))
|
|
226
346
|
end
|
|
227
347
|
end
|
|
228
348
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
349
|
+
OneOf = Data.define(:decoders) do
|
|
350
|
+
include Node
|
|
351
|
+
|
|
352
|
+
def decode(value)
|
|
353
|
+
errors = []
|
|
354
|
+
|
|
355
|
+
decoders.each do
|
|
356
|
+
decoded = it.decode(value)
|
|
357
|
+
return decoded if decoded?(decoded)
|
|
358
|
+
|
|
359
|
+
errors << decoded.error
|
|
236
360
|
end
|
|
361
|
+
|
|
362
|
+
Failure.new(wrap_errors(errors))
|
|
237
363
|
end
|
|
238
|
-
errors.empty? ? ok(block.call(values)) : err(wrap_errors(errors))
|
|
239
364
|
end
|
|
240
365
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
end
|
|
366
|
+
AndThen = Data.define(:fn, :d) do
|
|
367
|
+
include Node
|
|
244
368
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def interp_dict(k_inner, v_inner, value)
|
|
249
|
-
if (h = coerce_hash(value))
|
|
250
|
-
h.each_pair.map { |k, v| [[k, v], k.to_s] }
|
|
251
|
-
.then { decode_dict_entries(k_inner, v_inner, it, :at_field) }
|
|
252
|
-
elsif (arr = coerce_array(value))
|
|
253
|
-
arr.each_with_index.map { |pair, i| [pair, i] }
|
|
254
|
-
.then { decode_dict_entries(k_inner, v_inner, it, :at_index) }
|
|
255
|
-
else
|
|
256
|
-
type_err("Object or Array", value)
|
|
369
|
+
def decode(value)
|
|
370
|
+
decoded = d.decode(value)
|
|
371
|
+
decoded?(decoded) ? fn.call(decoded).desc.decode(value) : decoded
|
|
257
372
|
end
|
|
258
373
|
end
|
|
259
374
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
case pair
|
|
265
|
-
in [k_raw, v_raw]
|
|
266
|
-
k_res = interp(k_inner, k_raw)
|
|
267
|
-
v_res = interp(v_inner, v_raw)
|
|
268
|
-
if k_res.is_a?(Jade::Result::Ok) && v_res.is_a?(Jade::Result::Ok)
|
|
269
|
-
h[k_res._1] = v_res._1
|
|
270
|
-
else
|
|
271
|
-
[k_res, v_res].each do |r|
|
|
272
|
-
errors << wrap_pos(position, pos, r._1) if r.is_a?(Jade::Result::Err)
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
else
|
|
276
|
-
errors << wrap_pos(position, pos, Jade::Decode::WrongType["Array[2]", ruby_type_name(pair)])
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
errors.empty? ? ok(Jade::Dict::Dict[h]) : err(wrap_errors(errors))
|
|
375
|
+
Fail = Data.define(:msg) do
|
|
376
|
+
include Node
|
|
377
|
+
|
|
378
|
+
def decode(_value) = Failure.new(Custom[msg])
|
|
280
379
|
end
|
|
281
380
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
381
|
+
Variant = Data.define(:cases) do
|
|
382
|
+
include Node
|
|
383
|
+
|
|
384
|
+
def decode(value)
|
|
385
|
+
return type_err('Array', value) unless ::Array === value
|
|
386
|
+
return Failure.new(Custom['empty variant array']) if value.empty?
|
|
387
|
+
|
|
388
|
+
tag = value.first
|
|
389
|
+
return type_err('String tag at index 0', tag) unless ::String === tag
|
|
390
|
+
|
|
391
|
+
inner = cases[tag]
|
|
392
|
+
return Failure.new(Custom["unknown variant: #{tag.inspect}"]) unless inner
|
|
393
|
+
|
|
394
|
+
inner.decode(value)
|
|
395
|
+
end
|
|
286
396
|
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
module Runner
|
|
400
|
+
extend self
|
|
287
401
|
|
|
288
|
-
def
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
402
|
+
def from_json(decoder, json_string)
|
|
403
|
+
begin
|
|
404
|
+
parsed = JSON.parse(json_string)
|
|
405
|
+
rescue JSON::ParserError => e
|
|
406
|
+
return Jade::Result::Err[Jade::Decode::WrongType['valid JSON', e.message]]
|
|
293
407
|
end
|
|
408
|
+
run(decoder, parsed)
|
|
294
409
|
end
|
|
295
410
|
|
|
296
|
-
def
|
|
297
|
-
|
|
411
|
+
def run(decoder, value)
|
|
412
|
+
decoded = decoder.desc.decode(value)
|
|
413
|
+
|
|
414
|
+
Failure === decoded ? Jade::Result::Err[decoded.error] : Jade::Result::Ok[decoded]
|
|
298
415
|
end
|
|
299
416
|
|
|
300
|
-
|
|
301
|
-
|
|
417
|
+
# The decoded value or a raise — no Result for the caller to unwrap.
|
|
418
|
+
def run!(decoder, value)
|
|
419
|
+
decoded = decoder.desc.decode(value)
|
|
420
|
+
|
|
421
|
+
Failure === decoded ? yield(decoded.error) : decoded
|
|
302
422
|
end
|
|
303
423
|
end
|
|
304
424
|
|
|
@@ -146,7 +146,8 @@ module Jade
|
|
|
146
146
|
|
|
147
147
|
in AST::VariableReference | AST::ConstructorReference | AST::TypeDeclaration |
|
|
148
148
|
AST::ImportDeclaration | AST::Literal | AST::CharLiteral | AST::RecordAccessSugar | AST::InteropImportDeclaration |
|
|
149
|
-
AST::StructDeclaration | AST::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration
|
|
149
|
+
AST::StructDeclaration | AST::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration |
|
|
150
|
+
AST::RecordUpdateSugar
|
|
150
151
|
|
|
151
152
|
node
|
|
152
153
|
|
|
@@ -164,7 +165,7 @@ module Jade
|
|
|
164
165
|
flatten(left) + [operator] + flatten(right)
|
|
165
166
|
|
|
166
167
|
else
|
|
167
|
-
[ast]
|
|
168
|
+
[fix(ast)]
|
|
168
169
|
end
|
|
169
170
|
end
|
|
170
171
|
|
|
@@ -3,11 +3,12 @@ module Jade
|
|
|
3
3
|
module SemanticAnalysis
|
|
4
4
|
module Error
|
|
5
5
|
class VariableNotFound < Jade::Error
|
|
6
|
-
attr_reader :causes
|
|
6
|
+
attr_reader :causes, :candidates
|
|
7
7
|
|
|
8
|
-
def initialize(entry, span, name:, causes: [])
|
|
8
|
+
def initialize(entry, span, name:, causes: [], candidates: [])
|
|
9
9
|
@name = name
|
|
10
10
|
@causes = causes
|
|
11
|
+
@candidates = candidates
|
|
11
12
|
super(entry:, span:)
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -18,6 +19,12 @@ module Jade
|
|
|
18
19
|
def label
|
|
19
20
|
"not found"
|
|
20
21
|
end
|
|
22
|
+
|
|
23
|
+
# Qualified, like `candidates`: bare `fold_left` against `fold` is
|
|
24
|
+
# too short to clear the spell checker's threshold.
|
|
25
|
+
def queried_name
|
|
26
|
+
@name
|
|
27
|
+
end
|
|
21
28
|
end
|
|
22
29
|
end
|
|
23
30
|
end
|