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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +164 -0
  3. data/CHANGELOG.md +180 -1
  4. data/README.md +5 -1
  5. data/docs/interop.md +208 -0
  6. data/docs/json.md +163 -0
  7. data/docs/lsp.md +105 -0
  8. data/docs/stdlib.md +69 -0
  9. data/docs/syntax.md +458 -0
  10. data/docs/testing.md +70 -0
  11. data/lib/jade/api.rb +244 -0
  12. data/lib/jade/cli/check.rb +97 -0
  13. data/lib/jade/cli/q.rb +47 -1
  14. data/lib/jade/cli.rb +4 -2
  15. data/lib/jade/codegen/boundary/cache.rb +1 -2
  16. data/lib/jade/codegen/boundary/specialized/list.rb +9 -4
  17. data/lib/jade/codegen/boundary/specialized/maybe.rb +15 -3
  18. data/lib/jade/codegen/boundary/specialized/record.rb +7 -3
  19. data/lib/jade/codegen/boundary/specialized.rb +7 -3
  20. data/lib/jade/codegen/emitter.rb +11 -2
  21. data/lib/jade/codegen/function_declaration.rb +7 -6
  22. data/lib/jade/codegen/helpers.rb +6 -0
  23. data/lib/jade/codegen/inlines.rb +5 -0
  24. data/lib/jade/decode.rb +332 -212
  25. data/lib/jade/frontend/fixity_fixer.rb +3 -2
  26. data/lib/jade/frontend/semantic_analysis/error/variable_not_found.rb +9 -2
  27. data/lib/jade/frontend/semantic_analysis/member_access.rb +11 -1
  28. data/lib/jade/frontend/type_checking/constraints/deriving/decodable.rb +10 -15
  29. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +7 -16
  30. data/lib/jade/interop/boundary.rb +12 -4
  31. data/lib/jade/interop/error.rb +25 -5
  32. data/lib/jade/lsp/converters.rb +2 -15
  33. data/lib/jade/lsp/snippets.rb +21 -3
  34. data/lib/jade/module_loader/module_name.rb +46 -0
  35. data/lib/jade/module_loader.rb +5 -0
  36. data/lib/jade/signature.rb +47 -0
  37. data/lib/jade/stdlib/calendar.rb +20 -19
  38. data/lib/jade/stdlib/clock.rb +7 -13
  39. data/lib/jade/stdlib/decimal.rb +3 -16
  40. data/lib/jade/stdlib/decode.rb +35 -12
  41. data/lib/jade/stdlib/encode.rb +9 -0
  42. data/lib/jade/stdlib/intrinsics.rb +9 -1
  43. data/lib/jade/stdlib/result.rb +1 -1
  44. data/lib/jade/stdlib/text.rb +131 -0
  45. data/lib/jade/task.rb +2 -3
  46. data/lib/jade/version.rb +1 -1
  47. 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
- Str = Data.define()
16
- Int = Data.define()
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
- module Runner
38
- extend self
25
+ private
39
26
 
40
- def from_json(decoder, json_string)
41
- begin
42
- parsed = JSON.parse(json_string)
43
- rescue JSON::ParserError => e
44
- return err(Jade::Decode::WrongType["valid JSON", e.message])
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
- def run(decoder, value)
50
- interp(decoder.desc, value)
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
- private
67
+ Int = Data.define() do
68
+ include Node
54
69
 
55
- def interp(desc, value)
56
- case desc
57
- in Desc::Str[]
58
- value.is_a?(::String) ? ok(value.dup) : type_err("String", value)
70
+ def decode(value)
71
+ ::Integer === value ? value : type_err('Int', value)
72
+ end
73
+ end
59
74
 
60
- in Desc::Int[]
61
- value.is_a?(::Integer) && !value.is_a?(::Float) ? ok(value) : type_err("Int", value)
75
+ Flt = Data.define() do
76
+ include Node
62
77
 
63
- in Desc::Flt[]
64
- value.is_a?(::Numeric) ? ok(value.to_f) : type_err("Float", value)
78
+ def decode(value)
79
+ ::Numeric === value ? value.to_f : type_err('Float', value)
80
+ end
81
+ end
65
82
 
66
- in Desc::Bool[]
67
- value == true || value == false ? ok(value) : type_err("Bool", value)
83
+ Bool = Data.define() do
84
+ include Node
68
85
 
69
- in Desc::Pass[]
70
- ok(value)
86
+ def decode(value)
87
+ value == true || value == false ? value : type_err('Bool', value)
88
+ end
89
+ end
71
90
 
72
- in Desc::Nullable[inner]
73
- if value.nil?
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
- in Desc::Field[key, inner]
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("Object", value) unless h
113
+ return type_err('Object', value) unless h
85
114
 
86
- sym = key.to_sym
87
- if h.key?(sym)
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
- in Desc::OptField[key, inner]
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("Object", value) unless h
127
+ return type_err('Object', value) unless h
98
128
 
99
- sym = key.to_sym
100
- if h.key?(sym)
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
- in Desc::Optional[key, inner, default]
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("Object", value) unless h
142
+ return type_err('Object', value) unless h
111
143
 
112
- sym = key.to_sym
113
- raw = h.fetch(sym) { h.fetch(key) { :__absent__ } }
144
+ raw = at_key(h, key.to_sym, key)
145
+ return default if ABSENT.equal?(raw) || raw.nil?
114
146
 
