morphir-python 0.0.1__py3-none-any.whl

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 (99) hide show
  1. morphir/__init__.py +14 -0
  2. morphir/ir/__init__.py +110 -0
  3. morphir/ir/access_controlled.py +15 -0
  4. morphir/ir/decorations.py +19 -0
  5. morphir/ir/diagnostics.py +105 -0
  6. morphir/ir/distribution.py +21 -0
  7. morphir/ir/document.py +37 -0
  8. morphir/ir/documented.py +11 -0
  9. morphir/ir/format_version.py +483 -0
  10. morphir/ir/fqname.py +9 -0
  11. morphir/ir/json.py +11 -0
  12. morphir/ir/literal.py +25 -0
  13. morphir/ir/meta.py +11 -0
  14. morphir/ir/migrate.py +1267 -0
  15. morphir/ir/module.py +17 -0
  16. morphir/ir/name.py +27 -0
  17. morphir/ir/package.py +15 -0
  18. morphir/ir/path.py +25 -0
  19. morphir/ir/qname.py +9 -0
  20. morphir/ir/ref.py +17 -0
  21. morphir/ir/type.py +37 -0
  22. morphir/ir/type_constraints.py +33 -0
  23. morphir/ir/type_def.py +19 -0
  24. morphir/ir/type_spec.py +25 -0
  25. morphir/ir/v3/__init__.py +64 -0
  26. morphir/ir/v3/_legacy_value.py +201 -0
  27. morphir/ir/v3/access_controlled.py +31 -0
  28. morphir/ir/v3/codec.py +1275 -0
  29. morphir/ir/v3/codec_v1.py +491 -0
  30. morphir/ir/v3/codec_v2.py +187 -0
  31. morphir/ir/v3/distribution.py +42 -0
  32. morphir/ir/v3/documented.py +13 -0
  33. morphir/ir/v3/fqname.py +30 -0
  34. morphir/ir/v3/literal.py +46 -0
  35. morphir/ir/v3/module.py +32 -0
  36. morphir/ir/v3/name.py +42 -0
  37. morphir/ir/v3/package.py +23 -0
  38. morphir/ir/v3/path.py +29 -0
  39. morphir/ir/v3/qname.py +16 -0
  40. morphir/ir/v3/type.py +126 -0
  41. morphir/ir/v3/value.py +235 -0
  42. morphir/ir/v4/__init__.py +160 -0
  43. morphir/ir/v4/access_controlled.py +17 -0
  44. morphir/ir/v4/annotation.py +75 -0
  45. morphir/ir/v4/codec.py +2161 -0
  46. morphir/ir/v4/decorations.py +88 -0
  47. morphir/ir/v4/distribution.py +56 -0
  48. morphir/ir/v4/document.py +69 -0
  49. morphir/ir/v4/documented.py +10 -0
  50. morphir/ir/v4/fqname.py +69 -0
  51. morphir/ir/v4/incompleteness.py +78 -0
  52. morphir/ir/v4/json.py +103 -0
  53. morphir/ir/v4/literal.py +51 -0
  54. morphir/ir/v4/meta.py +27 -0
  55. morphir/ir/v4/module.py +30 -0
  56. morphir/ir/v4/name.py +69 -0
  57. morphir/ir/v4/package.py +19 -0
  58. morphir/ir/v4/path.py +50 -0
  59. morphir/ir/v4/qname.py +34 -0
  60. morphir/ir/v4/ref.py +35 -0
  61. morphir/ir/v4/type.py +140 -0
  62. morphir/ir/v4/type_constraints.py +91 -0
  63. morphir/ir/v4/type_def.py +38 -0
  64. morphir/ir/v4/type_spec.py +64 -0
  65. morphir/ir/v4/value.py +480 -0
  66. morphir/ir/value.py +81 -0
  67. morphir/py.typed +0 -0
  68. morphir/sdk/__init__.py +110 -0
  69. morphir/sdk/_compare.py +187 -0
  70. morphir/sdk/aggregate.py +421 -0
  71. morphir/sdk/basics.py +504 -0
  72. morphir/sdk/char.py +110 -0
  73. morphir/sdk/decimal.py +311 -0
  74. morphir/sdk/dict.py +336 -0
  75. morphir/sdk/instant.py +118 -0
  76. morphir/sdk/int.py +86 -0
  77. morphir/sdk/key.py +522 -0
  78. morphir/sdk/list.py +356 -0
  79. morphir/sdk/local_date.py +436 -0
  80. morphir/sdk/local_time.py +267 -0
  81. morphir/sdk/maybe.py +135 -0
  82. morphir/sdk/number.py +237 -0
  83. morphir/sdk/regex.py +287 -0
  84. morphir/sdk/result.py +162 -0
  85. morphir/sdk/result_list.py +169 -0
  86. morphir/sdk/rule.py +103 -0
  87. morphir/sdk/set.py +214 -0
  88. morphir/sdk/stateful_app.py +49 -0
  89. morphir/sdk/string.py +427 -0
  90. morphir/sdk/tuple.py +43 -0
  91. morphir/sdk/uuid.py +209 -0
  92. morphir_python-0.0.1.dist-info/METADATA +269 -0
  93. morphir_python-0.0.1.dist-info/RECORD +99 -0
  94. morphir_python-0.0.1.dist-info/WHEEL +4 -0
  95. morphir_python-0.0.1.dist-info/entry_points.txt +2 -0
  96. morphir_python-0.0.1.dist-info/licenses/LICENSE +201 -0
  97. morphir_tools/__init__.py +13 -0
  98. morphir_tools/cli.py +223 -0
  99. morphir_tools/py.typed +0 -0
