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.
- morphir/__init__.py +14 -0
- morphir/ir/__init__.py +110 -0
- morphir/ir/access_controlled.py +15 -0
- morphir/ir/decorations.py +19 -0
- morphir/ir/diagnostics.py +105 -0
- morphir/ir/distribution.py +21 -0
- morphir/ir/document.py +37 -0
- morphir/ir/documented.py +11 -0
- morphir/ir/format_version.py +483 -0
- morphir/ir/fqname.py +9 -0
- morphir/ir/json.py +11 -0
- morphir/ir/literal.py +25 -0
- morphir/ir/meta.py +11 -0
- morphir/ir/migrate.py +1267 -0
- morphir/ir/module.py +17 -0
- morphir/ir/name.py +27 -0
- morphir/ir/package.py +15 -0
- morphir/ir/path.py +25 -0
- morphir/ir/qname.py +9 -0
- morphir/ir/ref.py +17 -0
- morphir/ir/type.py +37 -0
- morphir/ir/type_constraints.py +33 -0
- morphir/ir/type_def.py +19 -0
- morphir/ir/type_spec.py +25 -0
- morphir/ir/v3/__init__.py +64 -0
- morphir/ir/v3/_legacy_value.py +201 -0
- morphir/ir/v3/access_controlled.py +31 -0
- morphir/ir/v3/codec.py +1275 -0
- morphir/ir/v3/codec_v1.py +491 -0
- morphir/ir/v3/codec_v2.py +187 -0
- morphir/ir/v3/distribution.py +42 -0
- morphir/ir/v3/documented.py +13 -0
- morphir/ir/v3/fqname.py +30 -0
- morphir/ir/v3/literal.py +46 -0
- morphir/ir/v3/module.py +32 -0
- morphir/ir/v3/name.py +42 -0
- morphir/ir/v3/package.py +23 -0
- morphir/ir/v3/path.py +29 -0
- morphir/ir/v3/qname.py +16 -0
- morphir/ir/v3/type.py +126 -0
- morphir/ir/v3/value.py +235 -0
- morphir/ir/v4/__init__.py +160 -0
- morphir/ir/v4/access_controlled.py +17 -0
- morphir/ir/v4/annotation.py +75 -0
- morphir/ir/v4/codec.py +2161 -0
- morphir/ir/v4/decorations.py +88 -0
- morphir/ir/v4/distribution.py +56 -0
- morphir/ir/v4/document.py +69 -0
- morphir/ir/v4/documented.py +10 -0
- morphir/ir/v4/fqname.py +69 -0
- morphir/ir/v4/incompleteness.py +78 -0
- morphir/ir/v4/json.py +103 -0
- morphir/ir/v4/literal.py +51 -0
- morphir/ir/v4/meta.py +27 -0
- morphir/ir/v4/module.py +30 -0
- morphir/ir/v4/name.py +69 -0
- morphir/ir/v4/package.py +19 -0
- morphir/ir/v4/path.py +50 -0
- morphir/ir/v4/qname.py +34 -0
- morphir/ir/v4/ref.py +35 -0
- morphir/ir/v4/type.py +140 -0
- morphir/ir/v4/type_constraints.py +91 -0
- morphir/ir/v4/type_def.py +38 -0
- morphir/ir/v4/type_spec.py +64 -0
- morphir/ir/v4/value.py +480 -0
- morphir/ir/value.py +81 -0
- morphir/py.typed +0 -0
- morphir/sdk/__init__.py +110 -0
- morphir/sdk/_compare.py +187 -0
- morphir/sdk/aggregate.py +421 -0
- morphir/sdk/basics.py +504 -0
- morphir/sdk/char.py +110 -0
- morphir/sdk/decimal.py +311 -0
- morphir/sdk/dict.py +336 -0
- morphir/sdk/instant.py +118 -0
- morphir/sdk/int.py +86 -0
- morphir/sdk/key.py +522 -0
- morphir/sdk/list.py +356 -0
- morphir/sdk/local_date.py +436 -0
- morphir/sdk/local_time.py +267 -0
- morphir/sdk/maybe.py +135 -0
- morphir/sdk/number.py +237 -0
- morphir/sdk/regex.py +287 -0
- morphir/sdk/result.py +162 -0
- morphir/sdk/result_list.py +169 -0
- morphir/sdk/rule.py +103 -0
- morphir/sdk/set.py +214 -0
- morphir/sdk/stateful_app.py +49 -0
- morphir/sdk/string.py +427 -0
- morphir/sdk/tuple.py +43 -0
- morphir/sdk/uuid.py +209 -0
- morphir_python-0.0.1.dist-info/METADATA +269 -0
- morphir_python-0.0.1.dist-info/RECORD +99 -0
- morphir_python-0.0.1.dist-info/WHEEL +4 -0
- morphir_python-0.0.1.dist-info/entry_points.txt +2 -0
- morphir_python-0.0.1.dist-info/licenses/LICENSE +201 -0
- morphir_tools/__init__.py +13 -0
- morphir_tools/cli.py +223 -0
- morphir_tools/py.typed +0 -0
morphir/ir/v4/codec.py
ADDED
|
@@ -0,0 +1,2161 @@
|
|
|
1
|
+
"""Draft JSON codec for Morphir IR v4.
|
|
2
|
+
|
|
3
|
+
**Draft.** IR v4 is not frozen upstream. This codec follows the finos/morphir v4
|
|
4
|
+
JSON profile and schema (``website/static/schemas/morphir-ir-v4.json``) at the
|
|
5
|
+
pinned commit `FINOS_MORPHIR_PIN`. Spellings may change in a later minor release
|
|
6
|
+
of this package when the codec is re-synced against the Morphir Compatibility Kit.
|
|
7
|
+
|
|
8
|
+
The envelope is ``{"formatVersion": 4, "distribution": {"Library": {...}}}``, with
|
|
9
|
+
the distribution wrapped by its kind (``Library``, ``Specs``, or ``Application``).
|
|
10
|
+
The codec never writes a format-3 tagged-array shape.
|
|
11
|
+
|
|
12
|
+
Every node is written in the schema's expanded form, ``{"Kind": {members}}``, with
|
|
13
|
+
``attributes`` omitted when empty. The reader accepts exactly the spellings listed
|
|
14
|
+
in `TYPE_NODES`, `VALUE_NODES`, `PATTERN_NODES`, and the other tables below, and
|
|
15
|
+
fails with a located diagnostic on anything else; it does not guess.
|
|
16
|
+
|
|
17
|
+
The codec covers the value-definition bodies (``ExpressionBody``, ``NativeBody``,
|
|
18
|
+
``ExternalBody``, ``IncompleteBody``), ``IncompleteTypeDefinition`` with typed hole
|
|
19
|
+
reasons, annotations on specifications, and the draft ``Union`` type (from
|
|
20
|
+
``docs/spec/draft/types.md``, not yet in the pinned schema).
|
|
21
|
+
|
|
22
|
+
Known gaps between the Python v4 model and upstream v4, each handled explicitly:
|
|
23
|
+
|
|
24
|
+
- The deprecated Python ``Native`` and ``External`` value nodes have no spelling
|
|
25
|
+
(upstream makes them definition bodies); encoding fails with ``no_v4_spelling``.
|
|
26
|
+
- Structured ``TypeConstraints`` (deprecated) have no spelling; encoding fails
|
|
27
|
+
with ``no_v4_spelling``.
|
|
28
|
+
- Only the expanded spelling is read and written. Compact spellings, the legacy
|
|
29
|
+
spellings readers accept for one release, and the 4.1.0 linked annotations
|
|
30
|
+
wait for the re-sync when upstream v4 settles.
|
|
31
|
+
- Records are objects keyed by field name (finos decision 0004); duplicate field
|
|
32
|
+
names fail encoding with ``duplicate_member``.
|
|
33
|
+
- ``ExpressionBody.inputTypes`` has no attribute slot: upstream spells an input as
|
|
34
|
+
a bare type (kit case ``definitions-0027``). Encoding accepts per-input
|
|
35
|
+
attributes that are empty or whose only member is ``inferred_type`` equal to the
|
|
36
|
+
declared input type, and fails with ``no_v4_spelling`` otherwise; decoding sets
|
|
37
|
+
``inferred_type`` to the declared type. `morphir.ir.migrate.drop_input_attributes`
|
|
38
|
+
resets the attributes on request and reports each input that lost information.
|
|
39
|
+
- The package release version has no member (finos decision 0017); it is not
|
|
40
|
+
written and decodes as absent.
|
|
41
|
+
- Python names are word lists without an initialism marker. A run of two or more
|
|
42
|
+
single-letter words is written as an uppercase initialism (``value-in-USD``).
|
|
43
|
+
Names the word-list model cannot represent, such as a one-letter initialism,
|
|
44
|
+
an initialism with digits, adjacent initialisms, or adjacent single-letter
|
|
45
|
+
words, fail with ``invalid_name``.
|
|
46
|
+
- An entry point's ``doc`` is written from ``EntryPoint.doc.value`` and decodes as
|
|
47
|
+
``Documented(None, doc)``.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from __future__ import annotations
|
|
51
|
+
|
|
52
|
+
import re
|
|
53
|
+
from collections.abc import Callable
|
|
54
|
+
from decimal import Decimal, InvalidOperation
|
|
55
|
+
from typing import Any
|
|
56
|
+
|
|
57
|
+
from .. import diagnostics as d
|
|
58
|
+
from ..diagnostics import DiagnosticError, Location, fail
|
|
59
|
+
from ..format_version import V4, V4_TABLE, read_document, write_format_version
|
|
60
|
+
from . import annotation as ann
|
|
61
|
+
from . import document as doc
|
|
62
|
+
from . import incompleteness as inc
|
|
63
|
+
from . import literal as lit
|
|
64
|
+
from . import module, package
|
|
65
|
+
from . import type as t
|
|
66
|
+
from . import type_def as td
|
|
67
|
+
from . import type_spec as ts
|
|
68
|
+
from . import value as v
|
|
69
|
+
from .access_controlled import AccessControlled, Private, Public
|
|
70
|
+
from .distribution import (
|
|
71
|
+
ApplicationDistribution,
|
|
72
|
+
Distribution,
|
|
73
|
+
EntryPoint,
|
|
74
|
+
EntryPointKind,
|
|
75
|
+
LibraryDistribution,
|
|
76
|
+
PackageInfo,
|
|
77
|
+
SpecsDistribution,
|
|
78
|
+
)
|
|
79
|
+
from .documented import Documented
|
|
80
|
+
from .fqname import FQName
|
|
81
|
+
from .name import Name
|
|
82
|
+
from .path import Path
|
|
83
|
+
|
|
84
|
+
FINOS_MORPHIR_PIN = "efa36060"
|
|
85
|
+
"""The finos/morphir commit whose v4 JSON profile this draft codec follows."""
|
|
86
|
+
|
|
87
|
+
DRAFT_NOTICE = (
|
|
88
|
+
"IR v4 support is draft: it follows the finos/morphir v4 JSON profile at "
|
|
89
|
+
f"commit {FINOS_MORPHIR_PIN} and may change in a later minor release."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
type Json = Any
|
|
93
|
+
|
|
94
|
+
# --- Spelling tables: node kind -> (required members, optional members) ---
|
|
95
|
+
|
|
96
|
+
type Members = tuple[frozenset[str], frozenset[str]]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _m(required: str = "", optional: str = "attributes") -> Members:
|
|
100
|
+
return frozenset(required.split()), frozenset(optional.split())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
TYPE_NODES: dict[str, Members] = {
|
|
104
|
+
"Variable": _m("name"),
|
|
105
|
+
"Reference": _m("fqname", "attributes args"),
|
|
106
|
+
"Tuple": _m("elements"),
|
|
107
|
+
"Record": _m("fields"),
|
|
108
|
+
"ExtensibleRecord": _m("variable fields"),
|
|
109
|
+
"Function": _m("parameterType returnType"),
|
|
110
|
+
"Unit": _m(),
|
|
111
|
+
"Union": _m("alternatives"),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
VALUE_NODES: dict[str, Members] = {
|
|
115
|
+
"Literal": _m("literal"),
|
|
116
|
+
"Constructor": _m("fqname"),
|
|
117
|
+
"Tuple": _m("elements"),
|
|
118
|
+
"List": _m("items"),
|
|
119
|
+
"Record": _m("fields"),
|
|
120
|
+
"Variable": _m("name"),
|
|
121
|
+
"Reference": _m("fqname"),
|
|
122
|
+
"Field": _m("target name"),
|
|
123
|
+
"FieldFunction": _m("name"),
|
|
124
|
+
"Apply": _m("function argument"),
|
|
125
|
+
"Lambda": _m("pattern body"),
|
|
126
|
+
"LetDefinition": _m("name definition in"),
|
|
127
|
+
"LetRecursion": _m("definitions in"),
|
|
128
|
+
"Destructure": _m("pattern value in"),
|
|
129
|
+
"IfThenElse": _m("condition then else"),
|
|
130
|
+
"PatternMatch": _m("value cases"),
|
|
131
|
+
"UpdateRecord": _m("target fields"),
|
|
132
|
+
"Unit": _m(),
|
|
133
|
+
"Hole": _m("reason", "attributes expectedType"),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
PATTERN_NODES: dict[str, Members] = {
|
|
137
|
+
"WildcardPattern": _m(),
|
|
138
|
+
"AsPattern": _m("pattern name"),
|
|
139
|
+
"TuplePattern": _m("patterns"),
|
|
140
|
+
"ConstructorPattern": _m("fqname patterns"),
|
|
141
|
+
"EmptyListPattern": _m(),
|
|
142
|
+
"HeadTailPattern": _m("head tail"),
|
|
143
|
+
"LiteralPattern": _m("literal"),
|
|
144
|
+
"UnitPattern": _m(),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
TYPE_SPECIFICATION_NODES: dict[str, Members] = {
|
|
148
|
+
"TypeAliasSpecification": _m("typeParams typeExp", "annotations"),
|
|
149
|
+
"OpaqueTypeSpecification": _m("", "annotations typeParams"),
|
|
150
|
+
"CustomTypeSpecification": _m("typeParams constructors", "annotations"),
|
|
151
|
+
"DerivedTypeSpecification": _m(
|
|
152
|
+
"typeParams baseType fromBaseType toBaseType", "annotations"
|
|
153
|
+
),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
TYPE_DEFINITION_NODES: dict[str, Members] = {
|
|
157
|
+
"TypeAliasDefinition": _m("typeParams typeExp", ""),
|
|
158
|
+
"CustomTypeDefinition": _m("typeParams constructors", "access"),
|
|
159
|
+
"IncompleteTypeDefinition": _m("typeParams incompleteness", "partialTypeExp"),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
HOLE_REASON_NODES: dict[str, Members] = {
|
|
163
|
+
"UnresolvedReference": _m("target", ""),
|
|
164
|
+
"DeletedDuringRefactor": _m("tx-id", ""),
|
|
165
|
+
"TypeMismatch": _m("expected found", ""),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
INCOMPLETENESS_NODES: dict[str, Members] = {
|
|
169
|
+
"Hole": _m("reason", "partialBody"),
|
|
170
|
+
"Draft": _m("", ""),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
VALUE_DEFINITION_NODES: dict[str, Members] = {
|
|
174
|
+
"ExpressionBody": _m("inputTypes outputType body", ""),
|
|
175
|
+
"NativeBody": _m("inputTypes outputType nativeInfo", ""),
|
|
176
|
+
"ExternalBody": _m("inputTypes outputType externals", "body"),
|
|
177
|
+
"IncompleteBody": _m("inputTypes incompleteness", "outputType partialBody"),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
NATIVE_HINT_NODES: dict[str, Members] = {
|
|
181
|
+
"Arithmetic": _m("", ""),
|
|
182
|
+
"Comparison": _m("", ""),
|
|
183
|
+
"StringOp": _m("", ""),
|
|
184
|
+
"CollectionOp": _m("", ""),
|
|
185
|
+
"PlatformSpecific": _m("platform", ""),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
DISTRIBUTION_NODES: dict[str, Members] = {
|
|
189
|
+
"Library": _m("packageName", "dependencies def"),
|
|
190
|
+
"Specs": _m("packageName", "dependencies spec"),
|
|
191
|
+
"Application": _m("packageName entryPoints", "dependencies def"),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
LITERAL_KINDS = frozenset(
|
|
195
|
+
{
|
|
196
|
+
"BoolLiteral",
|
|
197
|
+
"CharLiteral",
|
|
198
|
+
"StringLiteral",
|
|
199
|
+
"IntegerLiteral",
|
|
200
|
+
"WholeNumberLiteral",
|
|
201
|
+
"FloatLiteral",
|
|
202
|
+
"DecimalLiteral",
|
|
203
|
+
"DocumentLiteral",
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
ACCESS_TAGS = {"Public": Public, "Private": Private}
|
|
208
|
+
|
|
209
|
+
ENTRY_POINT_KINDS = {kind: kind.value.lower() for kind in EntryPointKind}
|
|
210
|
+
|
|
211
|
+
# --- Helpers ---
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _construct_error(
|
|
215
|
+
code: str, construct: str, loc: Location, msg: str
|
|
216
|
+
) -> DiagnosticError:
|
|
217
|
+
return fail(code, construct, loc, msg)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _expect_object(j: object, loc: Location, construct: str) -> dict[str, object]:
|
|
221
|
+
if not isinstance(j, dict):
|
|
222
|
+
raise fail(
|
|
223
|
+
d.INVALID_JSON, construct, loc, f"expected an object for {construct}"
|
|
224
|
+
)
|
|
225
|
+
return j
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _expect_list(j: object, loc: Location, construct: str) -> list[object]:
|
|
229
|
+
if not isinstance(j, list):
|
|
230
|
+
raise fail(d.INVALID_JSON, construct, loc, f"expected an array for {construct}")
|
|
231
|
+
return j
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _expect_str(j: object, loc: Location, construct: str) -> str:
|
|
235
|
+
if not isinstance(j, str):
|
|
236
|
+
raise fail(d.INVALID_JSON, construct, loc, f"expected a string for {construct}")
|
|
237
|
+
return j
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _check_members(
|
|
241
|
+
obj: dict[str, object], members: Members, loc: Location, construct: str
|
|
242
|
+
) -> None:
|
|
243
|
+
required, optional = members
|
|
244
|
+
for key in obj:
|
|
245
|
+
if key not in required and key not in optional:
|
|
246
|
+
raise fail(
|
|
247
|
+
d.UNKNOWN_MEMBER,
|
|
248
|
+
construct,
|
|
249
|
+
(*loc, key),
|
|
250
|
+
f"{construct} has no member {key!r} in the draft v4 codec",
|
|
251
|
+
)
|
|
252
|
+
for key in sorted(required):
|
|
253
|
+
if key not in obj:
|
|
254
|
+
raise fail(
|
|
255
|
+
d.MISSING_MEMBER,
|
|
256
|
+
construct,
|
|
257
|
+
loc,
|
|
258
|
+
f"{construct} requires member {key!r}",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _node(
|
|
263
|
+
j: object, loc: Location, family: str, table: dict[str, Members]
|
|
264
|
+
) -> tuple[str, dict[str, object], Location]:
|
|
265
|
+
obj = _expect_object(j, loc, family)
|
|
266
|
+
if len(obj) != 1:
|
|
267
|
+
raise fail(
|
|
268
|
+
d.UNKNOWN_NODE,
|
|
269
|
+
family,
|
|
270
|
+
loc,
|
|
271
|
+
f"expected a {family} wrapped by its kind, found members {sorted(obj)}",
|
|
272
|
+
)
|
|
273
|
+
((tag, payload),) = obj.items()
|
|
274
|
+
if tag not in table:
|
|
275
|
+
raise fail(
|
|
276
|
+
d.UNKNOWN_NODE,
|
|
277
|
+
family,
|
|
278
|
+
(*loc, tag),
|
|
279
|
+
f"unknown {family} kind {tag!r} in the draft v4 codec",
|
|
280
|
+
)
|
|
281
|
+
here = (*loc, tag)
|
|
282
|
+
body = _expect_object(payload, here, tag)
|
|
283
|
+
_check_members(body, table[tag], here, tag)
|
|
284
|
+
return tag, body, here
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _wrap(tag: str, members: dict[str, Json]) -> Json:
|
|
288
|
+
return {tag: members}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _keyed[K, A](
|
|
292
|
+
items: list[tuple[K, A]] | dict[K, A],
|
|
293
|
+
encode_key: Callable[[K, Location], str],
|
|
294
|
+
encode_value: Callable[[A, Location], Json],
|
|
295
|
+
loc: Location,
|
|
296
|
+
construct: str,
|
|
297
|
+
) -> dict[str, Json]:
|
|
298
|
+
pairs = list(items.items()) if isinstance(items, dict) else items
|
|
299
|
+
result: dict[str, Json] = {}
|
|
300
|
+
for key, value in pairs:
|
|
301
|
+
text = encode_key(key, loc)
|
|
302
|
+
if text in result:
|
|
303
|
+
raise fail(
|
|
304
|
+
d.DUPLICATE_MEMBER,
|
|
305
|
+
construct,
|
|
306
|
+
(*loc, text),
|
|
307
|
+
f"{construct} has {text!r} more than once",
|
|
308
|
+
)
|
|
309
|
+
result[text] = encode_value(value, (*loc, text))
|
|
310
|
+
return result
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _unkeyed[K, A](
|
|
314
|
+
j: object,
|
|
315
|
+
decode_key: Callable[[str, Location], K],
|
|
316
|
+
decode_value: Callable[[object, Location], A],
|
|
317
|
+
loc: Location,
|
|
318
|
+
construct: str,
|
|
319
|
+
) -> list[tuple[K, A]]:
|
|
320
|
+
obj = _expect_object(j, loc, construct)
|
|
321
|
+
return [
|
|
322
|
+
(decode_key(key, (*loc, key)), decode_value(value, (*loc, key)))
|
|
323
|
+
for key, value in obj.items()
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# --- Names ---
|
|
328
|
+
|
|
329
|
+
_WORD = re.compile(r"^[a-z0-9]+$")
|
|
330
|
+
_INITIALISM = re.compile(r"^[A-Z]{2,}$")
|
|
331
|
+
_LOWER_SEGMENT = re.compile(r"^[a-z0-9]+$")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _single_letter(word: str) -> bool:
|
|
335
|
+
return len(word) == 1 and word.isalpha()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def encode_name(name: Name, loc: Location = ()) -> str:
|
|
339
|
+
"""Write a name canonically, collapsing single-letter runs into initialisms.
|
|
340
|
+
|
|
341
|
+
Example:
|
|
342
|
+
>>> encode_name(Name(("value", "in", "u", "s", "d")))
|
|
343
|
+
'value-in-USD'
|
|
344
|
+
"""
|
|
345
|
+
segments: list[str] = []
|
|
346
|
+
run: list[str] = []
|
|
347
|
+
|
|
348
|
+
def flush() -> None:
|
|
349
|
+
if len(run) >= 2:
|
|
350
|
+
segments.append("".join(run).upper())
|
|
351
|
+
else:
|
|
352
|
+
segments.extend(run)
|
|
353
|
+
run.clear()
|
|
354
|
+
|
|
355
|
+
for word in name:
|
|
356
|
+
if not _WORD.match(word):
|
|
357
|
+
raise fail(
|
|
358
|
+
d.INVALID_NAME,
|
|
359
|
+
"Name",
|
|
360
|
+
loc,
|
|
361
|
+
f"name word {word!r} is not lowercase letters and digits",
|
|
362
|
+
)
|
|
363
|
+
if _single_letter(word):
|
|
364
|
+
run.append(word)
|
|
365
|
+
else:
|
|
366
|
+
flush()
|
|
367
|
+
segments.append(word)
|
|
368
|
+
flush()
|
|
369
|
+
if not segments:
|
|
370
|
+
raise fail(d.INVALID_NAME, "Name", loc, "a name needs at least one word")
|
|
371
|
+
return "-".join(segments)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def decode_name(j: object, loc: Location = ()) -> Name:
|
|
375
|
+
"""Read a canonical name string such as ``value-in-USD``."""
|
|
376
|
+
text = _expect_str(j, loc, "Name")
|
|
377
|
+
words: list[str] = []
|
|
378
|
+
previous = ""
|
|
379
|
+
for segment in text.split("-"):
|
|
380
|
+
if _INITIALISM.match(segment):
|
|
381
|
+
kind = "initialism"
|
|
382
|
+
parts = [ch.lower() for ch in segment]
|
|
383
|
+
elif _LOWER_SEGMENT.match(segment):
|
|
384
|
+
kind = "single" if _single_letter(segment) else "word"
|
|
385
|
+
parts = [segment]
|
|
386
|
+
else:
|
|
387
|
+
raise fail(
|
|
388
|
+
d.INVALID_NAME,
|
|
389
|
+
"Name",
|
|
390
|
+
loc,
|
|
391
|
+
f"name {text!r} has an invalid segment {segment!r}",
|
|
392
|
+
)
|
|
393
|
+
if kind in ("initialism", "single") and previous in ("initialism", "single"):
|
|
394
|
+
raise fail(
|
|
395
|
+
d.INVALID_NAME,
|
|
396
|
+
"Name",
|
|
397
|
+
loc,
|
|
398
|
+
f"name {text!r} has adjacent initialisms or single letters, which "
|
|
399
|
+
"the Python word-list model cannot represent",
|
|
400
|
+
)
|
|
401
|
+
previous = kind
|
|
402
|
+
words.extend(parts)
|
|
403
|
+
return Name(tuple(words))
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def encode_path(path: Path, loc: Location = ()) -> str:
|
|
407
|
+
return "/".join(encode_name(n, loc) for n in path)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def decode_path(j: object, loc: Location = ()) -> Path:
|
|
411
|
+
text = _expect_str(j, loc, "Path")
|
|
412
|
+
if not text:
|
|
413
|
+
return Path(())
|
|
414
|
+
return Path(tuple(decode_name(part, loc) for part in text.split("/")))
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
_FQNAME = re.compile(r"^([^:#]*):([^:#]*)#([^:#/]+)$")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def encode_fqname(fqn: FQName, loc: Location = ()) -> str:
|
|
421
|
+
return (
|
|
422
|
+
f"{encode_path(fqn.package_path, loc)}:{encode_path(fqn.module_path, loc)}"
|
|
423
|
+
f"#{encode_name(fqn.local_name, loc)}"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def decode_fqname(j: object, loc: Location = ()) -> FQName:
|
|
428
|
+
text = _expect_str(j, loc, "FQName")
|
|
429
|
+
match = _FQNAME.match(text)
|
|
430
|
+
if match is None:
|
|
431
|
+
raise fail(
|
|
432
|
+
d.INVALID_NAME, "FQName", loc, f"{text!r} is not package:module#name"
|
|
433
|
+
)
|
|
434
|
+
pkg, mod, local = match.groups()
|
|
435
|
+
return FQName(decode_path(pkg, loc), decode_path(mod, loc), decode_name(local, loc))
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _name_key(name: Name, loc: Location) -> str:
|
|
439
|
+
return encode_name(name, loc)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _path_key(path: Path, loc: Location) -> str:
|
|
443
|
+
return encode_path(path, loc)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
# --- Attributes ---
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def _encode_source(
|
|
450
|
+
src: t.SourceLocation | v.SourceLocation,
|
|
451
|
+
) -> dict[str, Json]:
|
|
452
|
+
return {
|
|
453
|
+
"startLine": src.start_line,
|
|
454
|
+
"startColumn": src.start_column,
|
|
455
|
+
"endLine": src.end_line,
|
|
456
|
+
"endColumn": src.end_column,
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _decode_source(j: object, loc: Location) -> tuple[int, int, int, int]:
|
|
461
|
+
obj = _expect_object(j, loc, "SourceLocation")
|
|
462
|
+
keys = ("startLine", "startColumn", "endLine", "endColumn")
|
|
463
|
+
_check_members(obj, (frozenset(keys), frozenset()), loc, "SourceLocation")
|
|
464
|
+
values = []
|
|
465
|
+
for key in keys:
|
|
466
|
+
value = obj[key]
|
|
467
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
468
|
+
raise fail(
|
|
469
|
+
d.INVALID_JSON,
|
|
470
|
+
"SourceLocation",
|
|
471
|
+
(*loc, key),
|
|
472
|
+
f"{key} must be an integer",
|
|
473
|
+
)
|
|
474
|
+
values.append(value)
|
|
475
|
+
return values[0], values[1], values[2], values[3]
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def _encode_extensions(ext: dict[FQName, Any], loc: Location) -> dict[str, Json]:
|
|
479
|
+
return {encode_fqname(k, loc): value for k, value in ext.items()}
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _decode_extensions(j: object, loc: Location) -> dict[FQName, Any]:
|
|
483
|
+
obj = _expect_object(j, loc, "extensions")
|
|
484
|
+
return {decode_fqname(k, (*loc, k)): value for k, value in obj.items()}
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _encode_type_attributes(a: t.TypeAttributes, loc: Location) -> dict[str, Json]:
|
|
488
|
+
out: dict[str, Json] = {}
|
|
489
|
+
if a.source is not None:
|
|
490
|
+
out["source"] = _encode_source(a.source)
|
|
491
|
+
if a.constraints is not None:
|
|
492
|
+
raise fail(
|
|
493
|
+
d.NO_V4_SPELLING,
|
|
494
|
+
"TypeConstraints",
|
|
495
|
+
(*loc, "attributes", "constraints"),
|
|
496
|
+
"structured type constraints have no spelling in the draft v4 profile",
|
|
497
|
+
)
|
|
498
|
+
if a.extensions:
|
|
499
|
+
out["extensions"] = _encode_extensions(a.extensions, (*loc, "attributes"))
|
|
500
|
+
return out
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _decode_type_attributes(body: dict[str, object], loc: Location) -> t.TypeAttributes:
|
|
504
|
+
if "attributes" not in body:
|
|
505
|
+
return t.TypeAttributes()
|
|
506
|
+
here = (*loc, "attributes")
|
|
507
|
+
obj = _expect_object(body["attributes"], here, "TypeAttributes")
|
|
508
|
+
_check_members(obj, _m("", "source extensions"), here, "TypeAttributes")
|
|
509
|
+
source = (
|
|
510
|
+
t.SourceLocation(*_decode_source(obj["source"], (*here, "source")))
|
|
511
|
+
if "source" in obj
|
|
512
|
+
else None
|
|
513
|
+
)
|
|
514
|
+
ext = (
|
|
515
|
+
_decode_extensions(obj["extensions"], (*here, "extensions"))
|
|
516
|
+
if "extensions" in obj
|
|
517
|
+
else {}
|
|
518
|
+
)
|
|
519
|
+
return t.TypeAttributes(source=source, extensions=ext)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _encode_value_attributes(a: v.ValueAttributes, loc: Location) -> dict[str, Json]:
|
|
523
|
+
out: dict[str, Json] = {}
|
|
524
|
+
if a.source is not None:
|
|
525
|
+
out["source"] = _encode_source(a.source)
|
|
526
|
+
if a.inferred_type is not None:
|
|
527
|
+
out["inferredType"] = encode_type(
|
|
528
|
+
a.inferred_type, (*loc, "attributes", "inferredType")
|
|
529
|
+
)
|
|
530
|
+
if a.extensions:
|
|
531
|
+
out["extensions"] = _encode_extensions(a.extensions, (*loc, "attributes"))
|
|
532
|
+
return out
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _decode_value_attributes(
|
|
536
|
+
body: dict[str, object], loc: Location
|
|
537
|
+
) -> v.ValueAttributes:
|
|
538
|
+
if "attributes" not in body:
|
|
539
|
+
return v.ValueAttributes()
|
|
540
|
+
here = (*loc, "attributes")
|
|
541
|
+
obj = _expect_object(body["attributes"], here, "ValueAttributes")
|
|
542
|
+
_check_members(
|
|
543
|
+
obj, _m("", "source inferredType extensions"), here, "ValueAttributes"
|
|
544
|
+
)
|
|
545
|
+
return v.ValueAttributes(
|
|
546
|
+
source=(
|
|
547
|
+
v.SourceLocation(*_decode_source(obj["source"], (*here, "source")))
|
|
548
|
+
if "source" in obj
|
|
549
|
+
else None
|
|
550
|
+
),
|
|
551
|
+
inferred_type=(
|
|
552
|
+
decode_type(obj["inferredType"], (*here, "inferredType"))
|
|
553
|
+
if "inferredType" in obj
|
|
554
|
+
else None
|
|
555
|
+
),
|
|
556
|
+
extensions=(
|
|
557
|
+
_decode_extensions(obj["extensions"], (*here, "extensions"))
|
|
558
|
+
if "extensions" in obj
|
|
559
|
+
else {}
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _with_attributes(
|
|
565
|
+
attrs: dict[str, Json], members: dict[str, Json]
|
|
566
|
+
) -> dict[str, Json]:
|
|
567
|
+
return {"attributes": attrs, **members} if attrs else members
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
# --- Types ---
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def encode_type(tpe: t.Type, loc: Location = ()) -> Json:
|
|
574
|
+
"""Encode a v4 type in the expanded draft spelling."""
|
|
575
|
+
attrs = _encode_type_attributes(tpe.attributes, loc)
|
|
576
|
+
|
|
577
|
+
def node(tag: str, members: dict[str, Json]) -> Json:
|
|
578
|
+
return _wrap(tag, _with_attributes(attrs, members))
|
|
579
|
+
|
|
580
|
+
here = (*loc, type(tpe).__name__)
|
|
581
|
+
match tpe:
|
|
582
|
+
case t.Variable(_, name):
|
|
583
|
+
return node("Variable", {"name": encode_name(name, here)})
|
|
584
|
+
case t.Reference(_, fqn, args):
|
|
585
|
+
members: dict[str, Json] = {"fqname": encode_fqname(fqn, here)}
|
|
586
|
+
if args:
|
|
587
|
+
members["args"] = [
|
|
588
|
+
encode_type(a, (*here, "args", i)) for i, a in enumerate(args)
|
|
589
|
+
]
|
|
590
|
+
return node("Reference", members)
|
|
591
|
+
case t.Tuple(_, elements):
|
|
592
|
+
return node(
|
|
593
|
+
"Tuple",
|
|
594
|
+
{
|
|
595
|
+
"elements": [
|
|
596
|
+
encode_type(e, (*here, "elements", i))
|
|
597
|
+
for i, e in enumerate(elements)
|
|
598
|
+
]
|
|
599
|
+
},
|
|
600
|
+
)
|
|
601
|
+
case t.Record(_, fields):
|
|
602
|
+
return node("Record", {"fields": _encode_type_fields(fields, here)})
|
|
603
|
+
case t.ExtensibleRecord(_, var, fields):
|
|
604
|
+
return node(
|
|
605
|
+
"ExtensibleRecord",
|
|
606
|
+
{
|
|
607
|
+
"variable": encode_name(var, here),
|
|
608
|
+
"fields": _encode_type_fields(fields, here),
|
|
609
|
+
},
|
|
610
|
+
)
|
|
611
|
+
case t.Function(_, arg, ret):
|
|
612
|
+
return node(
|
|
613
|
+
"Function",
|
|
614
|
+
{
|
|
615
|
+
"parameterType": encode_type(arg, (*here, "parameterType")),
|
|
616
|
+
"returnType": encode_type(ret, (*here, "returnType")),
|
|
617
|
+
},
|
|
618
|
+
)
|
|
619
|
+
case t.Unit(_):
|
|
620
|
+
return node("Unit", {})
|
|
621
|
+
case t.Union(_, alternatives):
|
|
622
|
+
_check_alternatives(len(alternatives), (*here, "alternatives"))
|
|
623
|
+
return node(
|
|
624
|
+
"Union",
|
|
625
|
+
{
|
|
626
|
+
"alternatives": [
|
|
627
|
+
encode_type(x, (*here, "alternatives", i))
|
|
628
|
+
for i, x in enumerate(alternatives)
|
|
629
|
+
]
|
|
630
|
+
},
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def _check_alternatives(count: int, loc: Location) -> None:
|
|
635
|
+
if count < 2:
|
|
636
|
+
raise fail(
|
|
637
|
+
d.INVALID_JSON,
|
|
638
|
+
"Union",
|
|
639
|
+
loc,
|
|
640
|
+
f"a union needs two or more alternatives, found {count}",
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _encode_type_fields(fields: list[t.Field], loc: Location) -> dict[str, Json]:
|
|
645
|
+
return _keyed(
|
|
646
|
+
[(f.name, f.tpe) for f in fields],
|
|
647
|
+
_name_key,
|
|
648
|
+
encode_type,
|
|
649
|
+
(*loc, "fields"),
|
|
650
|
+
"Record",
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def decode_type(j: object, loc: Location = ()) -> t.Type:
|
|
655
|
+
"""Decode a v4 type from the expanded draft spelling."""
|
|
656
|
+
tag, body, here = _node(j, loc, "Type", TYPE_NODES)
|
|
657
|
+
attrs = _decode_type_attributes(body, here)
|
|
658
|
+
|
|
659
|
+
def fields() -> list[t.Field]:
|
|
660
|
+
return [
|
|
661
|
+
t.Field(n, tp)
|
|
662
|
+
for n, tp in _unkeyed(
|
|
663
|
+
body["fields"], decode_name, decode_type, (*here, "fields"), "Record"
|
|
664
|
+
)
|
|
665
|
+
]
|
|
666
|
+
|
|
667
|
+
match tag:
|
|
668
|
+
case "Variable":
|
|
669
|
+
return t.Variable(attrs, decode_name(body["name"], (*here, "name")))
|
|
670
|
+
case "Reference":
|
|
671
|
+
args = (
|
|
672
|
+
_decode_list(decode_type, body["args"], (*here, "args"), "Type")
|
|
673
|
+
if "args" in body
|
|
674
|
+
else []
|
|
675
|
+
)
|
|
676
|
+
return t.Reference(attrs, decode_fqname(body["fqname"], here), args)
|
|
677
|
+
case "Tuple":
|
|
678
|
+
return t.Tuple(
|
|
679
|
+
attrs,
|
|
680
|
+
_decode_list(
|
|
681
|
+
decode_type, body["elements"], (*here, "elements"), "Type"
|
|
682
|
+
),
|
|
683
|
+
)
|
|
684
|
+
case "Record":
|
|
685
|
+
return t.Record(attrs, fields())
|
|
686
|
+
case "ExtensibleRecord":
|
|
687
|
+
return t.ExtensibleRecord(
|
|
688
|
+
attrs, decode_name(body["variable"], (*here, "variable")), fields()
|
|
689
|
+
)
|
|
690
|
+
case "Function":
|
|
691
|
+
return t.Function(
|
|
692
|
+
attrs,
|
|
693
|
+
decode_type(body["parameterType"], (*here, "parameterType")),
|
|
694
|
+
decode_type(body["returnType"], (*here, "returnType")),
|
|
695
|
+
)
|
|
696
|
+
case "Union":
|
|
697
|
+
alternatives = _decode_list(
|
|
698
|
+
decode_type, body["alternatives"], (*here, "alternatives"), "Union"
|
|
699
|
+
)
|
|
700
|
+
_check_alternatives(len(alternatives), (*here, "alternatives"))
|
|
701
|
+
return t.Union(attrs, alternatives)
|
|
702
|
+
case _:
|
|
703
|
+
return t.Unit(attrs)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _decode_list[A](
|
|
707
|
+
decode: Callable[[object, Location], A], j: object, loc: Location, construct: str
|
|
708
|
+
) -> list[A]:
|
|
709
|
+
return [
|
|
710
|
+
decode(item, (*loc, i))
|
|
711
|
+
for i, item in enumerate(_expect_list(j, loc, construct))
|
|
712
|
+
]
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
# --- Literals and documents ---
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _encode_document(value: doc.Document) -> Json:
|
|
719
|
+
match value:
|
|
720
|
+
case doc.DocNull():
|
|
721
|
+
return None
|
|
722
|
+
case doc.DocBool(b):
|
|
723
|
+
return b
|
|
724
|
+
case doc.DocInt(i):
|
|
725
|
+
return i
|
|
726
|
+
case doc.DocFloat(f):
|
|
727
|
+
return f
|
|
728
|
+
case doc.DocString(s):
|
|
729
|
+
return s
|
|
730
|
+
case doc.DocArray(elements):
|
|
731
|
+
return [_encode_document(e) for e in elements]
|
|
732
|
+
case doc.DocObject(fields):
|
|
733
|
+
return {k: _encode_document(e) for k, e in fields.items()}
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def _decode_document(j: object) -> doc.Document:
|
|
737
|
+
if j is None:
|
|
738
|
+
return doc.DocNull()
|
|
739
|
+
if isinstance(j, bool):
|
|
740
|
+
return doc.DocBool(j)
|
|
741
|
+
if isinstance(j, int):
|
|
742
|
+
return doc.DocInt(j)
|
|
743
|
+
if isinstance(j, float):
|
|
744
|
+
return doc.DocFloat(j)
|
|
745
|
+
if isinstance(j, str):
|
|
746
|
+
return doc.DocString(j)
|
|
747
|
+
if isinstance(j, list):
|
|
748
|
+
return doc.DocArray([_decode_document(e) for e in j])
|
|
749
|
+
obj = _expect_object(j, (), "Document")
|
|
750
|
+
return doc.DocObject({k: _decode_document(e) for k, e in obj.items()})
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def encode_literal(literal: lit.Literal, loc: Location = ()) -> Json:
|
|
754
|
+
"""Encode a literal as ``{"IntegerLiteral": 42}`` and similar."""
|
|
755
|
+
match literal:
|
|
756
|
+
case lit.BoolLiteral(value):
|
|
757
|
+
return {"BoolLiteral": value}
|
|
758
|
+
case lit.CharLiteral(value):
|
|
759
|
+
return {"CharLiteral": value}
|
|
760
|
+
case lit.StringLiteral(value):
|
|
761
|
+
return {"StringLiteral": value}
|
|
762
|
+
case lit.IntegerLiteral(value):
|
|
763
|
+
return {"IntegerLiteral": value}
|
|
764
|
+
case lit.FloatLiteral(value):
|
|
765
|
+
return {"FloatLiteral": value}
|
|
766
|
+
case lit.DecimalLiteral(value):
|
|
767
|
+
return {"DecimalLiteral": str(value)}
|
|
768
|
+
case lit.DocumentLiteral(value):
|
|
769
|
+
return {"DocumentLiteral": _encode_document(value)}
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def decode_literal(j: object, loc: Location = ()) -> lit.Literal:
|
|
773
|
+
"""Decode a literal; ``WholeNumberLiteral`` reads as ``IntegerLiteral``."""
|
|
774
|
+
obj = _expect_object(j, loc, "Literal")
|
|
775
|
+
if len(obj) != 1 or next(iter(obj)) not in LITERAL_KINDS:
|
|
776
|
+
raise fail(
|
|
777
|
+
d.UNKNOWN_NODE,
|
|
778
|
+
"Literal",
|
|
779
|
+
loc,
|
|
780
|
+
f"unknown literal spelling with members {sorted(obj)}",
|
|
781
|
+
)
|
|
782
|
+
((tag, payload),) = obj.items()
|
|
783
|
+
here = (*loc, tag)
|
|
784
|
+
|
|
785
|
+
def bad(message: str) -> DiagnosticError:
|
|
786
|
+
return fail(d.INVALID_LITERAL, tag, here, message)
|
|
787
|
+
|
|
788
|
+
match tag:
|
|
789
|
+
case "BoolLiteral":
|
|
790
|
+
if not isinstance(payload, bool):
|
|
791
|
+
raise bad("BoolLiteral expects a boolean")
|
|
792
|
+
return lit.BoolLiteral(payload)
|
|
793
|
+
case "CharLiteral":
|
|
794
|
+
if not isinstance(payload, str) or len(payload) != 1:
|
|
795
|
+
raise bad("CharLiteral expects a single character")
|
|
796
|
+
return lit.CharLiteral(payload)
|
|
797
|
+
case "StringLiteral":
|
|
798
|
+
if not isinstance(payload, str):
|
|
799
|
+
raise bad("StringLiteral expects a string")
|
|
800
|
+
return lit.StringLiteral(payload)
|
|
801
|
+
case "IntegerLiteral" | "WholeNumberLiteral":
|
|
802
|
+
if isinstance(payload, bool) or not isinstance(payload, int):
|
|
803
|
+
raise bad(f"{tag} expects an integer")
|
|
804
|
+
return lit.IntegerLiteral(payload)
|
|
805
|
+
case "FloatLiteral":
|
|
806
|
+
if isinstance(payload, bool) or not isinstance(payload, (int, float)):
|
|
807
|
+
raise bad("FloatLiteral expects a number")
|
|
808
|
+
return lit.FloatLiteral(float(payload))
|
|
809
|
+
case "DecimalLiteral":
|
|
810
|
+
if not isinstance(payload, str):
|
|
811
|
+
raise bad("DecimalLiteral expects a string")
|
|
812
|
+
try:
|
|
813
|
+
return lit.DecimalLiteral(Decimal(payload))
|
|
814
|
+
except InvalidOperation:
|
|
815
|
+
raise bad(f"invalid decimal {payload!r}") from None
|
|
816
|
+
case _:
|
|
817
|
+
return lit.DocumentLiteral(_decode_document(payload))
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
# --- Patterns ---
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def encode_pattern(p: v.Pattern, loc: Location = ()) -> Json:
|
|
824
|
+
"""Encode a v4 pattern in the expanded draft spelling."""
|
|
825
|
+
attrs = _encode_value_attributes(p.attributes, loc)
|
|
826
|
+
here = (*loc, type(p).__name__)
|
|
827
|
+
|
|
828
|
+
def node(tag: str, members: dict[str, Json]) -> Json:
|
|
829
|
+
return _wrap(tag, _with_attributes(attrs, members))
|
|
830
|
+
|
|
831
|
+
def many(items: list[v.Pattern], key: str) -> list[Json]:
|
|
832
|
+
return [encode_pattern(x, (*here, key, i)) for i, x in enumerate(items)]
|
|
833
|
+
|
|
834
|
+
match p:
|
|
835
|
+
case v.WildcardPattern(_):
|
|
836
|
+
return node("WildcardPattern", {})
|
|
837
|
+
case v.AsPattern(_, inner, name):
|
|
838
|
+
return node(
|
|
839
|
+
"AsPattern",
|
|
840
|
+
{
|
|
841
|
+
"pattern": encode_pattern(inner, (*here, "pattern")),
|
|
842
|
+
"name": encode_name(name, here),
|
|
843
|
+
},
|
|
844
|
+
)
|
|
845
|
+
case v.TuplePattern(_, elements):
|
|
846
|
+
return node("TuplePattern", {"patterns": many(elements, "patterns")})
|
|
847
|
+
case v.ConstructorPattern(_, ctor, args):
|
|
848
|
+
return node(
|
|
849
|
+
"ConstructorPattern",
|
|
850
|
+
{
|
|
851
|
+
"fqname": encode_fqname(ctor, here),
|
|
852
|
+
"patterns": many(args, "patterns"),
|
|
853
|
+
},
|
|
854
|
+
)
|
|
855
|
+
case v.EmptyListPattern(_):
|
|
856
|
+
return node("EmptyListPattern", {})
|
|
857
|
+
case v.HeadTailPattern(_, head, tail):
|
|
858
|
+
return node(
|
|
859
|
+
"HeadTailPattern",
|
|
860
|
+
{
|
|
861
|
+
"head": encode_pattern(head, (*here, "head")),
|
|
862
|
+
"tail": encode_pattern(tail, (*here, "tail")),
|
|
863
|
+
},
|
|
864
|
+
)
|
|
865
|
+
case v.LiteralPattern(_, literal):
|
|
866
|
+
return node("LiteralPattern", {"literal": encode_literal(literal, here)})
|
|
867
|
+
case v.UnitPattern(_):
|
|
868
|
+
return node("UnitPattern", {})
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def decode_pattern(j: object, loc: Location = ()) -> v.Pattern:
|
|
872
|
+
"""Decode a v4 pattern from the expanded draft spelling."""
|
|
873
|
+
tag, body, here = _node(j, loc, "Pattern", PATTERN_NODES)
|
|
874
|
+
attrs = _decode_value_attributes(body, here)
|
|
875
|
+
|
|
876
|
+
def sub(key: str) -> v.Pattern:
|
|
877
|
+
return decode_pattern(body[key], (*here, key))
|
|
878
|
+
|
|
879
|
+
def many(key: str) -> list[v.Pattern]:
|
|
880
|
+
return _decode_list(decode_pattern, body[key], (*here, key), "Pattern")
|
|
881
|
+
|
|
882
|
+
match tag:
|
|
883
|
+
case "WildcardPattern":
|
|
884
|
+
return v.WildcardPattern(attrs)
|
|
885
|
+
case "AsPattern":
|
|
886
|
+
return v.AsPattern(attrs, sub("pattern"), decode_name(body["name"], here))
|
|
887
|
+
case "TuplePattern":
|
|
888
|
+
return v.TuplePattern(attrs, many("patterns"))
|
|
889
|
+
case "ConstructorPattern":
|
|
890
|
+
return v.ConstructorPattern(
|
|
891
|
+
attrs, decode_fqname(body["fqname"], here), many("patterns")
|
|
892
|
+
)
|
|
893
|
+
case "EmptyListPattern":
|
|
894
|
+
return v.EmptyListPattern(attrs)
|
|
895
|
+
case "HeadTailPattern":
|
|
896
|
+
return v.HeadTailPattern(attrs, sub("head"), sub("tail"))
|
|
897
|
+
case "LiteralPattern":
|
|
898
|
+
return v.LiteralPattern(
|
|
899
|
+
attrs, decode_literal(body["literal"], (*here, "literal"))
|
|
900
|
+
)
|
|
901
|
+
case _:
|
|
902
|
+
return v.UnitPattern(attrs)
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
# --- Values ---
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
def encode_value(val: v.Value, loc: Location = ()) -> Json:
|
|
909
|
+
"""Encode a v4 value in the expanded draft spelling.
|
|
910
|
+
|
|
911
|
+
Raises:
|
|
912
|
+
DiagnosticError: With ``no_v4_spelling`` for ``Native`` and ``External``
|
|
913
|
+
nodes, or ``duplicate_member`` for repeated record fields.
|
|
914
|
+
"""
|
|
915
|
+
construct = type(val).__name__
|
|
916
|
+
here = (*loc, construct)
|
|
917
|
+
if isinstance(val, (v.Native, v.External)):
|
|
918
|
+
raise fail(
|
|
919
|
+
d.NO_V4_SPELLING,
|
|
920
|
+
construct,
|
|
921
|
+
loc,
|
|
922
|
+
f"{construct} value nodes have no spelling in the draft v4 profile; "
|
|
923
|
+
"upstream v4 makes them definition bodies",
|
|
924
|
+
)
|
|
925
|
+
attrs = _encode_value_attributes(val.attributes, loc)
|
|
926
|
+
|
|
927
|
+
def node(tag: str, members: dict[str, Json]) -> Json:
|
|
928
|
+
return _wrap(tag, _with_attributes(attrs, members))
|
|
929
|
+
|
|
930
|
+
def sub(x: v.Value, key: str) -> Json:
|
|
931
|
+
return encode_value(x, (*here, key))
|
|
932
|
+
|
|
933
|
+
def many(items: list[v.Value], key: str) -> list[Json]:
|
|
934
|
+
return [encode_value(x, (*here, key, i)) for i, x in enumerate(items)]
|
|
935
|
+
|
|
936
|
+
def fields(items: list[tuple[Name, v.Value]], key: str = "fields") -> Json:
|
|
937
|
+
return _keyed(items, _name_key, encode_value, (*here, key), construct)
|
|
938
|
+
|
|
939
|
+
match val:
|
|
940
|
+
case v.LiteralValue(_, literal):
|
|
941
|
+
return node("Literal", {"literal": encode_literal(literal, here)})
|
|
942
|
+
case v.Constructor(_, fqn):
|
|
943
|
+
return node("Constructor", {"fqname": encode_fqname(fqn, here)})
|
|
944
|
+
case v.Tuple(_, elements):
|
|
945
|
+
return node("Tuple", {"elements": many(elements, "elements")})
|
|
946
|
+
case v.List(_, elements):
|
|
947
|
+
return node("List", {"items": many(elements, "items")})
|
|
948
|
+
case v.Record(_, record_fields):
|
|
949
|
+
return node("Record", {"fields": fields(record_fields)})
|
|
950
|
+
case v.Variable(_, name):
|
|
951
|
+
return node("Variable", {"name": encode_name(name, here)})
|
|
952
|
+
case v.Reference(_, fqn):
|
|
953
|
+
return node("Reference", {"fqname": encode_fqname(fqn, here)})
|
|
954
|
+
case v.Field(_, subject, name):
|
|
955
|
+
return node(
|
|
956
|
+
"Field",
|
|
957
|
+
{"target": sub(subject, "target"), "name": encode_name(name, here)},
|
|
958
|
+
)
|
|
959
|
+
case v.FieldFunction(_, name):
|
|
960
|
+
return node("FieldFunction", {"name": encode_name(name, here)})
|
|
961
|
+
case v.Apply(_, function, argument):
|
|
962
|
+
return node(
|
|
963
|
+
"Apply",
|
|
964
|
+
{
|
|
965
|
+
"function": sub(function, "function"),
|
|
966
|
+
"argument": sub(argument, "argument"),
|
|
967
|
+
},
|
|
968
|
+
)
|
|
969
|
+
case v.Lambda(_, pattern, body):
|
|
970
|
+
return node(
|
|
971
|
+
"Lambda",
|
|
972
|
+
{
|
|
973
|
+
"pattern": encode_pattern(pattern, (*here, "pattern")),
|
|
974
|
+
"body": sub(body, "body"),
|
|
975
|
+
},
|
|
976
|
+
)
|
|
977
|
+
case v.LetDefinition(_, name, definition, in_value):
|
|
978
|
+
return node(
|
|
979
|
+
"LetDefinition",
|
|
980
|
+
{
|
|
981
|
+
"name": encode_name(name, here),
|
|
982
|
+
"definition": encode_value_definition(
|
|
983
|
+
definition, (*here, "definition")
|
|
984
|
+
),
|
|
985
|
+
"in": sub(in_value, "in"),
|
|
986
|
+
},
|
|
987
|
+
)
|
|
988
|
+
case v.LetRecursion(_, definitions, in_value):
|
|
989
|
+
return node(
|
|
990
|
+
"LetRecursion",
|
|
991
|
+
{
|
|
992
|
+
"definitions": _keyed(
|
|
993
|
+
definitions,
|
|
994
|
+
_name_key,
|
|
995
|
+
encode_value_definition,
|
|
996
|
+
(*here, "definitions"),
|
|
997
|
+
construct,
|
|
998
|
+
),
|
|
999
|
+
"in": sub(in_value, "in"),
|
|
1000
|
+
},
|
|
1001
|
+
)
|
|
1002
|
+
case v.Destructure(_, pattern, subject, in_value):
|
|
1003
|
+
return node(
|
|
1004
|
+
"Destructure",
|
|
1005
|
+
{
|
|
1006
|
+
"pattern": encode_pattern(pattern, (*here, "pattern")),
|
|
1007
|
+
"value": sub(subject, "value"),
|
|
1008
|
+
"in": sub(in_value, "in"),
|
|
1009
|
+
},
|
|
1010
|
+
)
|
|
1011
|
+
case v.IfThenElse(_, cond, then_branch, else_branch):
|
|
1012
|
+
return node(
|
|
1013
|
+
"IfThenElse",
|
|
1014
|
+
{
|
|
1015
|
+
"condition": sub(cond, "condition"),
|
|
1016
|
+
"then": sub(then_branch, "then"),
|
|
1017
|
+
"else": sub(else_branch, "else"),
|
|
1018
|
+
},
|
|
1019
|
+
)
|
|
1020
|
+
case v.PatternMatch(_, subject, cases):
|
|
1021
|
+
return node(
|
|
1022
|
+
"PatternMatch",
|
|
1023
|
+
{
|
|
1024
|
+
"value": sub(subject, "value"),
|
|
1025
|
+
"cases": [
|
|
1026
|
+
{
|
|
1027
|
+
"pattern": encode_pattern(
|
|
1028
|
+
p, (*here, "cases", i, "pattern")
|
|
1029
|
+
),
|
|
1030
|
+
"body": encode_value(b, (*here, "cases", i, "body")),
|
|
1031
|
+
}
|
|
1032
|
+
for i, (p, b) in enumerate(cases)
|
|
1033
|
+
],
|
|
1034
|
+
},
|
|
1035
|
+
)
|
|
1036
|
+
case v.UpdateRecord(_, subject, update_fields):
|
|
1037
|
+
return node(
|
|
1038
|
+
"UpdateRecord",
|
|
1039
|
+
{"target": sub(subject, "target"), "fields": fields(update_fields)},
|
|
1040
|
+
)
|
|
1041
|
+
case v.Unit(_):
|
|
1042
|
+
return node("Unit", {})
|
|
1043
|
+
case v.Hole(_, reason, expected_type):
|
|
1044
|
+
members: dict[str, Json] = {
|
|
1045
|
+
"reason": encode_hole_reason(reason, (*here, "reason"))
|
|
1046
|
+
}
|
|
1047
|
+
if expected_type is not None:
|
|
1048
|
+
members["expectedType"] = encode_type(
|
|
1049
|
+
expected_type, (*here, "expectedType")
|
|
1050
|
+
)
|
|
1051
|
+
return node("Hole", members)
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
def decode_value(j: object, loc: Location = ()) -> v.Value:
|
|
1055
|
+
"""Decode a v4 value from the expanded draft spelling."""
|
|
1056
|
+
tag, body, here = _node(j, loc, "Value", VALUE_NODES)
|
|
1057
|
+
attrs = _decode_value_attributes(body, here)
|
|
1058
|
+
|
|
1059
|
+
def sub(key: str) -> v.Value:
|
|
1060
|
+
return decode_value(body[key], (*here, key))
|
|
1061
|
+
|
|
1062
|
+
def many(key: str) -> list[v.Value]:
|
|
1063
|
+
return _decode_list(decode_value, body[key], (*here, key), "Value")
|
|
1064
|
+
|
|
1065
|
+
def fields() -> list[tuple[Name, v.Value]]:
|
|
1066
|
+
return _unkeyed(
|
|
1067
|
+
body["fields"], decode_name, decode_value, (*here, "fields"), tag
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
match tag:
|
|
1071
|
+
case "Literal":
|
|
1072
|
+
return v.LiteralValue(
|
|
1073
|
+
attrs, decode_literal(body["literal"], (*here, "literal"))
|
|
1074
|
+
)
|
|
1075
|
+
case "Constructor":
|
|
1076
|
+
return v.Constructor(attrs, decode_fqname(body["fqname"], here))
|
|
1077
|
+
case "Tuple":
|
|
1078
|
+
return v.Tuple(attrs, many("elements"))
|
|
1079
|
+
case "List":
|
|
1080
|
+
return v.List(attrs, many("items"))
|
|
1081
|
+
case "Record":
|
|
1082
|
+
return v.Record(attrs, fields())
|
|
1083
|
+
case "Variable":
|
|
1084
|
+
return v.Variable(attrs, decode_name(body["name"], here))
|
|
1085
|
+
case "Reference":
|
|
1086
|
+
return v.Reference(attrs, decode_fqname(body["fqname"], here))
|
|
1087
|
+
case "Field":
|
|
1088
|
+
return v.Field(attrs, sub("target"), decode_name(body["name"], here))
|
|
1089
|
+
case "FieldFunction":
|
|
1090
|
+
return v.FieldFunction(attrs, decode_name(body["name"], here))
|
|
1091
|
+
case "Apply":
|
|
1092
|
+
return v.Apply(attrs, sub("function"), sub("argument"))
|
|
1093
|
+
case "Lambda":
|
|
1094
|
+
return v.Lambda(
|
|
1095
|
+
attrs, decode_pattern(body["pattern"], (*here, "pattern")), sub("body")
|
|
1096
|
+
)
|
|
1097
|
+
case "LetDefinition":
|
|
1098
|
+
return v.LetDefinition(
|
|
1099
|
+
attrs,
|
|
1100
|
+
decode_name(body["name"], here),
|
|
1101
|
+
decode_value_definition(body["definition"], (*here, "definition")),
|
|
1102
|
+
sub("in"),
|
|
1103
|
+
)
|
|
1104
|
+
case "LetRecursion":
|
|
1105
|
+
return v.LetRecursion(
|
|
1106
|
+
attrs,
|
|
1107
|
+
dict(
|
|
1108
|
+
_unkeyed(
|
|
1109
|
+
body["definitions"],
|
|
1110
|
+
decode_name,
|
|
1111
|
+
decode_value_definition,
|
|
1112
|
+
(*here, "definitions"),
|
|
1113
|
+
tag,
|
|
1114
|
+
)
|
|
1115
|
+
),
|
|
1116
|
+
sub("in"),
|
|
1117
|
+
)
|
|
1118
|
+
case "Destructure":
|
|
1119
|
+
return v.Destructure(
|
|
1120
|
+
attrs,
|
|
1121
|
+
decode_pattern(body["pattern"], (*here, "pattern")),
|
|
1122
|
+
sub("value"),
|
|
1123
|
+
sub("in"),
|
|
1124
|
+
)
|
|
1125
|
+
case "IfThenElse":
|
|
1126
|
+
return v.IfThenElse(attrs, sub("condition"), sub("then"), sub("else"))
|
|
1127
|
+
case "PatternMatch":
|
|
1128
|
+
cases: list[tuple[v.Pattern, v.Value]] = []
|
|
1129
|
+
for i, case in enumerate(
|
|
1130
|
+
_expect_list(body["cases"], (*here, "cases"), tag)
|
|
1131
|
+
):
|
|
1132
|
+
case_loc = (*here, "cases", i)
|
|
1133
|
+
case_obj = _expect_object(case, case_loc, "Case")
|
|
1134
|
+
_check_members(case_obj, _m("pattern body", ""), case_loc, "Case")
|
|
1135
|
+
cases.append(
|
|
1136
|
+
(
|
|
1137
|
+
decode_pattern(case_obj["pattern"], (*case_loc, "pattern")),
|
|
1138
|
+
decode_value(case_obj["body"], (*case_loc, "body")),
|
|
1139
|
+
)
|
|
1140
|
+
)
|
|
1141
|
+
return v.PatternMatch(attrs, sub("value"), cases)
|
|
1142
|
+
case "UpdateRecord":
|
|
1143
|
+
return v.UpdateRecord(attrs, sub("target"), fields())
|
|
1144
|
+
case "Unit":
|
|
1145
|
+
return v.Unit(attrs)
|
|
1146
|
+
case _:
|
|
1147
|
+
return v.Hole(
|
|
1148
|
+
attrs,
|
|
1149
|
+
decode_hole_reason(body["reason"], (*here, "reason")),
|
|
1150
|
+
decode_type(body["expectedType"], (*here, "expectedType"))
|
|
1151
|
+
if "expectedType" in body
|
|
1152
|
+
else None,
|
|
1153
|
+
)
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
# --- Hole reasons and incompleteness ---
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
def encode_hole_reason(reason: inc.HoleReason, loc: Location = ()) -> Json:
|
|
1160
|
+
"""Encode a hole reason as ``{"Kind": {members}}``."""
|
|
1161
|
+
match reason:
|
|
1162
|
+
case inc.UnresolvedReference(target):
|
|
1163
|
+
return _wrap(
|
|
1164
|
+
"UnresolvedReference",
|
|
1165
|
+
{"target": encode_fqname(target, (*loc, "UnresolvedReference"))},
|
|
1166
|
+
)
|
|
1167
|
+
case inc.DeletedDuringRefactor(tx_id):
|
|
1168
|
+
return _wrap("DeletedDuringRefactor", {"tx-id": tx_id})
|
|
1169
|
+
case inc.TypeMismatch(expected, found):
|
|
1170
|
+
return _wrap("TypeMismatch", {"expected": expected, "found": found})
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
def decode_hole_reason(j: object, loc: Location = ()) -> inc.HoleReason:
|
|
1174
|
+
"""Decode a hole reason; ``Draft`` is an incompleteness, not a reason."""
|
|
1175
|
+
tag, body, here = _node(j, loc, "HoleReason", HOLE_REASON_NODES)
|
|
1176
|
+
match tag:
|
|
1177
|
+
case "UnresolvedReference":
|
|
1178
|
+
return inc.UnresolvedReference(
|
|
1179
|
+
decode_fqname(body["target"], (*here, "target"))
|
|
1180
|
+
)
|
|
1181
|
+
case "DeletedDuringRefactor":
|
|
1182
|
+
return inc.DeletedDuringRefactor(
|
|
1183
|
+
_expect_str(body["tx-id"], (*here, "tx-id"), tag)
|
|
1184
|
+
)
|
|
1185
|
+
case _:
|
|
1186
|
+
return inc.TypeMismatch(
|
|
1187
|
+
_expect_str(body["expected"], (*here, "expected"), tag),
|
|
1188
|
+
_expect_str(body["found"], (*here, "found"), tag),
|
|
1189
|
+
)
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def encode_incompleteness(item: inc.Incompleteness, loc: Location = ()) -> Json:
|
|
1193
|
+
"""Encode an incompleteness as ``{"Hole": {...}}`` or ``{"Draft": {}}``."""
|
|
1194
|
+
match item:
|
|
1195
|
+
case inc.HoleIncompleteness(reason, partial):
|
|
1196
|
+
here = (*loc, "Hole")
|
|
1197
|
+
members: dict[str, Json] = {
|
|
1198
|
+
"reason": encode_hole_reason(reason, (*here, "reason"))
|
|
1199
|
+
}
|
|
1200
|
+
if partial is not None:
|
|
1201
|
+
members["partialBody"] = encode_type(partial, (*here, "partialBody"))
|
|
1202
|
+
return _wrap("Hole", members)
|
|
1203
|
+
case inc.DraftIncompleteness():
|
|
1204
|
+
return _wrap("Draft", {})
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def decode_incompleteness(j: object, loc: Location = ()) -> inc.Incompleteness:
|
|
1208
|
+
"""Decode ``{"Hole": {...}}`` or ``{"Draft": {}}``."""
|
|
1209
|
+
tag, body, here = _node(j, loc, "Incompleteness", INCOMPLETENESS_NODES)
|
|
1210
|
+
if tag == "Draft":
|
|
1211
|
+
return inc.DraftIncompleteness()
|
|
1212
|
+
return inc.HoleIncompleteness(
|
|
1213
|
+
decode_hole_reason(body["reason"], (*here, "reason")),
|
|
1214
|
+
decode_type(body["partialBody"], (*here, "partialBody"))
|
|
1215
|
+
if "partialBody" in body
|
|
1216
|
+
else None,
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
def _encode_inputs(
|
|
1221
|
+
inputs: list[tuple[Name, t.Type]], loc: Location, construct: str
|
|
1222
|
+
) -> dict[str, Json]:
|
|
1223
|
+
return _keyed(inputs, _name_key, encode_type, loc, construct)
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
def _encode_expression_body(defn: v.Definition, loc: Location) -> Json:
|
|
1227
|
+
here = (*loc, "ExpressionBody")
|
|
1228
|
+
for i, (name, attrs, tpe) in enumerate(defn.input_types):
|
|
1229
|
+
derived = v.ValueAttributes(inferred_type=tpe)
|
|
1230
|
+
if attrs not in (v.ValueAttributes(), derived):
|
|
1231
|
+
raise fail(
|
|
1232
|
+
d.NO_V4_SPELLING,
|
|
1233
|
+
"ValueDefinition",
|
|
1234
|
+
(*here, "inputTypes", i),
|
|
1235
|
+
f"input {encode_name(name, here)!r} carries attributes other than its "
|
|
1236
|
+
"declared type; ExpressionBody.inputTypes has no attribute slot. "
|
|
1237
|
+
"Drop them with morphir.ir.migrate.drop_input_attributes or "
|
|
1238
|
+
"`morphir-python ir convert --drop-input-attributes`",
|
|
1239
|
+
)
|
|
1240
|
+
return {
|
|
1241
|
+
"ExpressionBody": {
|
|
1242
|
+
"inputTypes": _encode_inputs(
|
|
1243
|
+
[(n, tp) for n, _, tp in defn.input_types],
|
|
1244
|
+
(*here, "inputTypes"),
|
|
1245
|
+
"ExpressionBody",
|
|
1246
|
+
),
|
|
1247
|
+
"outputType": encode_type(defn.output_type, (*here, "outputType")),
|
|
1248
|
+
"body": encode_value(defn.body, (*here, "body")),
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
def _encode_native_hint(hint: v.NativeHint) -> Json:
|
|
1254
|
+
if isinstance(hint, v.PlatformSpecific):
|
|
1255
|
+
return _wrap("PlatformSpecific", {"platform": hint.platform})
|
|
1256
|
+
return _wrap(type(hint).__name__, {})
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
def _decode_native_hint(j: object, loc: Location) -> v.NativeHint:
|
|
1260
|
+
tag, body, here = _node(j, loc, "NativeHint", NATIVE_HINT_NODES)
|
|
1261
|
+
match tag:
|
|
1262
|
+
case "Arithmetic":
|
|
1263
|
+
return v.Arithmetic()
|
|
1264
|
+
case "Comparison":
|
|
1265
|
+
return v.Comparison()
|
|
1266
|
+
case "StringOp":
|
|
1267
|
+
return v.StringOp()
|
|
1268
|
+
case "CollectionOp":
|
|
1269
|
+
return v.CollectionOp()
|
|
1270
|
+
case _:
|
|
1271
|
+
return v.PlatformSpecific(
|
|
1272
|
+
_expect_str(body["platform"], (*here, "platform"), tag)
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
def _encode_externals(bindings: list[v.ExternalBinding], loc: Location) -> list[Json]:
|
|
1277
|
+
if not bindings:
|
|
1278
|
+
raise fail(
|
|
1279
|
+
d.INVALID_JSON,
|
|
1280
|
+
"ExternalBody",
|
|
1281
|
+
loc,
|
|
1282
|
+
"an external body needs at least one platform binding",
|
|
1283
|
+
)
|
|
1284
|
+
seen: set[str] = set()
|
|
1285
|
+
result: list[Json] = []
|
|
1286
|
+
for i, binding in enumerate(bindings):
|
|
1287
|
+
if binding.target_platform in seen:
|
|
1288
|
+
raise fail(
|
|
1289
|
+
d.DUPLICATE_MEMBER,
|
|
1290
|
+
"ExternalBinding",
|
|
1291
|
+
(*loc, i, "targetPlatform"),
|
|
1292
|
+
f"target platform {binding.target_platform!r} is bound more than once",
|
|
1293
|
+
)
|
|
1294
|
+
seen.add(binding.target_platform)
|
|
1295
|
+
result.append(
|
|
1296
|
+
{
|
|
1297
|
+
"targetPlatform": binding.target_platform,
|
|
1298
|
+
"externalName": binding.external_name,
|
|
1299
|
+
}
|
|
1300
|
+
)
|
|
1301
|
+
return result
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
def _decode_externals(j: object, loc: Location) -> list[v.ExternalBinding]:
|
|
1305
|
+
items = _expect_list(j, loc, "ExternalBody")
|
|
1306
|
+
if not items:
|
|
1307
|
+
raise fail(
|
|
1308
|
+
d.INVALID_JSON,
|
|
1309
|
+
"ExternalBody",
|
|
1310
|
+
loc,
|
|
1311
|
+
"an external body needs at least one platform binding",
|
|
1312
|
+
)
|
|
1313
|
+
bindings: list[v.ExternalBinding] = []
|
|
1314
|
+
for i, item in enumerate(items):
|
|
1315
|
+
here = (*loc, i)
|
|
1316
|
+
obj = _expect_object(item, here, "ExternalBinding")
|
|
1317
|
+
_check_members(
|
|
1318
|
+
obj, _m("targetPlatform externalName", ""), here, "ExternalBinding"
|
|
1319
|
+
)
|
|
1320
|
+
bindings.append(
|
|
1321
|
+
v.ExternalBinding(
|
|
1322
|
+
_expect_str(
|
|
1323
|
+
obj["targetPlatform"], (*here, "targetPlatform"), "ExternalBinding"
|
|
1324
|
+
),
|
|
1325
|
+
_expect_str(
|
|
1326
|
+
obj["externalName"], (*here, "externalName"), "ExternalBinding"
|
|
1327
|
+
),
|
|
1328
|
+
)
|
|
1329
|
+
)
|
|
1330
|
+
# Re-encode to apply the same uniqueness rule as the writer.
|
|
1331
|
+
_encode_externals(bindings, loc)
|
|
1332
|
+
return bindings
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
def encode_value_definition(defn: v.ValueDefinitionBody, loc: Location = ()) -> Json:
|
|
1336
|
+
"""Encode a value definition as ``{"<Kind>Body": {...}}``.
|
|
1337
|
+
|
|
1338
|
+
Raises:
|
|
1339
|
+
DiagnosticError: With ``no_v4_spelling`` when an expression-body input
|
|
1340
|
+
carries attributes other than its declared type, ``duplicate_member``
|
|
1341
|
+
when an external body binds a platform twice, and ``invalid_json``
|
|
1342
|
+
when it binds none.
|
|
1343
|
+
"""
|
|
1344
|
+
match defn:
|
|
1345
|
+
case v.Definition():
|
|
1346
|
+
return _encode_expression_body(defn, loc)
|
|
1347
|
+
case v.NativeBody(inputs, output, info):
|
|
1348
|
+
here = (*loc, "NativeBody")
|
|
1349
|
+
return _wrap(
|
|
1350
|
+
"NativeBody",
|
|
1351
|
+
{
|
|
1352
|
+
"inputTypes": _encode_inputs(
|
|
1353
|
+
inputs, (*here, "inputTypes"), "NativeBody"
|
|
1354
|
+
),
|
|
1355
|
+
"outputType": encode_type(output, (*here, "outputType")),
|
|
1356
|
+
"nativeInfo": {"hint": _encode_native_hint(info.hint)},
|
|
1357
|
+
},
|
|
1358
|
+
)
|
|
1359
|
+
case v.ExternalBody(inputs, output, externals, fallback):
|
|
1360
|
+
here = (*loc, "ExternalBody")
|
|
1361
|
+
members: dict[str, Json] = {
|
|
1362
|
+
"inputTypes": _encode_inputs(
|
|
1363
|
+
inputs, (*here, "inputTypes"), "ExternalBody"
|
|
1364
|
+
),
|
|
1365
|
+
"outputType": encode_type(output, (*here, "outputType")),
|
|
1366
|
+
"externals": _encode_externals(externals, (*here, "externals")),
|
|
1367
|
+
}
|
|
1368
|
+
if fallback is not None:
|
|
1369
|
+
members["body"] = encode_value(fallback, (*here, "body"))
|
|
1370
|
+
return _wrap("ExternalBody", members)
|
|
1371
|
+
case v.IncompleteBody(inputs, incompleteness, output, partial):
|
|
1372
|
+
here = (*loc, "IncompleteBody")
|
|
1373
|
+
members = {
|
|
1374
|
+
"inputTypes": _encode_inputs(
|
|
1375
|
+
inputs, (*here, "inputTypes"), "IncompleteBody"
|
|
1376
|
+
)
|
|
1377
|
+
}
|
|
1378
|
+
if output is not None:
|
|
1379
|
+
members["outputType"] = encode_type(output, (*here, "outputType"))
|
|
1380
|
+
members["incompleteness"] = encode_incompleteness(
|
|
1381
|
+
incompleteness, (*here, "incompleteness")
|
|
1382
|
+
)
|
|
1383
|
+
if partial is not None:
|
|
1384
|
+
members["partialBody"] = encode_value(partial, (*here, "partialBody"))
|
|
1385
|
+
return _wrap("IncompleteBody", members)
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
def decode_value_definition(j: object, loc: Location = ()) -> v.ValueDefinitionBody:
|
|
1389
|
+
"""Decode an expression, native, external or incomplete body."""
|
|
1390
|
+
tag, body, here = _node(j, loc, "ValueDefinition", VALUE_DEFINITION_NODES)
|
|
1391
|
+
inputs = _unkeyed(
|
|
1392
|
+
body["inputTypes"],
|
|
1393
|
+
decode_name,
|
|
1394
|
+
decode_type,
|
|
1395
|
+
(*here, "inputTypes"),
|
|
1396
|
+
"inputTypes",
|
|
1397
|
+
)
|
|
1398
|
+
|
|
1399
|
+
def optional_type(key: str) -> t.Type | None:
|
|
1400
|
+
return decode_type(body[key], (*here, key)) if key in body else None
|
|
1401
|
+
|
|
1402
|
+
def optional_value(key: str) -> v.Value | None:
|
|
1403
|
+
return decode_value(body[key], (*here, key)) if key in body else None
|
|
1404
|
+
|
|
1405
|
+
match tag:
|
|
1406
|
+
case "ExpressionBody":
|
|
1407
|
+
return v.Definition(
|
|
1408
|
+
[(n, v.ValueAttributes(inferred_type=tp), tp) for n, tp in inputs],
|
|
1409
|
+
decode_type(body["outputType"], (*here, "outputType")),
|
|
1410
|
+
decode_value(body["body"], (*here, "body")),
|
|
1411
|
+
)
|
|
1412
|
+
case "NativeBody":
|
|
1413
|
+
info_loc = (*here, "nativeInfo")
|
|
1414
|
+
info = _expect_object(body["nativeInfo"], info_loc, "NativeInfo")
|
|
1415
|
+
_check_members(info, _m("hint", ""), info_loc, "NativeInfo")
|
|
1416
|
+
return v.NativeBody(
|
|
1417
|
+
inputs,
|
|
1418
|
+
decode_type(body["outputType"], (*here, "outputType")),
|
|
1419
|
+
v.NativeInfo(_decode_native_hint(info["hint"], (*info_loc, "hint"))),
|
|
1420
|
+
)
|
|
1421
|
+
case "ExternalBody":
|
|
1422
|
+
return v.ExternalBody(
|
|
1423
|
+
inputs,
|
|
1424
|
+
decode_type(body["outputType"], (*here, "outputType")),
|
|
1425
|
+
_decode_externals(body["externals"], (*here, "externals")),
|
|
1426
|
+
optional_value("body"),
|
|
1427
|
+
)
|
|
1428
|
+
case _:
|
|
1429
|
+
return v.IncompleteBody(
|
|
1430
|
+
inputs,
|
|
1431
|
+
decode_incompleteness(
|
|
1432
|
+
body["incompleteness"], (*here, "incompleteness")
|
|
1433
|
+
),
|
|
1434
|
+
optional_type("outputType"),
|
|
1435
|
+
optional_value("partialBody"),
|
|
1436
|
+
)
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
def encode_value_specification(spec: v.Specification, loc: Location = ()) -> Json:
|
|
1440
|
+
return _with_annotations(
|
|
1441
|
+
spec.annotations,
|
|
1442
|
+
loc,
|
|
1443
|
+
{
|
|
1444
|
+
"inputs": _encode_inputs(
|
|
1445
|
+
spec.inputs, (*loc, "inputs"), "ValueSpecification"
|
|
1446
|
+
),
|
|
1447
|
+
"output": encode_type(spec.output, (*loc, "output")),
|
|
1448
|
+
},
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
def decode_value_specification(j: object, loc: Location = ()) -> v.Specification:
|
|
1453
|
+
obj = _expect_object(j, loc, "ValueSpecification")
|
|
1454
|
+
_check_members(obj, _m("inputs output", "annotations"), loc, "ValueSpecification")
|
|
1455
|
+
return v.Specification(
|
|
1456
|
+
_unkeyed(obj["inputs"], decode_name, decode_type, (*loc, "inputs"), "inputs"),
|
|
1457
|
+
decode_type(obj["output"], (*loc, "output")),
|
|
1458
|
+
_decode_annotations(obj, loc),
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
|
|
1462
|
+
# --- Annotations ---
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def encode_annotation(item: ann.Annotation, loc: Location = ()) -> Json:
|
|
1466
|
+
"""Encode an annotation in the form it was written in."""
|
|
1467
|
+
match item:
|
|
1468
|
+
case ann.CompactAnnotation(name, text):
|
|
1469
|
+
fqn = encode_fqname(name, loc)
|
|
1470
|
+
return fqn if text is None else f"{fqn}:{text}"
|
|
1471
|
+
case ann.StructuredAnnotation(name, arguments):
|
|
1472
|
+
members: dict[str, Json] = {"name": encode_fqname(name, loc)}
|
|
1473
|
+
if arguments:
|
|
1474
|
+
members["arguments"] = [
|
|
1475
|
+
_encode_annotation_argument(x, (*loc, "arguments", i))
|
|
1476
|
+
for i, x in enumerate(arguments)
|
|
1477
|
+
]
|
|
1478
|
+
return members
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
def _encode_annotation_argument(arg: ann.AnnotationArgument, loc: Location) -> Json:
|
|
1482
|
+
match arg:
|
|
1483
|
+
case ann.PositionalArgument(value):
|
|
1484
|
+
return encode_value(value, loc)
|
|
1485
|
+
case ann.NamedArgument(name, value):
|
|
1486
|
+
return {
|
|
1487
|
+
"name": encode_name(name, loc),
|
|
1488
|
+
"value": encode_value(value, (*loc, "value")),
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
def decode_annotation(j: object, loc: Location = ()) -> ann.Annotation:
|
|
1493
|
+
"""Decode a compact string or a structured annotation object.
|
|
1494
|
+
|
|
1495
|
+
In the compact form the free text starts after the first colon after ``#``.
|
|
1496
|
+
"""
|
|
1497
|
+
if isinstance(j, str):
|
|
1498
|
+
hash_at = j.find("#")
|
|
1499
|
+
colon_at = j.find(":", hash_at) if hash_at >= 0 else -1
|
|
1500
|
+
if colon_at < 0:
|
|
1501
|
+
return ann.CompactAnnotation(decode_fqname(j, loc))
|
|
1502
|
+
return ann.CompactAnnotation(
|
|
1503
|
+
decode_fqname(j[:colon_at], loc), j[colon_at + 1 :]
|
|
1504
|
+
)
|
|
1505
|
+
obj = _expect_object(j, loc, "Annotation")
|
|
1506
|
+
_check_members(obj, _m("name", "arguments"), loc, "Annotation")
|
|
1507
|
+
arguments = (
|
|
1508
|
+
_decode_list(
|
|
1509
|
+
_decode_annotation_argument,
|
|
1510
|
+
obj["arguments"],
|
|
1511
|
+
(*loc, "arguments"),
|
|
1512
|
+
"Annotation",
|
|
1513
|
+
)
|
|
1514
|
+
if "arguments" in obj
|
|
1515
|
+
else []
|
|
1516
|
+
)
|
|
1517
|
+
return ann.StructuredAnnotation(decode_fqname(obj["name"], loc), arguments)
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
def _decode_annotation_argument(j: object, loc: Location) -> ann.AnnotationArgument:
|
|
1521
|
+
if isinstance(j, dict) and set(j) == {"name", "value"}:
|
|
1522
|
+
return ann.NamedArgument(
|
|
1523
|
+
decode_name(j["name"], (*loc, "name")),
|
|
1524
|
+
decode_value(j["value"], (*loc, "value")),
|
|
1525
|
+
)
|
|
1526
|
+
return ann.PositionalArgument(decode_value(j, loc))
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
def _with_annotations(
|
|
1530
|
+
annotations: list[ann.Annotation], loc: Location, members: dict[str, Json]
|
|
1531
|
+
) -> dict[str, Json]:
|
|
1532
|
+
"""Put a non-empty ``annotations`` member first, as the kit writes it."""
|
|
1533
|
+
if not annotations:
|
|
1534
|
+
return members
|
|
1535
|
+
encoded = [
|
|
1536
|
+
encode_annotation(x, (*loc, "annotations", i))
|
|
1537
|
+
for i, x in enumerate(annotations)
|
|
1538
|
+
]
|
|
1539
|
+
return {"annotations": encoded, **members}
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
def _decode_annotations(obj: dict[str, object], loc: Location) -> list[ann.Annotation]:
|
|
1543
|
+
if "annotations" not in obj:
|
|
1544
|
+
return []
|
|
1545
|
+
return _decode_list(
|
|
1546
|
+
decode_annotation, obj["annotations"], (*loc, "annotations"), "annotations"
|
|
1547
|
+
)
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
# --- Type specifications and definitions ---
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
def _encode_params(params: list[Name], loc: Location) -> list[Json]:
|
|
1554
|
+
return [encode_name(p, loc) for p in params]
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
def _decode_params(j: object, loc: Location) -> list[Name]:
|
|
1558
|
+
return _decode_list(decode_name, j, loc, "typeParams")
|
|
1559
|
+
|
|
1560
|
+
|
|
1561
|
+
def _encode_constructors(ctors: ts.Constructors, loc: Location) -> Json:
|
|
1562
|
+
def encode_args(args: list[tuple[Name, t.Type]], here: Location) -> Json:
|
|
1563
|
+
return [
|
|
1564
|
+
[encode_name(n, here), encode_type(tp, (*here, i))]
|
|
1565
|
+
for i, (n, tp) in enumerate(args)
|
|
1566
|
+
]
|
|
1567
|
+
|
|
1568
|
+
return _keyed(ctors, _name_key, encode_args, (*loc, "constructors"), "Constructors")
|
|
1569
|
+
|
|
1570
|
+
|
|
1571
|
+
def _decode_constructors(j: object, loc: Location) -> ts.Constructors:
|
|
1572
|
+
def decode_args(j: object, here: Location) -> list[tuple[Name, t.Type]]:
|
|
1573
|
+
args: list[tuple[Name, t.Type]] = []
|
|
1574
|
+
for i, pair in enumerate(_expect_list(j, here, "Constructor")):
|
|
1575
|
+
items = _expect_list(pair, (*here, i), "ConstructorArg")
|
|
1576
|
+
if len(items) != 2:
|
|
1577
|
+
raise fail(
|
|
1578
|
+
d.INVALID_JSON,
|
|
1579
|
+
"ConstructorArg",
|
|
1580
|
+
(*here, i),
|
|
1581
|
+
"a constructor argument is [name, type]",
|
|
1582
|
+
)
|
|
1583
|
+
args.append(
|
|
1584
|
+
(decode_name(items[0], (*here, i)), decode_type(items[1], (*here, i)))
|
|
1585
|
+
)
|
|
1586
|
+
return args
|
|
1587
|
+
|
|
1588
|
+
return dict(_unkeyed(j, decode_name, decode_args, loc, "Constructors"))
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
def encode_type_specification(spec: ts.Specification, loc: Location = ()) -> Json:
|
|
1592
|
+
tag = type(spec).__name__
|
|
1593
|
+
encoded = _encode_type_specification(spec, loc)
|
|
1594
|
+
return _wrap(tag, _with_annotations(spec.annotations, (*loc, tag), encoded[tag]))
|
|
1595
|
+
|
|
1596
|
+
|
|
1597
|
+
def _encode_type_specification(spec: ts.Specification, loc: Location) -> Json:
|
|
1598
|
+
params = _encode_params(spec.type_params, loc)
|
|
1599
|
+
match spec:
|
|
1600
|
+
case ts.TypeAliasSpecification(_, tpe):
|
|
1601
|
+
here = (*loc, "TypeAliasSpecification")
|
|
1602
|
+
return _wrap(
|
|
1603
|
+
"TypeAliasSpecification",
|
|
1604
|
+
{"typeParams": params, "typeExp": encode_type(tpe, (*here, "typeExp"))},
|
|
1605
|
+
)
|
|
1606
|
+
case ts.OpaqueTypeSpecification(_):
|
|
1607
|
+
return _wrap("OpaqueTypeSpecification", {"typeParams": params})
|
|
1608
|
+
case ts.CustomTypeSpecification(_, ctors):
|
|
1609
|
+
here = (*loc, "CustomTypeSpecification")
|
|
1610
|
+
return _wrap(
|
|
1611
|
+
"CustomTypeSpecification",
|
|
1612
|
+
{
|
|
1613
|
+
"typeParams": params,
|
|
1614
|
+
"constructors": _encode_constructors(ctors, here),
|
|
1615
|
+
},
|
|
1616
|
+
)
|
|
1617
|
+
case ts.DerivedTypeSpecification(_, details):
|
|
1618
|
+
here = (*loc, "DerivedTypeSpecification")
|
|
1619
|
+
return _wrap(
|
|
1620
|
+
"DerivedTypeSpecification",
|
|
1621
|
+
{
|
|
1622
|
+
"typeParams": params,
|
|
1623
|
+
"baseType": encode_type(details.base_type, (*here, "baseType")),
|
|
1624
|
+
"fromBaseType": encode_fqname(details.from_base_type, here),
|
|
1625
|
+
"toBaseType": encode_fqname(details.to_base_type, here),
|
|
1626
|
+
},
|
|
1627
|
+
)
|
|
1628
|
+
|
|
1629
|
+
|
|
1630
|
+
def decode_type_specification(j: object, loc: Location = ()) -> ts.Specification:
|
|
1631
|
+
tag, body, here = _node(j, loc, "TypeSpecification", TYPE_SPECIFICATION_NODES)
|
|
1632
|
+
params = (
|
|
1633
|
+
_decode_params(body["typeParams"], (*here, "typeParams"))
|
|
1634
|
+
if "typeParams" in body
|
|
1635
|
+
else []
|
|
1636
|
+
)
|
|
1637
|
+
annotations = _decode_annotations(body, here)
|
|
1638
|
+
match tag:
|
|
1639
|
+
case "TypeAliasSpecification":
|
|
1640
|
+
return ts.TypeAliasSpecification(
|
|
1641
|
+
params, decode_type(body["typeExp"], (*here, "typeExp")), annotations
|
|
1642
|
+
)
|
|
1643
|
+
case "OpaqueTypeSpecification":
|
|
1644
|
+
return ts.OpaqueTypeSpecification(params, annotations)
|
|
1645
|
+
case "CustomTypeSpecification":
|
|
1646
|
+
return ts.CustomTypeSpecification(
|
|
1647
|
+
params,
|
|
1648
|
+
_decode_constructors(body["constructors"], (*here, "constructors")),
|
|
1649
|
+
annotations,
|
|
1650
|
+
)
|
|
1651
|
+
case _:
|
|
1652
|
+
return ts.DerivedTypeSpecification(
|
|
1653
|
+
params,
|
|
1654
|
+
ts.DerivedTypeSpecificationDetails(
|
|
1655
|
+
decode_type(body["baseType"], (*here, "baseType")),
|
|
1656
|
+
decode_fqname(body["fromBaseType"], here),
|
|
1657
|
+
decode_fqname(body["toBaseType"], here),
|
|
1658
|
+
),
|
|
1659
|
+
annotations,
|
|
1660
|
+
)
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
def encode_type_definition(defn: td.Definition, loc: Location = ()) -> Json:
|
|
1664
|
+
params = _encode_params(defn.type_params, loc)
|
|
1665
|
+
match defn:
|
|
1666
|
+
case td.TypeAliasDefinition(_, tpe):
|
|
1667
|
+
here = (*loc, "TypeAliasDefinition")
|
|
1668
|
+
return _wrap(
|
|
1669
|
+
"TypeAliasDefinition",
|
|
1670
|
+
{"typeParams": params, "typeExp": encode_type(tpe, (*here, "typeExp"))},
|
|
1671
|
+
)
|
|
1672
|
+
case td.CustomTypeDefinition(_, ctors):
|
|
1673
|
+
here = (*loc, "CustomTypeDefinition")
|
|
1674
|
+
return _wrap(
|
|
1675
|
+
"CustomTypeDefinition",
|
|
1676
|
+
{
|
|
1677
|
+
"typeParams": params,
|
|
1678
|
+
"access": "Public" if isinstance(ctors, Public) else "Private",
|
|
1679
|
+
"constructors": _encode_constructors(ctors.value, here),
|
|
1680
|
+
},
|
|
1681
|
+
)
|
|
1682
|
+
case td.IncompleteTypeDefinition(_, incompleteness, partial):
|
|
1683
|
+
here = (*loc, "IncompleteTypeDefinition")
|
|
1684
|
+
members: dict[str, Json] = {
|
|
1685
|
+
"typeParams": params,
|
|
1686
|
+
"incompleteness": encode_incompleteness(
|
|
1687
|
+
incompleteness, (*here, "incompleteness")
|
|
1688
|
+
),
|
|
1689
|
+
}
|
|
1690
|
+
if partial is not None:
|
|
1691
|
+
members["partialTypeExp"] = encode_type(
|
|
1692
|
+
partial, (*here, "partialTypeExp")
|
|
1693
|
+
)
|
|
1694
|
+
return _wrap("IncompleteTypeDefinition", members)
|
|
1695
|
+
|
|
1696
|
+
|
|
1697
|
+
def decode_type_definition(j: object, loc: Location = ()) -> td.Definition:
|
|
1698
|
+
tag, body, here = _node(j, loc, "TypeDefinition", TYPE_DEFINITION_NODES)
|
|
1699
|
+
params = _decode_params(body["typeParams"], (*here, "typeParams"))
|
|
1700
|
+
if tag == "TypeAliasDefinition":
|
|
1701
|
+
return td.TypeAliasDefinition(
|
|
1702
|
+
params, decode_type(body["typeExp"], (*here, "typeExp"))
|
|
1703
|
+
)
|
|
1704
|
+
if tag == "IncompleteTypeDefinition":
|
|
1705
|
+
return td.IncompleteTypeDefinition(
|
|
1706
|
+
params,
|
|
1707
|
+
decode_incompleteness(body["incompleteness"], (*here, "incompleteness")),
|
|
1708
|
+
decode_type(body["partialTypeExp"], (*here, "partialTypeExp"))
|
|
1709
|
+
if "partialTypeExp" in body
|
|
1710
|
+
else None,
|
|
1711
|
+
)
|
|
1712
|
+
access = body.get("access", "Public")
|
|
1713
|
+
if access not in ACCESS_TAGS:
|
|
1714
|
+
raise fail(
|
|
1715
|
+
d.UNKNOWN_NODE, "Access", (*here, "access"), f"unknown access {access!r}"
|
|
1716
|
+
)
|
|
1717
|
+
ctors = _decode_constructors(body["constructors"], (*here, "constructors"))
|
|
1718
|
+
wrapped: AccessControlled[ts.Constructors] = (
|
|
1719
|
+
Public(ctors) if access == "Public" else Private(ctors)
|
|
1720
|
+
)
|
|
1721
|
+
return td.CustomTypeDefinition(params, wrapped)
|
|
1722
|
+
|
|
1723
|
+
|
|
1724
|
+
# --- Documented and access-controlled wrappers ---
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
def _encode_documented[A](
|
|
1728
|
+
encode: Callable[[A, Location], Json],
|
|
1729
|
+
) -> Callable[[Documented[A], Location], Json]:
|
|
1730
|
+
def run(item: Documented[A], loc: Location) -> Json:
|
|
1731
|
+
if item.doc is None:
|
|
1732
|
+
return encode(item.value, loc)
|
|
1733
|
+
return {"doc": item.doc, "value": encode(item.value, (*loc, "value"))}
|
|
1734
|
+
|
|
1735
|
+
return run
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
def _decode_documented[A](
|
|
1739
|
+
decode: Callable[[object, Location], A],
|
|
1740
|
+
) -> Callable[[object, Location], Documented[A]]:
|
|
1741
|
+
def run(j: object, loc: Location) -> Documented[A]:
|
|
1742
|
+
if isinstance(j, dict) and set(j) == {"doc", "value"}:
|
|
1743
|
+
text = _expect_str(j["doc"], (*loc, "doc"), "Documented")
|
|
1744
|
+
return Documented(text, decode(j["value"], (*loc, "value")))
|
|
1745
|
+
return Documented(None, decode(j, loc))
|
|
1746
|
+
|
|
1747
|
+
return run
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
def _encode_access[A](
|
|
1751
|
+
encode: Callable[[A, Location], Json],
|
|
1752
|
+
) -> Callable[[AccessControlled[A], Location], Json]:
|
|
1753
|
+
def run(item: AccessControlled[A], loc: Location) -> Json:
|
|
1754
|
+
tag = "Public" if isinstance(item, Public) else "Private"
|
|
1755
|
+
return {tag: encode(item.value, (*loc, tag))}
|
|
1756
|
+
|
|
1757
|
+
return run
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
def _decode_access[A](
|
|
1761
|
+
decode: Callable[[object, Location], A],
|
|
1762
|
+
) -> Callable[[object, Location], AccessControlled[A]]:
|
|
1763
|
+
def run(j: object, loc: Location) -> AccessControlled[A]:
|
|
1764
|
+
tag, payload = _single(j, loc, "AccessControlled", ACCESS_TAGS)
|
|
1765
|
+
value = decode(payload, (*loc, tag))
|
|
1766
|
+
return Public(value) if tag == "Public" else Private(value)
|
|
1767
|
+
|
|
1768
|
+
return run
|
|
1769
|
+
|
|
1770
|
+
|
|
1771
|
+
def _single(
|
|
1772
|
+
j: object, loc: Location, construct: str, tags: dict[str, Any]
|
|
1773
|
+
) -> tuple[str, object]:
|
|
1774
|
+
obj = _expect_object(j, loc, construct)
|
|
1775
|
+
if len(obj) != 1 or next(iter(obj)) not in tags:
|
|
1776
|
+
raise fail(
|
|
1777
|
+
d.UNKNOWN_NODE,
|
|
1778
|
+
construct,
|
|
1779
|
+
loc,
|
|
1780
|
+
f"expected one of {sorted(tags)} for {construct}, found {sorted(obj)}",
|
|
1781
|
+
)
|
|
1782
|
+
((tag, payload),) = obj.items()
|
|
1783
|
+
return tag, payload
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
# --- Modules and packages ---
|
|
1787
|
+
|
|
1788
|
+
_enc_type_spec = _encode_documented(encode_type_specification)
|
|
1789
|
+
_dec_type_spec = _decode_documented(decode_type_specification)
|
|
1790
|
+
_enc_value_spec = _encode_documented(encode_value_specification)
|
|
1791
|
+
_dec_value_spec = _decode_documented(decode_value_specification)
|
|
1792
|
+
_enc_type_def = _encode_access(_encode_documented(encode_type_definition))
|
|
1793
|
+
_dec_type_def = _decode_access(_decode_documented(decode_type_definition))
|
|
1794
|
+
_enc_value_def = _encode_access(_encode_documented(encode_value_definition))
|
|
1795
|
+
_dec_value_def = _decode_access(_decode_documented(decode_value_definition))
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
def _with_doc(members: dict[str, Json], text: str | None) -> dict[str, Json]:
|
|
1799
|
+
return {**members, "doc": text} if text is not None else members
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
def _decode_doc(obj: dict[str, object], loc: Location) -> str | None:
|
|
1803
|
+
if "doc" not in obj:
|
|
1804
|
+
return None
|
|
1805
|
+
return _expect_str(obj["doc"], (*loc, "doc"), "doc")
|
|
1806
|
+
|
|
1807
|
+
|
|
1808
|
+
def encode_module_specification(spec: module.Specification, loc: Location = ()) -> Json:
|
|
1809
|
+
return _with_annotations(
|
|
1810
|
+
spec.annotations,
|
|
1811
|
+
loc,
|
|
1812
|
+
_with_doc(
|
|
1813
|
+
{
|
|
1814
|
+
"types": _keyed(
|
|
1815
|
+
spec.types, _name_key, _enc_type_spec, (*loc, "types"), "types"
|
|
1816
|
+
),
|
|
1817
|
+
"values": _keyed(
|
|
1818
|
+
spec.values, _name_key, _enc_value_spec, (*loc, "values"), "values"
|
|
1819
|
+
),
|
|
1820
|
+
},
|
|
1821
|
+
spec.doc,
|
|
1822
|
+
),
|
|
1823
|
+
)
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def decode_module_specification(j: object, loc: Location = ()) -> module.Specification:
|
|
1827
|
+
obj = _expect_object(j, loc, "ModuleSpecification")
|
|
1828
|
+
_check_members(
|
|
1829
|
+
obj, _m("", "annotations types values doc"), loc, "ModuleSpecification"
|
|
1830
|
+
)
|
|
1831
|
+
return module.Specification(
|
|
1832
|
+
types=dict(
|
|
1833
|
+
_unkeyed(
|
|
1834
|
+
obj.get("types", {}),
|
|
1835
|
+
decode_name,
|
|
1836
|
+
_dec_type_spec,
|
|
1837
|
+
(*loc, "types"),
|
|
1838
|
+
"types",
|
|
1839
|
+
)
|
|
1840
|
+
),
|
|
1841
|
+
values=dict(
|
|
1842
|
+
_unkeyed(
|
|
1843
|
+
obj.get("values", {}),
|
|
1844
|
+
decode_name,
|
|
1845
|
+
_dec_value_spec,
|
|
1846
|
+
(*loc, "values"),
|
|
1847
|
+
"values",
|
|
1848
|
+
)
|
|
1849
|
+
),
|
|
1850
|
+
doc=_decode_doc(obj, loc),
|
|
1851
|
+
annotations=_decode_annotations(obj, loc),
|
|
1852
|
+
)
|
|
1853
|
+
|
|
1854
|
+
|
|
1855
|
+
def encode_module_definition(defn: module.Definition, loc: Location = ()) -> Json:
|
|
1856
|
+
return _with_doc(
|
|
1857
|
+
{
|
|
1858
|
+
"types": _keyed(
|
|
1859
|
+
defn.types, _name_key, _enc_type_def, (*loc, "types"), "types"
|
|
1860
|
+
),
|
|
1861
|
+
"values": _keyed(
|
|
1862
|
+
defn.values, _name_key, _enc_value_def, (*loc, "values"), "values"
|
|
1863
|
+
),
|
|
1864
|
+
},
|
|
1865
|
+
defn.doc,
|
|
1866
|
+
)
|
|
1867
|
+
|
|
1868
|
+
|
|
1869
|
+
def decode_module_definition(j: object, loc: Location = ()) -> module.Definition:
|
|
1870
|
+
obj = _expect_object(j, loc, "ModuleDefinition")
|
|
1871
|
+
_check_members(obj, _m("", "types values doc"), loc, "ModuleDefinition")
|
|
1872
|
+
return module.Definition(
|
|
1873
|
+
types=dict(
|
|
1874
|
+
_unkeyed(
|
|
1875
|
+
obj.get("types", {}),
|
|
1876
|
+
decode_name,
|
|
1877
|
+
_dec_type_def,
|
|
1878
|
+
(*loc, "types"),
|
|
1879
|
+
"types",
|
|
1880
|
+
)
|
|
1881
|
+
),
|
|
1882
|
+
values=dict(
|
|
1883
|
+
_unkeyed(
|
|
1884
|
+
obj.get("values", {}),
|
|
1885
|
+
decode_name,
|
|
1886
|
+
_dec_value_def,
|
|
1887
|
+
(*loc, "values"),
|
|
1888
|
+
"values",
|
|
1889
|
+
)
|
|
1890
|
+
),
|
|
1891
|
+
doc=_decode_doc(obj, loc),
|
|
1892
|
+
)
|
|
1893
|
+
|
|
1894
|
+
|
|
1895
|
+
_enc_module_def = _encode_access(encode_module_definition)
|
|
1896
|
+
_dec_module_def = _decode_access(decode_module_definition)
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
def encode_package_specification(
|
|
1900
|
+
spec: package.Specification, loc: Location = ()
|
|
1901
|
+
) -> Json:
|
|
1902
|
+
return {
|
|
1903
|
+
"modules": _keyed(
|
|
1904
|
+
spec.modules,
|
|
1905
|
+
_path_key,
|
|
1906
|
+
encode_module_specification,
|
|
1907
|
+
(*loc, "modules"),
|
|
1908
|
+
"modules",
|
|
1909
|
+
)
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
|
|
1913
|
+
def decode_package_specification(
|
|
1914
|
+
j: object, loc: Location = ()
|
|
1915
|
+
) -> package.Specification:
|
|
1916
|
+
obj = _expect_object(j, loc, "PackageSpecification")
|
|
1917
|
+
_check_members(obj, _m("", "modules"), loc, "PackageSpecification")
|
|
1918
|
+
return package.Specification(
|
|
1919
|
+
dict(
|
|
1920
|
+
_unkeyed(
|
|
1921
|
+
obj.get("modules", {}),
|
|
1922
|
+
decode_path,
|
|
1923
|
+
decode_module_specification,
|
|
1924
|
+
(*loc, "modules"),
|
|
1925
|
+
"modules",
|
|
1926
|
+
)
|
|
1927
|
+
)
|
|
1928
|
+
)
|
|
1929
|
+
|
|
1930
|
+
|
|
1931
|
+
def encode_package_definition(defn: package.Definition, loc: Location = ()) -> Json:
|
|
1932
|
+
return {
|
|
1933
|
+
"modules": _keyed(
|
|
1934
|
+
defn.modules, _path_key, _enc_module_def, (*loc, "modules"), "modules"
|
|
1935
|
+
)
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
def decode_package_definition(j: object, loc: Location = ()) -> package.Definition:
|
|
1940
|
+
obj = _expect_object(j, loc, "PackageDefinition")
|
|
1941
|
+
_check_members(obj, _m("", "modules"), loc, "PackageDefinition")
|
|
1942
|
+
return package.Definition(
|
|
1943
|
+
dict(
|
|
1944
|
+
_unkeyed(
|
|
1945
|
+
obj.get("modules", {}),
|
|
1946
|
+
decode_path,
|
|
1947
|
+
_dec_module_def,
|
|
1948
|
+
(*loc, "modules"),
|
|
1949
|
+
"modules",
|
|
1950
|
+
)
|
|
1951
|
+
)
|
|
1952
|
+
)
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
# --- Distributions ---
|
|
1956
|
+
|
|
1957
|
+
|
|
1958
|
+
def _encode_entry_point(entry: EntryPoint, loc: Location) -> Json:
|
|
1959
|
+
out: dict[str, Json] = {
|
|
1960
|
+
"target": encode_fqname(entry.target, loc),
|
|
1961
|
+
"kind": ENTRY_POINT_KINDS[entry.kind],
|
|
1962
|
+
}
|
|
1963
|
+
if entry.doc is not None:
|
|
1964
|
+
out["doc"] = entry.doc.value
|
|
1965
|
+
return out
|
|
1966
|
+
|
|
1967
|
+
|
|
1968
|
+
def _decode_entry_point(j: object, loc: Location) -> EntryPoint:
|
|
1969
|
+
obj = _expect_object(j, loc, "EntryPoint")
|
|
1970
|
+
_check_members(obj, _m("target kind", "doc"), loc, "EntryPoint")
|
|
1971
|
+
kinds = {text: kind for kind, text in ENTRY_POINT_KINDS.items()}
|
|
1972
|
+
kind_text = obj["kind"]
|
|
1973
|
+
if not isinstance(kind_text, str) or kind_text not in kinds:
|
|
1974
|
+
raise fail(
|
|
1975
|
+
d.UNKNOWN_NODE,
|
|
1976
|
+
"EntryPointKind",
|
|
1977
|
+
(*loc, "kind"),
|
|
1978
|
+
f"unknown entry point kind {kind_text!r}",
|
|
1979
|
+
)
|
|
1980
|
+
doc_text = _decode_doc(obj, loc)
|
|
1981
|
+
return EntryPoint(
|
|
1982
|
+
decode_fqname(obj["target"], (*loc, "target")),
|
|
1983
|
+
kinds[kind_text],
|
|
1984
|
+
Documented(None, doc_text) if doc_text is not None else None,
|
|
1985
|
+
)
|
|
1986
|
+
|
|
1987
|
+
|
|
1988
|
+
def encode_distribution(dist: Distribution, loc: Location = ()) -> Json:
|
|
1989
|
+
"""Encode a distribution wrapped by its kind.
|
|
1990
|
+
|
|
1991
|
+
The package release version is not written; the v4 profile has no member for it.
|
|
1992
|
+
"""
|
|
1993
|
+
name = encode_path(dist.package.name, loc)
|
|
1994
|
+
match dist:
|
|
1995
|
+
case LibraryDistribution(_, definition, deps):
|
|
1996
|
+
here = (*loc, "Library")
|
|
1997
|
+
return {
|
|
1998
|
+
"Library": {
|
|
1999
|
+
"packageName": name,
|
|
2000
|
+
"dependencies": _keyed(
|
|
2001
|
+
deps,
|
|
2002
|
+
_path_key,
|
|
2003
|
+
encode_package_specification,
|
|
2004
|
+
(*here, "dependencies"),
|
|
2005
|
+
"dependencies",
|
|
2006
|
+
),
|
|
2007
|
+
"def": encode_package_definition(definition, (*here, "def")),
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
case SpecsDistribution(_, specification, deps):
|
|
2011
|
+
here = (*loc, "Specs")
|
|
2012
|
+
return {
|
|
2013
|
+
"Specs": {
|
|
2014
|
+
"packageName": name,
|
|
2015
|
+
"dependencies": _keyed(
|
|
2016
|
+
deps,
|
|
2017
|
+
_path_key,
|
|
2018
|
+
encode_package_specification,
|
|
2019
|
+
(*here, "dependencies"),
|
|
2020
|
+
"dependencies",
|
|
2021
|
+
),
|
|
2022
|
+
"spec": encode_package_specification(
|
|
2023
|
+
specification, (*here, "spec")
|
|
2024
|
+
),
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
case ApplicationDistribution(_, definition, deps, entry_points):
|
|
2028
|
+
here = (*loc, "Application")
|
|
2029
|
+
return {
|
|
2030
|
+
"Application": {
|
|
2031
|
+
"packageName": name,
|
|
2032
|
+
"dependencies": _keyed(
|
|
2033
|
+
deps,
|
|
2034
|
+
_path_key,
|
|
2035
|
+
encode_package_definition,
|
|
2036
|
+
(*here, "dependencies"),
|
|
2037
|
+
"dependencies",
|
|
2038
|
+
),
|
|
2039
|
+
"def": encode_package_definition(definition, (*here, "def")),
|
|
2040
|
+
"entryPoints": _keyed(
|
|
2041
|
+
entry_points,
|
|
2042
|
+
_name_key,
|
|
2043
|
+
_encode_entry_point,
|
|
2044
|
+
(*here, "entryPoints"),
|
|
2045
|
+
"entryPoints",
|
|
2046
|
+
),
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
|
|
2051
|
+
def decode_distribution(j: object, loc: Location = ()) -> Distribution:
|
|
2052
|
+
"""Decode a distribution wrapped by its kind; the release version is absent."""
|
|
2053
|
+
tag, body, here = _node(j, loc, "Distribution", DISTRIBUTION_NODES)
|
|
2054
|
+
info = PackageInfo(decode_path(body["packageName"], (*here, "packageName")))
|
|
2055
|
+
match tag:
|
|
2056
|
+
case "Library":
|
|
2057
|
+
return LibraryDistribution(
|
|
2058
|
+
info,
|
|
2059
|
+
decode_package_definition(body.get("def", {}), (*here, "def")),
|
|
2060
|
+
dict(
|
|
2061
|
+
_unkeyed(
|
|
2062
|
+
body.get("dependencies", {}),
|
|
2063
|
+
decode_path,
|
|
2064
|
+
decode_package_specification,
|
|
2065
|
+
(*here, "dependencies"),
|
|
2066
|
+
"dependencies",
|
|
2067
|
+
)
|
|
2068
|
+
),
|
|
2069
|
+
)
|
|
2070
|
+
case "Specs":
|
|
2071
|
+
return SpecsDistribution(
|
|
2072
|
+
info,
|
|
2073
|
+
decode_package_specification(body.get("spec", {}), (*here, "spec")),
|
|
2074
|
+
dict(
|
|
2075
|
+
_unkeyed(
|
|
2076
|
+
body.get("dependencies", {}),
|
|
2077
|
+
decode_path,
|
|
2078
|
+
decode_package_specification,
|
|
2079
|
+
(*here, "dependencies"),
|
|
2080
|
+
"dependencies",
|
|
2081
|
+
)
|
|
2082
|
+
),
|
|
2083
|
+
)
|
|
2084
|
+
case _:
|
|
2085
|
+
return ApplicationDistribution(
|
|
2086
|
+
info,
|
|
2087
|
+
decode_package_definition(body.get("def", {}), (*here, "def")),
|
|
2088
|
+
dict(
|
|
2089
|
+
_unkeyed(
|
|
2090
|
+
body.get("dependencies", {}),
|
|
2091
|
+
decode_path,
|
|
2092
|
+
decode_package_definition,
|
|
2093
|
+
(*here, "dependencies"),
|
|
2094
|
+
"dependencies",
|
|
2095
|
+
)
|
|
2096
|
+
),
|
|
2097
|
+
dict(
|
|
2098
|
+
_unkeyed(
|
|
2099
|
+
body["entryPoints"],
|
|
2100
|
+
decode_name,
|
|
2101
|
+
_decode_entry_point,
|
|
2102
|
+
(*here, "entryPoints"),
|
|
2103
|
+
"entryPoints",
|
|
2104
|
+
)
|
|
2105
|
+
),
|
|
2106
|
+
)
|
|
2107
|
+
|
|
2108
|
+
|
|
2109
|
+
def encode_versioned_distribution(dist: Distribution) -> dict[str, Json]:
|
|
2110
|
+
"""Encode ``{"formatVersion": 4, "distribution": ...}``, version first.
|
|
2111
|
+
|
|
2112
|
+
Raises:
|
|
2113
|
+
DiagnosticError: When the distribution uses a construct with no draft
|
|
2114
|
+
spelling.
|
|
2115
|
+
"""
|
|
2116
|
+
return {
|
|
2117
|
+
"formatVersion": write_format_version(V4),
|
|
2118
|
+
"distribution": encode_distribution(dist, ("distribution",)),
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
|
|
2122
|
+
def decode_versioned_distribution(document: str | object) -> Distribution:
|
|
2123
|
+
"""Decode a draft v4 document; the version must be in ``[4.0.0,4.1.0)``.
|
|
2124
|
+
|
|
2125
|
+
Args:
|
|
2126
|
+
document: JSON text or an already-decoded JSON value.
|
|
2127
|
+
|
|
2128
|
+
Raises:
|
|
2129
|
+
DiagnosticError: On a format-version or decoding failure.
|
|
2130
|
+
"""
|
|
2131
|
+
_, root = read_document(document, V4_TABLE)
|
|
2132
|
+
_check_members(root, _m("formatVersion distribution", ""), (), "document")
|
|
2133
|
+
return decode_distribution(root["distribution"], ("distribution",))
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
__all__ = [
|
|
2137
|
+
"DRAFT_NOTICE",
|
|
2138
|
+
"FINOS_MORPHIR_PIN",
|
|
2139
|
+
"decode_distribution",
|
|
2140
|
+
"decode_fqname",
|
|
2141
|
+
"decode_hole_reason",
|
|
2142
|
+
"decode_incompleteness",
|
|
2143
|
+
"decode_literal",
|
|
2144
|
+
"decode_name",
|
|
2145
|
+
"decode_path",
|
|
2146
|
+
"decode_pattern",
|
|
2147
|
+
"decode_type",
|
|
2148
|
+
"decode_value",
|
|
2149
|
+
"decode_versioned_distribution",
|
|
2150
|
+
"encode_distribution",
|
|
2151
|
+
"encode_fqname",
|
|
2152
|
+
"encode_hole_reason",
|
|
2153
|
+
"encode_incompleteness",
|
|
2154
|
+
"encode_literal",
|
|
2155
|
+
"encode_name",
|
|
2156
|
+
"encode_path",
|
|
2157
|
+
"encode_pattern",
|
|
2158
|
+
"encode_type",
|
|
2159
|
+
"encode_value",
|
|
2160
|
+
"encode_versioned_distribution",
|
|
2161
|
+
]
|