115
- if raw == :__absent__ || raw.nil?
116
- ok(default)
117
- else
118
- case interp(inner, raw)
119
- in Jade::Result::Ok => r then r
120
- in Jade::Result::Err[e] then err(Jade::Decode::AtField[key.to_s, e])
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
- in Desc::Idx[index, inner]
125
- arr = coerce_array(value)
126
- return type_err("Array", value) unless arr
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
- case interp(inner, arr[index])
130
- in Jade::Result::Ok => r then r
131
- in Jade::Result::Err[e] then err(Jade::Decode::AtIndex[index, e])
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
- in Desc::Lst[inner]
135
- arr = coerce_array(value)
136
- return type_err("Array", value) unless arr
180
+ errors ? Failure.new(wrap_errors(errors)) : values
181
+ end
182
+ end
137
183
 
138
- values = []
139
- errors = []
140
- arr.each_with_index do |v, i|
141
- case interp(inner, v)
142
- in Jade::Result::Ok[decoded] then values << decoded
143
- in Jade::Result::Err[e] then errors << Jade::Decode::AtIndex[i, e]
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
- in Desc::Dct[k_inner, v_inner]
149
- interp_dict(k_inner, v_inner, value)
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
- in Desc::Map[fn, d]
152
- case interp(d, value)
153
- in Jade::Result::Ok[v] then ok(fn.call(v))
154
- in Jade::Result::Err => e then e
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
- in Desc::Succeed[v]
158
- ok(v)
244
+ errors ? Failure.new(wrap_errors(errors)) : ctor.new(*args)
245
+ end
246
+ end
159
247
 
160
- in Desc::AndMap[wrapped, value_d]
161
- combine([interp(wrapped, value), interp(value_d, value)]) { |fn_and_a|
162
- fn, a = fn_and_a
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
- in Desc::Sequence[ds]
167
- combine(ds.map { interp(it, value) }) { it }
252
+ def decode(value)
253
+ return type_err(label, value) unless ::String === value
168
254
 
169
- in Desc::OneOf[ds]
170
- errors = []
171
- ds.each do |d|
172
- case interp(d, value)
173
- in Jade::Result::Ok => r then return r
174
- in Jade::Result::Err[e] then errors << e
175
- end
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
- err(wrap_errors(errors))
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
- in Desc::AndThen[fn, d]
180
- case interp(d, value)
181
- in Jade::Result::Ok[v] then interp(fn.call(v).desc, value)
182
- in Jade::Result::Err => e then e
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
- in Desc::Fail[msg]
186
- err(Jade::Decode::Custom[msg])
299
+ errors.empty? ? Jade::Dict::Dict[h] : Failure.new(wrap_errors(errors))
300
+ end
187
301
 
188
- in Desc::Variant[cases]
189
- interp_variant(cases, value)
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
- def interp_variant(cases, value)
194
- return type_err('Array', value) unless value.is_a?(::Array)
195
- return err(Custom["empty variant array"]) if value.empty?
307
+ Map = Data.define(:fn, :d) do
308
+ include Node
196
309
 
197
- tag = value.first
198
- return type_err('String tag at index 0', tag) unless tag.is_a?(::String)
310
+ def decode(value)
311
+ decoded = d.decode(value)
312
+ decoded?(decoded) ? fn.call(decoded) : decoded
313
+ end
314
+ end
199
315
 
200
- inner = cases[tag]
201
- return err(Custom["unknown variant: #{tag.inspect}"]) unless inner
316
+ Succeed = Data.define(:value) do
317
+ include Node
202
318
 
203
- interp(inner, value)
319
+ def decode(_value) = value
204
320
  end
205
321
 
206
- def ok(v) = Jade::Result::Ok[v]
207
- def err(e) = Jade::Result::Err[e]
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
- def type_err(expected, got)
212
- err(Jade::Decode::WrongType[expected, ruby_type_name(got)])
213
- end
325
+ def decode(value)
326
+ fn = wrapped.decode(value)
327
+ arg = value_d.decode(value)
214
328
 
215
- def wrap_at_field(key, result)
216
- case result
217
- in Jade::Result::Ok => r then r
218
- in Jade::Result::Err[e] then err(Jade::Decode::AtField[key.to_s, e])
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
- def wrap_opt_at_field(key, result)
223
- case result
224
- in Jade::Result::Ok[v] then ok(just(v))
225
- in Jade::Result::Err[e] then err(Jade::Decode::AtField[key.to_s, e])
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
- def combine(results, &block)
230
- values = []
231
- errors = []
232
- results.each do |r|
233
- case r
234
- in Jade::Result::Ok[v] then values << v
235
- in Jade::Result::Err[e] then errors << e
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
- def wrap_errors(errors)
242
- errors.length == 1 ? errors.first : Jade::Decode::Multiple[errors]
243
- end
366
+ AndThen = Data.define(:fn, :d) do
367
+ include Node
244
368
 
245
- # Two accepted wire shapes for Dict: a Hash (the natural Ruby form
246
- # and what String-keyed JSON parses to) and an Array of [k, v] pairs
247
- # (the form Encode.dict emits — survives non-String keys).
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
- def decode_dict_entries(k_inner, v_inner, entries, position)
261
- h = {}
262
- errors = []
263
- entries.each do |pair, pos|
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
- def wrap_pos(position, pos, inner)
283
- position == :at_index \
284
- ? Jade::Decode::AtIndex[pos, inner]
285
- : Jade::Decode::AtField[pos, inner]
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 coerce_hash(value)
289
- case value
290
- when ::Hash then value
291
- when ::Data then value.to_h
292
- else nil
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 coerce_array(value)
297
- value.is_a?(::Array) ? value : nil
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
- def ruby_type_name(v)
301
- Jade::Decode.type_name(v)
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