morphir/ir/v3/codec.py ADDED
@@ -0,0 +1,1275 @@
1
+ """JSON codecs for Morphir IR format 3 (3.0.0 and 3.1.0).
2
+
3
+ Each section mirrors one morphir-elm codec module at commit bc99af69
4
+ (``src/Morphir/IR/*/Codec.elm``). The ``Specs`` distribution of format 3.1.0,
5
+ which elm does not have, follows the finos/morphir v3 specification
6
+ (``docs/spec/ir/schemas/v3/whats-new.md``) and morphir-rust commit cd9f7fb. As in elm, node codecs take attribute codecs as
7
+ parameters, so untyped fragments such as ``["Literal", {}, ["WholeNumberLiteral",
8
+ 42]]`` decode with `decode_unit`. Dictionaries are written sorted by key, as elm's
9
+ ``Dict.toList`` does.
10
+
11
+ The only intended departure from elm for 3.0.0 content is the ``formatVersion``
12
+ reader, which is `morphir.ir.format_version` (elm accepts only an integer).
13
+
14
+ Decoders raise `DiagnosticError` with a location path from the decoded value.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Callable
20
+ from decimal import Decimal, InvalidOperation
21
+ from typing import Any
22
+
23
+ from .. import diagnostics as d
24
+ from ..diagnostics import DiagnosticError, Location, fail
25
+ from ..format_version import (
26
+ V3,
27
+ V3_1,
28
+ V3_TABLE,
29
+ FormatVersion,
30
+ read_document,
31
+ write_format_version,
32
+ )
33
+ from . import literal as lit
34
+ from . import module, package
35
+ from . import type as t
36
+ from . import value as v
37
+ from .access_controlled import Access, AccessControlled
38
+ from .distribution import Distribution, Library, Specs
39
+ from .documented import Documented
40
+ from .fqname import FQName
41
+ from .name import Name
42
+ from .path import Path
43
+ from .qname import QName
44
+
45
+ type Json = Any
46
+ type Encoder[A] = Callable[[A], Json]
47
+ type Decoder[A] = Callable[[object, Location], A]
48
+
49
+ # --- Primitive helpers ---
50
+
51
+
52
+ def _expect_list(j: object, loc: Location, construct: str) -> list[object]:
53
+ if not isinstance(j, list):
54
+ raise fail(d.INVALID_JSON, construct, loc, f"expected an array for {construct}")
55
+ return j
56
+
57
+
58
+ def _expect_object(j: object, loc: Location, construct: str) -> dict[str, object]:
59
+ if not isinstance(j, dict):
60
+ raise fail(
61
+ d.INVALID_JSON, construct, loc, f"expected an object for {construct}"
62
+ )
63
+ return j
64
+
65
+
66
+ def _expect_str(j: object, loc: Location, construct: str) -> str:
67
+ if not isinstance(j, str):
68
+ raise fail(d.INVALID_JSON, construct, loc, f"expected a string for {construct}")
69
+ return j
70
+
71
+
72
+ def _index(items: list[object], i: int, loc: Location, construct: str) -> object:
73
+ if i >= len(items):
74
+ raise fail(
75
+ d.MISSING_MEMBER, construct, loc, f"{construct} has no element at index {i}"
76
+ )
77
+ return items[i]
78
+
79
+
80
+ def _field(obj: dict[str, object], key: str, loc: Location, construct: str) -> object:
81
+ if key not in obj:
82
+ raise fail(
83
+ d.MISSING_MEMBER, construct, loc, f"{construct} has no member {key!r}"
84
+ )
85
+ return obj[key]
86
+
87
+
88
+ def _tag(j: object, loc: Location, construct: str) -> tuple[str, list[object]]:
89
+ items = _expect_list(j, loc, construct)
90
+ tag = _expect_str(_index(items, 0, loc, construct), (*loc, 0), construct)
91
+ return tag, items
92
+
93
+
94
+ def _unknown(tag: str, loc: Location, construct: str) -> DiagnosticError:
95
+ return fail(d.UNKNOWN_NODE, construct, loc, f"unknown {construct} kind {tag!r}")
96
+
97
+
98
+ def _list_of[A](
99
+ decode: Decoder[A], j: object, loc: Location, construct: str
100
+ ) -> tuple[A, ...]:
101
+ items = _expect_list(j, loc, construct)
102
+ return tuple(decode(item, (*loc, i)) for i, item in enumerate(items))
103
+
104
+
105
+ def _pair_list_to_dict[K, A](
106
+ decode_key: Decoder[K],
107
+ decode_value: Decoder[A],
108
+ j: object,
109
+ loc: Location,
110
+ construct: str,
111
+ ) -> dict[K, A]:
112
+ result: dict[K, A] = {}
113
+ for i, entry in enumerate(_expect_list(j, loc, construct)):
114
+ here = (*loc, i)
115
+ pair = _expect_list(entry, here, construct)
116
+ key = decode_key(_index(pair, 0, here, construct), (*here, 0))
117
+ result[key] = decode_value(_index(pair, 1, here, construct), (*here, 1))
118
+ return result
119
+
120
+
121
+ def _sorted_pairs[K: tuple[Any, ...], A](
122
+ items: dict[K, A], encode_key: Callable[[K], Json], encode_value: Encoder[A]
123
+ ) -> list[Json]:
124
+ return [
125
+ [encode_key(key), encode_value(value)] for key, value in sorted(items.items())
126
+ ]
127
+
128
+
129
+ # --- Morphir.Codec ---
130
+
131
+
132
+ def encode_unit(_: None) -> Json:
133
+ """Encode elm's unit attribute as an empty object."""
134
+ return {}
135
+
136
+
137
+ def decode_unit(_: object, __: Location = ()) -> None:
138
+ """Decode elm's unit attribute; any JSON value is accepted, as in elm."""
139
+ return None
140
+
141
+
142
+ # --- Name, Path, QName, FQName ---
143
+
144
+
145
+ def encode_name(name: Name) -> Json:
146
+ return list(name)
147
+
148
+
149
+ def decode_name(j: object, loc: Location = ()) -> Name:
150
+ words = _expect_list(j, loc, "Name")
151
+ for i, word in enumerate(words):
152
+ if not isinstance(word, str):
153
+ raise fail(d.INVALID_NAME, "Name", (*loc, i), "name words must be strings")
154
+ return Name(tuple(str(w) for w in words))
155
+
156
+
157
+ def encode_path(path: Path) -> Json:
158
+ return [encode_name(n) for n in path]
159
+
160
+
161
+ def decode_path(j: object, loc: Location = ()) -> Path:
162
+ return Path(_list_of(decode_name, j, loc, "Path"))
163
+
164
+
165
+ def encode_qname(qn: QName) -> Json:
166
+ return [encode_path(qn.module_path), encode_name(qn.local_name)]
167
+
168
+
169
+ def decode_qname(j: object, loc: Location = ()) -> QName:
170
+ items = _expect_list(j, loc, "QName")
171
+ return QName(
172
+ decode_path(_index(items, 0, loc, "QName"), (*loc, 0)),
173
+ decode_name(_index(items, 1, loc, "QName"), (*loc, 1)),
174
+ )
175
+
176
+
177
+ def encode_fqname(fqn: FQName) -> Json:
178
+ return [
179
+ encode_path(fqn.package_path),
180
+ encode_path(fqn.module_path),
181
+ encode_name(fqn.local_name),
182
+ ]
183
+
184
+
185
+ def decode_fqname(j: object, loc: Location = ()) -> FQName:
186
+ items = _expect_list(j, loc, "FQName")
187
+ return FQName(
188
+ decode_path(_index(items, 0, loc, "FQName"), (*loc, 0)),
189
+ decode_path(_index(items, 1, loc, "FQName"), (*loc, 1)),
190
+ decode_name(_index(items, 2, loc, "FQName"), (*loc, 2)),
191
+ )
192
+
193
+
194
+ # --- AccessControlled, Documented ---
195
+
196
+
197
+ def encode_access_controlled[A](
198
+ encode_value: Encoder[A],
199
+ ) -> Encoder[AccessControlled[A]]:
200
+ def encode(ac: AccessControlled[A]) -> Json:
201
+ return {"access": ac.access.value, "value": encode_value(ac.value)}
202
+
203
+ return encode
204
+
205
+
206
+ def decode_access_controlled[A](
207
+ decode_value: Decoder[A],
208
+ ) -> Decoder[AccessControlled[A]]:
209
+ def decode(j: object, loc: Location = ()) -> AccessControlled[A]:
210
+ obj = _expect_object(j, loc, "AccessControlled")
211
+ access = _expect_str(
212
+ _field(obj, "access", loc, "AccessControlled"),
213
+ (*loc, "access"),
214
+ "AccessControlled",
215
+ )
216
+ try:
217
+ level = Access(access)
218
+ except ValueError:
219
+ raise _unknown(access, (*loc, "access"), "AccessControlled") from None
220
+ return AccessControlled(
221
+ level,
222
+ decode_value(
223
+ _field(obj, "value", loc, "AccessControlled"), (*loc, "value")
224
+ ),
225
+ )
226
+
227
+ return decode
228
+
229
+
230
+ def encode_documented[A](encode_value: Encoder[A]) -> Encoder[Documented[A]]:
231
+ def encode(doc: Documented[A]) -> Json:
232
+ return {"doc": doc.doc, "value": encode_value(doc.value)}
233
+
234
+ return encode
235
+
236
+
237
+ def decode_documented[A](decode_value: Decoder[A]) -> Decoder[Documented[A]]:
238
+ """Decode ``{"doc", "value"}``, or an undocumented value with doc ``""``."""
239
+
240
+ def decode(j: object, loc: Location = ()) -> Documented[A]:
241
+ if isinstance(j, dict) and isinstance(j.get("doc"), str) and "value" in j:
242
+ return Documented(str(j["doc"]), decode_value(j["value"], (*loc, "value")))
243
+ return Documented("", decode_value(j, loc))
244
+
245
+ return decode
246
+
247
+
248
+ # --- Literal ---
249
+
250
+
251
+ def encode_literal(literal: lit.Literal) -> Json:
252
+ match literal:
253
+ case lit.BoolLiteral(value):
254
+ return ["BoolLiteral", value]
255
+ case lit.CharLiteral(value):
256
+ return ["CharLiteral", value]
257
+ case lit.StringLiteral(value):
258
+ return ["StringLiteral", value]
259
+ case lit.WholeNumberLiteral(value):
260
+ return ["WholeNumberLiteral", value]
261
+ case lit.FloatLiteral(value):
262
+ return ["FloatLiteral", value]
263
+ case lit.DecimalLiteral(value):
264
+ return ["DecimalLiteral", str(value)]
265
+
266
+
267
+ def decode_literal(j: object, loc: Location = ()) -> lit.Literal:
268
+ tag, items = _tag(j, loc, "Literal")
269
+ payload = _index(items, 1, loc, "Literal")
270
+ here = (*loc, 1)
271
+
272
+ def bad(message: str) -> DiagnosticError:
273
+ return fail(d.INVALID_LITERAL, tag, here, message)
274
+
275
+ match tag:
276
+ case "BoolLiteral":
277
+ if not isinstance(payload, bool):
278
+ raise bad("BoolLiteral expects a boolean")
279
+ return lit.BoolLiteral(payload)
280
+ case "CharLiteral":
281
+ if not isinstance(payload, str) or not payload:
282
+ raise bad("CharLiteral expects a single character")
283
+ return lit.CharLiteral(payload[0])
284
+ case "StringLiteral":
285
+ if not isinstance(payload, str):
286
+ raise bad("StringLiteral expects a string")
287
+ return lit.StringLiteral(payload)
288
+ case "WholeNumberLiteral":
289
+ if isinstance(payload, bool) or not isinstance(payload, int):
290
+ raise bad("WholeNumberLiteral expects an integer")
291
+ return lit.WholeNumberLiteral(payload)
292
+ case "FloatLiteral":
293
+ if isinstance(payload, bool) or not isinstance(payload, (int, float)):
294
+ raise bad("FloatLiteral expects a number")
295
+ return lit.FloatLiteral(float(payload))
296
+ case "DecimalLiteral":
297
+ if not isinstance(payload, str):
298
+ raise bad("DecimalLiteral expects a string")
299
+ try:
300
+ return lit.DecimalLiteral(Decimal(payload))
301
+ except InvalidOperation:
302
+ raise bad(f"invalid decimal {payload!r}") from None
303
+ case _:
304
+ raise _unknown(tag, loc, "Literal")
305
+
306
+
307
+ # --- Type ---
308
+
309
+
310
+ def encode_type[A](encode_attributes: Encoder[A]) -> Encoder[t.Type[A]]:
311
+ def encode(tpe: t.Type[A]) -> Json:
312
+ a = encode_attributes(tpe.attributes)
313
+ match tpe:
314
+ case t.Variable(_, name):
315
+ return ["Variable", a, encode_name(name)]
316
+ case t.Reference(_, fqn, args):
317
+ return ["Reference", a, encode_fqname(fqn), [encode(x) for x in args]]
318
+ case t.Tuple(_, elements):
319
+ return ["Tuple", a, [encode(x) for x in elements]]
320
+ case t.Record(_, fields):
321
+ return ["Record", a, [encode_field(f) for f in fields]]
322
+ case t.ExtensibleRecord(_, var, fields):
323
+ return [
324
+ "ExtensibleRecord",
325
+ a,
326
+ encode_name(var),
327
+ [encode_field(f) for f in fields],
328
+ ]
329
+ case t.Function(_, arg, ret):
330
+ return ["Function", a, encode(arg), encode(ret)]
331
+ case t.Unit(_):
332
+ return ["Unit", a]
333
+
334
+ def encode_field(f: t.Field[A]) -> Json:
335
+ return {"name": encode_name(f.name), "tpe": encode(f.tpe)}
336
+
337
+ return encode
338
+
339
+
340
+ def decode_type[A](decode_attributes: Decoder[A]) -> Decoder[t.Type[A]]:
341
+ def decode(j: object, loc: Location = ()) -> t.Type[A]:
342
+ tag, items = _tag(j, loc, "Type")
343
+
344
+ def at(i: int) -> tuple[object, Location]:
345
+ return _index(items, i, loc, tag), (*loc, i)
346
+
347
+ attrs = decode_attributes(*at(1))
348
+ match tag:
349
+ case "Variable":
350
+ return t.Variable(attrs, decode_name(*at(2)))
351
+ case "Reference":
352
+ return t.Reference(
353
+ attrs, decode_fqname(*at(2)), _list_of(decode, *at(3), "Type")
354
+ )
355
+ case "Tuple":
356
+ return t.Tuple(attrs, _list_of(decode, *at(2), "Type"))
357
+ case "Record":
358
+ return t.Record(attrs, _list_of(decode_field, *at(2), "Field"))
359
+ case "ExtensibleRecord":
360
+ return t.ExtensibleRecord(
361
+ attrs, decode_name(*at(2)), _list_of(decode_field, *at(3), "Field")
362
+ )
363
+ case "Function":
364
+ return t.Function(attrs, decode(*at(2)), decode(*at(3)))
365
+ case "Unit":
366
+ return t.Unit(attrs)
367
+ case _:
368
+ raise _unknown(tag, loc, "Type")
369
+
370
+ def decode_field(j: object, loc: Location = ()) -> t.Field[A]:
371
+ obj = _expect_object(j, loc, "Field")
372
+ return t.Field(
373
+ decode_name(_field(obj, "name", loc, "Field"), (*loc, "name")),
374
+ decode(_field(obj, "tpe", loc, "Field"), (*loc, "tpe")),
375
+ )
376
+
377
+ return decode
378
+
379
+
380
+ def _encode_constructors[A](
381
+ encode_attributes: Encoder[A],
382
+ ) -> Encoder[t.Constructors[A]]:
383
+ encode_t = encode_type(encode_attributes)
384
+
385
+ def encode(ctors: t.Constructors[A]) -> Json:
386
+ return _sorted_pairs(
387
+ ctors,
388
+ encode_name,
389
+ lambda args: [[encode_name(n), encode_t(tp)] for n, tp in args],
390
+ )
391
+
392
+ return encode
393
+
394
+
395
+ def _decode_constructors[A](
396
+ decode_attributes: Decoder[A],
397
+ ) -> Decoder[t.Constructors[A]]:
398
+ decode_t = decode_type(decode_attributes)
399
+
400
+ def decode_arg(j: object, loc: Location) -> tuple[Name, t.Type[A]]:
401
+ pair = _expect_list(j, loc, "ConstructorArg")
402
+ return (
403
+ decode_name(_index(pair, 0, loc, "ConstructorArg"), (*loc, 0)),
404
+ decode_t(_index(pair, 1, loc, "ConstructorArg"), (*loc, 1)),
405
+ )
406
+
407
+ def decode_args(j: object, loc: Location) -> t.ConstructorArgs[A]:
408
+ return _list_of(decode_arg, j, loc, "Constructor")
409
+
410
+ def decode(j: object, loc: Location = ()) -> t.Constructors[A]:
411
+ return _pair_list_to_dict(decode_name, decode_args, j, loc, "Constructors")
412
+
413
+ return decode
414
+
415
+
416
+ def encode_type_specification[A](
417
+ encode_attributes: Encoder[A],
418
+ ) -> Encoder[t.Specification[A]]:
419
+ encode_t = encode_type(encode_attributes)
420
+ encode_ctors = _encode_constructors(encode_attributes)
421
+
422
+ def encode(spec: t.Specification[A]) -> Json:
423
+ params = [encode_name(p) for p in spec.type_params]
424
+ match spec:
425
+ case t.TypeAliasSpecification(_, tpe):
426
+ return ["TypeAliasSpecification", params, encode_t(tpe)]
427
+ case t.OpaqueTypeSpecification(_):
428
+ return ["OpaqueTypeSpecification", params]
429
+ case t.CustomTypeSpecification(_, ctors):
430
+ return ["CustomTypeSpecification", params, encode_ctors(ctors)]
431
+ case t.DerivedTypeSpecification(_, base, from_base, to_base):
432
+ return [
433
+ "DerivedTypeSpecification",
434
+ params,
435
+ {
436
+ "baseType": encode_t(base),
437
+ "fromBaseType": encode_fqname(from_base),
438
+ "toBaseType": encode_fqname(to_base),
439
+ },
440
+ ]
441
+
442
+ return encode
443
+
444
+
445
+ def decode_type_specification[A](
446
+ decode_attributes: Decoder[A],
447
+ ) -> Decoder[t.Specification[A]]:
448
+ decode_t = decode_type(decode_attributes)
449
+ decode_ctors = _decode_constructors(decode_attributes)
450
+
451
+ def decode(j: object, loc: Location = ()) -> t.Specification[A]:
452
+ tag, items = _tag(j, loc, "TypeSpecification")
453
+
454
+ def at(i: int) -> tuple[object, Location]:
455
+ return _index(items, i, loc, tag), (*loc, i)
456
+
457
+ match tag:
458
+ case "TypeAliasSpecification":
459
+ return t.TypeAliasSpecification(
460
+ _list_of(decode_name, *at(1), "Name"), decode_t(*at(2))
461
+ )
462
+ case "OpaqueTypeSpecification":
463
+ return t.OpaqueTypeSpecification(_list_of(decode_name, *at(1), "Name"))
464
+ case "CustomTypeSpecification":
465
+ return t.CustomTypeSpecification(
466
+ _list_of(decode_name, *at(1), "Name"), decode_ctors(*at(2))
467
+ )
468
+ case "DerivedTypeSpecification":
469
+ params = _list_of(decode_name, *at(1), "Name")
470
+ config_json, config_loc = at(2)
471
+ config = _expect_object(config_json, config_loc, tag)
472
+
473
+ def member(key: str) -> tuple[object, Location]:
474
+ return _field(config, key, config_loc, tag), (*config_loc, key)
475
+
476
+ return t.DerivedTypeSpecification(
477
+ params,
478
+ decode_t(*member("baseType")),
479
+ decode_fqname(*member("fromBaseType")),
480
+ decode_fqname(*member("toBaseType")),
481
+ )
482
+ case _:
483
+ raise _unknown(tag, loc, "TypeSpecification")
484
+
485
+ return decode
486
+
487
+
488
+ def encode_type_definition[A](
489
+ encode_attributes: Encoder[A],
490
+ ) -> Encoder[t.Definition[A]]:
491
+ encode_t = encode_type(encode_attributes)
492
+ encode_ctors = encode_access_controlled(_encode_constructors(encode_attributes))
493
+
494
+ def encode(defn: t.Definition[A]) -> Json:
495
+ params = [encode_name(p) for p in defn.type_params]
496
+ match defn:
497
+ case t.TypeAliasDefinition(_, tpe):
498
+ return ["TypeAliasDefinition", params, encode_t(tpe)]
499
+ case t.CustomTypeDefinition(_, ctors):
500
+ return ["CustomTypeDefinition", params, encode_ctors(ctors)]
501
+
502
+ return encode
503
+
504
+
505
+ def decode_type_definition[A](
506
+ decode_attributes: Decoder[A],
507
+ ) -> Decoder[t.Definition[A]]:
508
+ decode_t = decode_type(decode_attributes)
509
+ decode_ctors = decode_access_controlled(_decode_constructors(decode_attributes))
510
+
511
+ def decode(j: object, loc: Location = ()) -> t.Definition[A]:
512
+ tag, items = _tag(j, loc, "TypeDefinition")
513
+
514
+ def at(i: int) -> tuple[object, Location]:
515
+ return _index(items, i, loc, tag), (*loc, i)
516
+
517
+ match tag:
518
+ case "TypeAliasDefinition":
519
+ return t.TypeAliasDefinition(
520
+ _list_of(decode_name, *at(1), "Name"), decode_t(*at(2))
521
+ )
522
+ case "CustomTypeDefinition":
523
+ return t.CustomTypeDefinition(
524
+ _list_of(decode_name, *at(1), "Name"), decode_ctors(*at(2))
525
+ )
526
+ case _:
527
+ raise _unknown(tag, loc, "TypeDefinition")
528
+
529
+ return decode
530
+
531
+
532
+ # --- Value ---
533
+
534
+
535
+ def encode_pattern[A](encode_attributes: Encoder[A]) -> Encoder[v.Pattern[A]]:
536
+ def encode(p: v.Pattern[A]) -> Json:
537
+ a = encode_attributes(p.attributes)
538
+ match p:
539
+ case v.WildcardPattern(_):
540
+ return ["WildcardPattern", a]
541
+ case v.AsPattern(_, inner, name):
542
+ return ["AsPattern", a, encode(inner), encode_name(name)]
543
+ case v.TuplePattern(_, elements):
544
+ return ["TuplePattern", a, [encode(x) for x in elements]]
545
+ case v.ConstructorPattern(_, fqn, args):
546
+ return [
547
+ "ConstructorPattern",
548
+ a,
549
+ encode_fqname(fqn),
550
+ [encode(x) for x in args],
551
+ ]
552
+ case v.EmptyListPattern(_):
553
+ return ["EmptyListPattern", a]
554
+ case v.HeadTailPattern(_, head, tail):
555
+ return ["HeadTailPattern", a, encode(head), encode(tail)]
556
+ case v.LiteralPattern(_, literal):
557
+ return ["LiteralPattern", a, encode_literal(literal)]
558
+ case v.UnitPattern(_):
559
+ return ["UnitPattern", a]
560
+
561
+ return encode
562
+
563
+
564
+ def decode_pattern[A](decode_attributes: Decoder[A]) -> Decoder[v.Pattern[A]]:
565
+ def decode(j: object, loc: Location = ()) -> v.Pattern[A]:
566
+ tag, items = _tag(j, loc, "Pattern")
567
+
568
+ def at(i: int) -> tuple[object, Location]:
569
+ return _index(items, i, loc, tag), (*loc, i)
570
+
571
+ attrs = decode_attributes(*at(1))
572
+ match tag:
573
+ case "WildcardPattern":
574
+ return v.WildcardPattern(attrs)
575
+ case "AsPattern":
576
+ return v.AsPattern(attrs, decode(*at(2)), decode_name(*at(3)))
577
+ case "TuplePattern":
578
+ return v.TuplePattern(attrs, _list_of(decode, *at(2), "Pattern"))
579
+ case "ConstructorPattern":
580
+ return v.ConstructorPattern(
581
+ attrs, decode_fqname(*at(2)), _list_of(decode, *at(3), "Pattern")
582
+ )
583
+ case "EmptyListPattern":
584
+ return v.EmptyListPattern(attrs)
585
+ case "HeadTailPattern":
586
+ return v.HeadTailPattern(attrs, decode(*at(2)), decode(*at(3)))
587
+ case "LiteralPattern":
588
+ return v.LiteralPattern(attrs, decode_literal(*at(2)))
589
+ case "UnitPattern":
590
+ return v.UnitPattern(attrs)
591
+ case _:
592
+ raise _unknown(tag, loc, "Pattern")
593
+
594
+ return decode
595
+
596
+
597
+ def encode_value[TA, VA](
598
+ encode_type_attributes: Encoder[TA], encode_value_attributes: Encoder[VA]
599
+ ) -> Encoder[v.Value[TA, VA]]:
600
+ encode_p = encode_pattern(encode_value_attributes)
601
+
602
+ def encode_named(fields: dict[Name, v.Value[TA, VA]]) -> Json:
603
+ return _sorted_pairs(fields, encode_name, encode)
604
+
605
+ def encode(val: v.Value[TA, VA]) -> Json:
606
+ a = encode_value_attributes(val.attributes)
607
+ match val:
608
+ case v.LiteralValue(_, literal):
609
+ return ["Literal", a, encode_literal(literal)]
610
+ case v.Constructor(_, fqn):
611
+ return ["Constructor", a, encode_fqname(fqn)]
612
+ case v.Tuple(_, elements):
613
+ return ["Tuple", a, [encode(x) for x in elements]]
614
+ case v.List(_, items):
615
+ return ["List", a, [encode(x) for x in items]]
616
+ case v.Record(_, fields):
617
+ return ["Record", a, encode_named(fields)]
618
+ case v.Variable(_, name):
619
+ return ["Variable", a, encode_name(name)]
620
+ case v.Reference(_, fqn):
621
+ return ["Reference", a, encode_fqname(fqn)]
622
+ case v.Field(_, subject, name):
623
+ return ["Field", a, encode(subject), encode_name(name)]
624
+ case v.FieldFunction(_, name):
625
+ return ["FieldFunction", a, encode_name(name)]
626
+ case v.Apply(_, function, argument):
627
+ return ["Apply", a, encode(function), encode(argument)]
628
+ case v.Lambda(_, pattern, body):
629
+ return ["Lambda", a, encode_p(pattern), encode(body)]
630
+ case v.LetDefinition(_, name, defn, in_value):
631
+ return [
632
+ "LetDefinition",
633
+ a,
634
+ encode_name(name),
635
+ encode_def(defn),
636
+ encode(in_value),
637
+ ]
638
+ case v.LetRecursion(_, defs, in_value):
639
+ return [
640
+ "LetRecursion",
641
+ a,
642
+ _sorted_pairs(defs, encode_name, encode_def),
643
+ encode(in_value),
644
+ ]
645
+ case v.Destructure(_, pattern, subject, in_value):
646
+ return [
647
+ "Destructure",
648
+ a,
649
+ encode_p(pattern),
650
+ encode(subject),
651
+ encode(in_value),
652
+ ]
653
+ case v.IfThenElse(_, cond, then_branch, else_branch):
654
+ return [
655
+ "IfThenElse",
656
+ a,
657
+ encode(cond),
658
+ encode(then_branch),
659
+ encode(else_branch),
660
+ ]
661
+ case v.PatternMatch(_, subject, cases):
662
+ return [
663
+ "PatternMatch",
664
+ a,
665
+ encode(subject),
666
+ [[encode_p(p), encode(body)] for p, body in cases],
667
+ ]
668
+ case v.UpdateRecord(_, subject, fields):
669
+ return ["UpdateRecord", a, encode(subject), encode_named(fields)]
670
+ case v.Unit(_):
671
+ return ["Unit", a]
672
+
673
+ def encode_def(defn: v.Definition[TA, VA]) -> Json:
674
+ return _encode_value_definition(
675
+ encode_type_attributes, encode_value_attributes, encode, defn
676
+ )
677
+
678
+ return encode
679
+
680
+
681
+ def decode_value[TA, VA](
682
+ decode_type_attributes: Decoder[TA], decode_value_attributes: Decoder[VA]
683
+ ) -> Decoder[v.Value[TA, VA]]:
684
+ decode_p = decode_pattern(decode_value_attributes)
685
+
686
+ def decode_named(j: object, loc: Location) -> dict[Name, v.Value[TA, VA]]:
687
+ return _pair_list_to_dict(decode_name, decode, j, loc, "Value")
688
+
689
+ def decode_def(j: object, loc: Location) -> v.Definition[TA, VA]:
690
+ return _decode_value_definition(
691
+ decode_type_attributes, decode_value_attributes, decode, j, loc
692
+ )
693
+
694
+ def decode(j: object, loc: Location = ()) -> v.Value[TA, VA]:
695
+ tag, items = _tag(j, loc, "Value")
696
+
697
+ def at(i: int) -> tuple[object, Location]:
698
+ return _index(items, i, loc, tag), (*loc, i)
699
+
700
+ attrs = decode_value_attributes(*at(1))
701
+ match tag:
702
+ case "Literal":
703
+ return v.LiteralValue(attrs, decode_literal(*at(2)))
704
+ case "Constructor":
705
+ return v.Constructor(attrs, decode_fqname(*at(2)))
706
+ case "Tuple":
707
+ return v.Tuple(attrs, _list_of(decode, *at(2), "Value"))
708
+ case "List":
709
+ return v.List(attrs, _list_of(decode, *at(2), "Value"))
710
+ case "Record":
711
+ return v.Record(attrs, decode_named(*at(2)))
712
+ case "Variable":
713
+ return v.Variable(attrs, decode_name(*at(2)))
714
+ case "Reference":
715
+ return v.Reference(attrs, decode_fqname(*at(2)))
716
+ case "Field":
717
+ return v.Field(attrs, decode(*at(2)), decode_name(*at(3)))
718
+ case "FieldFunction":
719
+ return v.FieldFunction(attrs, decode_name(*at(2)))
720
+ case "Apply":
721
+ return v.Apply(attrs, decode(*at(2)), decode(*at(3)))
722
+ case "Lambda":
723
+ return v.Lambda(attrs, decode_p(*at(2)), decode(*at(3)))
724
+ case "LetDefinition":
725
+ return v.LetDefinition(
726
+ attrs, decode_name(*at(2)), decode_def(*at(3)), decode(*at(4))
727
+ )
728
+ case "LetRecursion":
729
+ return v.LetRecursion(
730
+ attrs,
731
+ _pair_list_to_dict(decode_name, decode_def, *at(2), "Definition"),
732
+ decode(*at(3)),
733
+ )
734
+ case "Destructure":
735
+ return v.Destructure(
736
+ attrs, decode_p(*at(2)), decode(*at(3)), decode(*at(4))
737
+ )
738
+ case "IfThenElse":
739
+ return v.IfThenElse(
740
+ attrs, decode(*at(2)), decode(*at(3)), decode(*at(4))
741
+ )
742
+ case "PatternMatch":
743
+ cases_json, cases_loc = at(3)
744
+ cases = []
745
+ for i, case in enumerate(_expect_list(cases_json, cases_loc, tag)):
746
+ here = (*cases_loc, i)
747
+ pair = _expect_list(case, here, "Case")
748
+ cases.append(
749
+ (
750
+ decode_p(_index(pair, 0, here, "Case"), (*here, 0)),
751
+ decode(_index(pair, 1, here, "Case"), (*here, 1)),
752
+ )
753
+ )
754
+ return v.PatternMatch(attrs, decode(*at(2)), tuple(cases))
755
+ case "UpdateRecord":
756
+ return v.UpdateRecord(attrs, decode(*at(2)), decode_named(*at(3)))
757
+ case "Unit":
758
+ return v.Unit(attrs)
759
+ case _:
760
+ raise _unknown(tag, loc, "Value")
761
+
762
+ return decode
763
+
764
+
765
+ def encode_value_specification[A](
766
+ encode_attributes: Encoder[A],
767
+ ) -> Encoder[v.Specification[A]]:
768
+ encode_t = encode_type(encode_attributes)
769
+
770
+ def encode(spec: v.Specification[A]) -> Json:
771
+ return {
772
+ "inputs": [[encode_name(n), encode_t(tp)] for n, tp in spec.inputs],
773
+ "output": encode_t(spec.output),
774
+ }
775
+
776
+ return encode
777
+
778
+
779
+ def decode_value_specification[A](
780
+ decode_attributes: Decoder[A],
781
+ ) -> Decoder[v.Specification[A]]:
782
+ decode_t = decode_type(decode_attributes)
783
+
784
+ def decode_input(j: object, loc: Location) -> tuple[Name, t.Type[A]]:
785
+ pair = _expect_list(j, loc, "Input")
786
+ return (
787
+ decode_name(_index(pair, 0, loc, "Input"), (*loc, 0)),
788
+ decode_t(_index(pair, 1, loc, "Input"), (*loc, 1)),
789
+ )
790
+
791
+ def decode(j: object, loc: Location = ()) -> v.Specification[A]:
792
+ obj = _expect_object(j, loc, "ValueSpecification")
793
+ return v.Specification(
794
+ _list_of(
795
+ decode_input,
796
+ _field(obj, "inputs", loc, "ValueSpecification"),
797
+ (*loc, "inputs"),
798
+ "Input",
799
+ ),
800
+ decode_t(
801
+ _field(obj, "output", loc, "ValueSpecification"), (*loc, "output")
802
+ ),
803
+ )
804
+
805
+ return decode
806
+
807
+
808
+ def _encode_value_definition[TA, VA](
809
+ encode_type_attributes: Encoder[TA],
810
+ encode_value_attributes: Encoder[VA],
811
+ encode_body: Encoder[v.Value[TA, VA]],
812
+ defn: v.Definition[TA, VA],
813
+ ) -> Json:
814
+ encode_t = encode_type(encode_type_attributes)
815
+ return {
816
+ "inputTypes": [
817
+ [encode_name(n), encode_value_attributes(a), encode_t(tp)]
818
+ for n, a, tp in defn.input_types
819
+ ],
820
+ "outputType": encode_t(defn.output_type),
821
+ "body": encode_body(defn.body),
822
+ }
823
+
824
+
825
+ def _decode_value_definition[TA, VA](
826
+ decode_type_attributes: Decoder[TA],
827
+ decode_value_attributes: Decoder[VA],
828
+ decode_body: Decoder[v.Value[TA, VA]],
829
+ j: object,
830
+ loc: Location,
831
+ ) -> v.Definition[TA, VA]:
832
+ decode_t = decode_type(decode_type_attributes)
833
+ construct = "ValueDefinition"
834
+ obj = _expect_object(j, loc, construct)
835
+
836
+ def decode_input(j: object, loc: Location) -> tuple[Name, VA, t.Type[TA]]:
837
+ triple = _expect_list(j, loc, "Input")
838
+ return (
839
+ decode_name(_index(triple, 0, loc, "Input"), (*loc, 0)),
840
+ decode_value_attributes(_index(triple, 1, loc, "Input"), (*loc, 1)),
841
+ decode_t(_index(triple, 2, loc, "Input"), (*loc, 2)),
842
+ )
843
+
844
+ return v.Definition(
845
+ _list_of(
846
+ decode_input,
847
+ _field(obj, "inputTypes", loc, construct),
848
+ (*loc, "inputTypes"),
849
+ "Input",
850
+ ),
851
+ decode_t(_field(obj, "outputType", loc, construct), (*loc, "outputType")),
852
+ decode_body(_field(obj, "body", loc, construct), (*loc, "body")),
853
+ )
854
+
855
+
856
+ def encode_value_definition[TA, VA](
857
+ encode_type_attributes: Encoder[TA], encode_value_attributes: Encoder[VA]
858
+ ) -> Encoder[v.Definition[TA, VA]]:
859
+ encode_body = encode_value(encode_type_attributes, encode_value_attributes)
860
+
861
+ def encode(defn: v.Definition[TA, VA]) -> Json:
862
+ return _encode_value_definition(
863
+ encode_type_attributes, encode_value_attributes, encode_body, defn
864
+ )
865
+
866
+ return encode
867
+
868
+
869
+ def decode_value_definition[TA, VA](
870
+ decode_type_attributes: Decoder[TA], decode_value_attributes: Decoder[VA]
871
+ ) -> Decoder[v.Definition[TA, VA]]:
872
+ decode_body = decode_value(decode_type_attributes, decode_value_attributes)
873
+
874
+ def decode(j: object, loc: Location = ()) -> v.Definition[TA, VA]:
875
+ return _decode_value_definition(
876
+ decode_type_attributes, decode_value_attributes, decode_body, j, loc
877
+ )
878
+
879
+ return decode
880
+
881
+
882
+ # --- Module ---
883
+
884
+
885
+ def _decode_doc(obj: dict[str, object]) -> str | None:
886
+ doc = obj.get("doc")
887
+ return doc if isinstance(doc, str) else None
888
+
889
+
890
+ def encode_module_specification[TA](
891
+ encode_type_attributes: Encoder[TA],
892
+ ) -> Encoder[module.Specification[TA]]:
893
+ encode_ts = encode_documented(encode_type_specification(encode_type_attributes))
894
+ encode_vs = encode_documented(encode_value_specification(encode_type_attributes))
895
+
896
+ def encode(spec: module.Specification[TA]) -> Json:
897
+ return {
898
+ "types": _sorted_pairs(spec.types, encode_name, encode_ts),
899
+ "values": _sorted_pairs(spec.values, encode_name, encode_vs),
900
+ "doc": spec.doc,
901
+ }
902
+
903
+ return encode
904
+
905
+
906
+ def decode_module_specification[TA](
907
+ decode_type_attributes: Decoder[TA],
908
+ ) -> Decoder[module.Specification[TA]]:
909
+ decode_ts = decode_documented(decode_type_specification(decode_type_attributes))
910
+ decode_vs = decode_documented(decode_value_specification(decode_type_attributes))
911
+ construct = "ModuleSpecification"
912
+
913
+ def decode(j: object, loc: Location = ()) -> module.Specification[TA]:
914
+ obj = _expect_object(j, loc, construct)
915
+ return module.Specification(
916
+ _pair_list_to_dict(
917
+ decode_name,
918
+ decode_ts,
919
+ _field(obj, "types", loc, construct),
920
+ (*loc, "types"),
921
+ "Types",
922
+ ),
923
+ _pair_list_to_dict(
924
+ decode_name,
925
+ decode_vs,
926
+ _field(obj, "values", loc, construct),
927
+ (*loc, "values"),
928
+ "Values",
929
+ ),
930
+ _decode_doc(obj),
931
+ )
932
+
933
+ return decode
934
+
935
+
936
+ def encode_module_definition[TA, VA](
937
+ encode_type_attributes: Encoder[TA], encode_value_attributes: Encoder[VA]
938
+ ) -> Encoder[module.Definition[TA, VA]]:
939
+ encode_td = encode_access_controlled(
940
+ encode_documented(encode_type_definition(encode_type_attributes))
941
+ )
942
+ encode_vd = encode_access_controlled(
943
+ encode_documented(
944
+ encode_value_definition(encode_type_attributes, encode_value_attributes)
945
+ )
946
+ )
947
+
948
+ def encode(defn: module.Definition[TA, VA]) -> Json:
949
+ return {
950
+ "types": _sorted_pairs(defn.types, encode_name, encode_td),
951
+ "values": _sorted_pairs(defn.values, encode_name, encode_vd),
952
+ "doc": defn.doc,
953
+ }
954
+
955
+ return encode
956
+
957
+
958
+ def decode_module_definition[TA, VA](
959
+ decode_type_attributes: Decoder[TA], decode_value_attributes: Decoder[VA]
960
+ ) -> Decoder[module.Definition[TA, VA]]:
961
+ decode_td = decode_access_controlled(
962
+ decode_documented(decode_type_definition(decode_type_attributes))
963
+ )
964
+ decode_vd = decode_access_controlled(
965
+ decode_documented(
966
+ decode_value_definition(decode_type_attributes, decode_value_attributes)
967
+ )
968
+ )
969
+ construct = "ModuleDefinition"
970
+
971
+ def decode(j: object, loc: Location = ()) -> module.Definition[TA, VA]:
972
+ obj = _expect_object(j, loc, construct)
973
+ return module.Definition(
974
+ _pair_list_to_dict(
975
+ decode_name,
976
+ decode_td,
977
+ _field(obj, "types", loc, construct),
978
+ (*loc, "types"),
979
+ "Types",
980
+ ),
981
+ _pair_list_to_dict(
982
+ decode_name,
983
+ decode_vd,
984
+ _field(obj, "values", loc, construct),
985
+ (*loc, "values"),
986
+ "Values",
987
+ ),
988
+ _decode_doc(obj),
989
+ )
990
+
991
+ return decode
992
+
993
+
994
+ # --- Package ---
995
+
996
+
997
+ def encode_package_specification[TA](
998
+ encode_type_attributes: Encoder[TA],
999
+ ) -> Encoder[package.Specification[TA]]:
1000
+ encode_m = encode_module_specification(encode_type_attributes)
1001
+
1002
+ def encode(spec: package.Specification[TA]) -> Json:
1003
+ return {"modules": _sorted_pairs(spec.modules, encode_path, encode_m)}
1004
+
1005
+ return encode
1006
+
1007
+
1008
+ def decode_package_specification[TA](
1009
+ decode_type_attributes: Decoder[TA],
1010
+ ) -> Decoder[package.Specification[TA]]:
1011
+ decode_m = decode_module_specification(decode_type_attributes)
1012
+ construct = "PackageSpecification"
1013
+
1014
+ def decode(j: object, loc: Location = ()) -> package.Specification[TA]:
1015
+ obj = _expect_object(j, loc, construct)
1016
+ return package.Specification(
1017
+ _pair_list_to_dict(
1018
+ decode_path,
1019
+ decode_m,
1020
+ _field(obj, "modules", loc, construct),
1021
+ (*loc, "modules"),
1022
+ "Modules",
1023
+ )
1024
+ )
1025
+
1026
+ return decode
1027
+
1028
+
1029
+ def encode_package_definition[TA, VA](
1030
+ encode_type_attributes: Encoder[TA], encode_value_attributes: Encoder[VA]
1031
+ ) -> Encoder[package.Definition[TA, VA]]:
1032
+ encode_m = encode_access_controlled(
1033
+ encode_module_definition(encode_type_attributes, encode_value_attributes)
1034
+ )
1035
+
1036
+ def encode(defn: package.Definition[TA, VA]) -> Json:
1037
+ return {"modules": _sorted_pairs(defn.modules, encode_path, encode_m)}
1038
+
1039
+ return encode
1040
+
1041
+
1042
+ def decode_package_definition[TA, VA](
1043
+ decode_type_attributes: Decoder[TA], decode_value_attributes: Decoder[VA]
1044
+ ) -> Decoder[package.Definition[TA, VA]]:
1045
+ decode_m = decode_access_controlled(
1046
+ decode_module_definition(decode_type_attributes, decode_value_attributes)
1047
+ )
1048
+ construct = "PackageDefinition"
1049
+
1050
+ def decode(j: object, loc: Location = ()) -> package.Definition[TA, VA]:
1051
+ obj = _expect_object(j, loc, construct)
1052
+ return package.Definition(
1053
+ _pair_list_to_dict(
1054
+ decode_path,
1055
+ decode_m,
1056
+ _field(obj, "modules", loc, construct),
1057
+ (*loc, "modules"),
1058
+ "Modules",
1059
+ )
1060
+ )
1061
+
1062
+ return decode
1063
+
1064
+
1065
+ # --- Distribution ---
1066
+
1067
+ _encode_typed_type = encode_type(encode_unit)
1068
+ _decode_typed_type = decode_type(decode_unit)
1069
+ _encode_dependency = encode_package_specification(encode_unit)
1070
+ _decode_dependency = decode_package_specification(decode_unit)
1071
+ _encode_library_def = encode_package_definition(encode_unit, _encode_typed_type)
1072
+ _decode_library_def = decode_package_definition(decode_unit, _decode_typed_type)
1073
+
1074
+
1075
+ _encode_specs_def = encode_package_specification(encode_unit)
1076
+ _SPECS_MODULE_MEMBERS = frozenset({"types", "values", "doc"})
1077
+
1078
+
1079
+ def _decode_specs_module(j: object, loc: Location = ()) -> module.Specification[None]:
1080
+ """Decode one of a Specs distribution's own modules, refusing other members.
1081
+
1082
+ The elm-faithful module-specification decoder ignores unknown members, so a
1083
+ module definition would read as a broken specification. As in morphir-rust
1084
+ ``SpecsModuleEntry``, only ``types``, ``values`` and ``doc`` are accepted here.
1085
+ """
1086
+ obj = _expect_object(j, loc, "ModuleSpecification")
1087
+ for key in obj:
1088
+ if key not in _SPECS_MODULE_MEMBERS:
1089
+ raise fail(
1090
+ d.UNKNOWN_MEMBER,
1091
+ "ModuleSpecification",
1092
+ (*loc, key),
1093
+ "a v3 Specs distribution holds module specifications, not "
1094
+ f"definitions: unexpected member {key!r}",
1095
+ )
1096
+ return _decode_dependency_module(obj, loc)
1097
+
1098
+
1099
+ _decode_dependency_module = decode_module_specification(decode_unit)
1100
+
1101
+
1102
+ def _decode_specs_def(j: object, loc: Location = ()) -> package.Specification[None]:
1103
+ obj = _expect_object(j, loc, "PackageSpecification")
1104
+ return package.Specification(
1105
+ _pair_list_to_dict(
1106
+ decode_path,
1107
+ _decode_specs_module,
1108
+ _field(obj, "modules", loc, "PackageSpecification"),
1109
+ (*loc, "modules"),
1110
+ "Modules",
1111
+ )
1112
+ )
1113
+
1114
+
1115
+ def encode_distribution(dist: Distribution) -> Json:
1116
+ """Encode a distribution as ``[kind, name, deps, def or spec]``.
1117
+
1118
+ A `Library` is elm's ``["Library", name, deps, def]``; a `Specs` is
1119
+ ``["Specs", name, deps, spec]``.
1120
+ """
1121
+ match dist:
1122
+ case Library():
1123
+ return [
1124
+ "Library",
1125
+ encode_path(dist.package_name),
1126
+ _sorted_pairs(dist.dependencies, encode_path, _encode_dependency),
1127
+ _encode_library_def(dist.definition),
1128
+ ]
1129
+ case Specs():
1130
+ return [
1131
+ "Specs",
1132
+ encode_path(dist.package_name),
1133
+ _sorted_pairs(dist.dependencies, encode_path, _encode_dependency),
1134
+ _encode_specs_def(dist.specification),
1135
+ ]
1136
+
1137
+
1138
+ def decode_distribution(j: object, loc: Location = ()) -> Distribution:
1139
+ """Decode a ``Library`` or, from format 3.1.0, a ``Specs`` distribution.
1140
+
1141
+ This decoder does not see ``formatVersion``; `decode_versioned_distribution`
1142
+ refuses a ``Specs`` declared below 3.1.0.
1143
+ """
1144
+ tag, items = _tag(j, loc, "Distribution")
1145
+ if tag not in ("Library", "Specs"):
1146
+ raise fail(
1147
+ d.UNKNOWN_NODE,
1148
+ "Distribution",
1149
+ (*loc, 0),
1150
+ f"unknown distribution kind {tag!r}; format 3 has Library and, "
1151
+ "from 3.1.0, Specs",
1152
+ )
1153
+
1154
+ def at(i: int) -> tuple[object, Location]:
1155
+ return _index(items, i, loc, tag), (*loc, i)
1156
+
1157
+ package_name = decode_path(*at(1))
1158
+ dependencies = _pair_list_to_dict(
1159
+ decode_path, _decode_dependency, *at(2), "Dependencies"
1160
+ )
1161
+ if tag == "Specs":
1162
+ return Specs(
1163
+ package_name=package_name,
1164
+ specification=_decode_specs_def(*at(3)),
1165
+ dependencies=dependencies,
1166
+ )
1167
+ return Library(
1168
+ package_name=package_name,
1169
+ dependencies=dependencies,
1170
+ definition=_decode_library_def(*at(3)),
1171
+ )
1172
+
1173
+
1174
+ # --- FormatVersion ---
1175
+
1176
+
1177
+ def format_version_of(dist: Distribution) -> FormatVersion:
1178
+ """Return the lowest release that expresses ``dist``.
1179
+
1180
+ A `Library` needs only 3.0.0; a `Specs` needs 3.1.0, the release that added
1181
+ it (finos/morphir decision record 0018).
1182
+ """
1183
+ return V3_1 if isinstance(dist, Specs) else V3
1184
+
1185
+
1186
+ def encode_versioned_distribution(dist: Distribution) -> dict[str, Json]:
1187
+ """Encode ``{"formatVersion": ..., "distribution": ...}``, version first.
1188
+
1189
+ The version is the lowest one that fits the content: ``3`` for a `Library`,
1190
+ ``"3.1.0"`` for a `Specs`, whatever version the input declared.
1191
+ """
1192
+ return {
1193
+ "formatVersion": write_format_version(format_version_of(dist)),
1194
+ "distribution": encode_distribution(dist),
1195
+ }
1196
+
1197
+
1198
+ def decode_versioned_distribution(document: str | object) -> Distribution:
1199
+ """Decode a format-3 ``morphir-ir.json`` document.
1200
+
1201
+ The format version is read with the shared permissive reader and must be in
1202
+ ``[3.0.0,3.2.0)``; ``4`` fails with ``unsupported_format_version_major``. A
1203
+ ``Specs`` distribution declared below 3.1.0 fails with ``specs_before_3_1``
1204
+ before its body is decoded.
1205
+
1206
+ Args:
1207
+ document: JSON text or an already-decoded JSON value.
1208
+
1209
+ Raises:
1210
+ DiagnosticError: On a format-version or decoding failure.
1211
+ """
1212
+ version, root = read_document(document, V3_TABLE)
1213
+ obj = _expect_object(root, (), "document")
1214
+ distribution = _field(obj, "distribution", (), "document")
1215
+ loc: Location = ("distribution",)
1216
+ tag, _ = _tag(distribution, loc, "Distribution")
1217
+ if tag == "Specs" and version < V3_1:
1218
+ raise fail(
1219
+ d.SPECS_BEFORE_3_1,
1220
+ "Specs",
1221
+ ("formatVersion",),
1222
+ f"a v3 Specs distribution needs formatVersion {V3_1} or later, "
1223
+ f"found {obj['formatVersion']!r}",
1224
+ )
1225
+ return decode_distribution(distribution, loc)
1226
+
1227
+
1228
+ __all__ = [
1229
+ "Decoder",
1230
+ "Encoder",
1231
+ "Json",
1232
+ "decode_access_controlled",
1233
+ "decode_distribution",
1234
+ "decode_documented",
1235
+ "decode_fqname",
1236
+ "decode_literal",
1237
+ "decode_module_definition",
1238
+ "decode_module_specification",
1239
+ "decode_name",
1240
+ "decode_package_definition",
1241
+ "decode_package_specification",
1242
+ "decode_path",
1243
+ "decode_pattern",
1244
+ "decode_qname",
1245
+ "decode_type",
1246
+ "decode_type_definition",
1247
+ "decode_type_specification",
1248
+ "decode_unit",
1249
+ "decode_value",
1250
+ "decode_value_definition",
1251
+ "decode_value_specification",
1252
+ "decode_versioned_distribution",
1253
+ "encode_access_controlled",
1254
+ "encode_distribution",
1255
+ "encode_documented",
1256
+ "encode_fqname",
1257
+ "encode_literal",
1258
+ "encode_module_definition",
1259
+ "encode_module_specification",
1260
+ "encode_name",
1261
+ "encode_package_definition",
1262
+ "encode_package_specification",
1263
+ "encode_path",
1264
+ "encode_pattern",
1265
+ "encode_qname",
1266
+ "encode_type",
1267
+ "encode_type_definition",
1268
+ "encode_type_specification",
1269
+ "encode_unit",
1270
+ "encode_value",
1271
+ "encode_value_definition",
1272
+ "encode_value_specification",
1273
+ "encode_versioned_distribution",
1274
+ "format_version_of",
1275
+ ]