yapyon 0.1.0__tar.gz
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.
- yapyon-0.1.0/CHANGELOG.md +307 -0
- yapyon-0.1.0/LICENSE +674 -0
- yapyon-0.1.0/MANIFEST.in +18 -0
- yapyon-0.1.0/PKG-INFO +306 -0
- yapyon-0.1.0/README.md +279 -0
- yapyon-0.1.0/docs/GRAMMAR.md +543 -0
- yapyon-0.1.0/docs/SPEC.md +818 -0
- yapyon-0.1.0/examples/gateway.ypy +56 -0
- yapyon-0.1.0/examples/registry.ypy +40 -0
- yapyon-0.1.0/pyproject.toml +48 -0
- yapyon-0.1.0/setup.cfg +4 -0
- yapyon-0.1.0/src/yapyon/__init__.py +58 -0
- yapyon-0.1.0/src/yapyon/__main__.py +39 -0
- yapyon-0.1.0/src/yapyon/_cli.py +46 -0
- yapyon-0.1.0/src/yapyon/_xid.py +1449 -0
- yapyon-0.1.0/src/yapyon/emitter.py +225 -0
- yapyon-0.1.0/src/yapyon/lexer.py +1131 -0
- yapyon-0.1.0/src/yapyon/loader.py +185 -0
- yapyon-0.1.0/src/yapyon/multimap.py +165 -0
- yapyon-0.1.0/src/yapyon/parser.py +529 -0
- yapyon-0.1.0/src/yapyon/record.py +59 -0
- yapyon-0.1.0/src/yapyon/resolver.py +628 -0
- yapyon-0.1.0/src/yapyon/serializers.py +248 -0
- yapyon-0.1.0/src/yapyon/template.py +161 -0
- yapyon-0.1.0/src/yapyon.egg-info/PKG-INFO +306 -0
- yapyon-0.1.0/src/yapyon.egg-info/SOURCES.txt +41 -0
- yapyon-0.1.0/src/yapyon.egg-info/dependency_links.txt +1 -0
- yapyon-0.1.0/src/yapyon.egg-info/requires.txt +5 -0
- yapyon-0.1.0/src/yapyon.egg-info/top_level.txt +1 -0
- yapyon-0.1.0/tests/test_cli.py +81 -0
- yapyon-0.1.0/tests/test_conformance.py +321 -0
- yapyon-0.1.0/tests/test_diagnostics.py +169 -0
- yapyon-0.1.0/tests/test_emitter.py +165 -0
- yapyon-0.1.0/tests/test_identifiers.py +146 -0
- yapyon-0.1.0/tests/test_lexer.py +520 -0
- yapyon-0.1.0/tests/test_loader.py +216 -0
- yapyon-0.1.0/tests/test_parser.py +369 -0
- yapyon-0.1.0/tests/test_record.py +195 -0
- yapyon-0.1.0/tests/test_ref_grammar.py +49 -0
- yapyon-0.1.0/tests/test_resolver.py +364 -0
- yapyon-0.1.0/tests/test_subscripts.py +598 -0
- yapyon-0.1.0/tools/check_ref_grammar.py +269 -0
- yapyon-0.1.0/tools/gen_xid_table.py +83 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to yapyon are recorded here. Section numbers refer to the
|
|
4
|
+
yapyon specification.
|
|
5
|
+
|
|
6
|
+
This project uses [PEP 440](https://peps.python.org/pep-0440/) versions.
|
|
7
|
+
`0.1.0` is the first release that is not a pre-release; every `0.1.0aN`
|
|
8
|
+
before it needed `pip install yapyon --pre` to be seen.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0.1.0 — 2026-09-29
|
|
13
|
+
|
|
14
|
+
The first release that is not an alpha, and the first `pip install yapyon`
|
|
15
|
+
finds without `--pre`.
|
|
16
|
+
|
|
17
|
+
This is the **self-contained tier**: typed literals, holes and splicing,
|
|
18
|
+
serializers, multimaps, and layering within a document — all resolvable from
|
|
19
|
+
the document's own bytes, with no ambient scope, no includes and no
|
|
20
|
+
environment at the format level. Consumer functions and file embedding are a
|
|
21
|
+
deliberate later tier, not a gap in this one.
|
|
22
|
+
|
|
23
|
+
**Breaking.** The two-tier limits change what a document between the tiers
|
|
24
|
+
does, and the identifier rule changes which keys are legal and which are the
|
|
25
|
+
same key. Both are carried here rather than left spread down the alpha line,
|
|
26
|
+
so if you installed `0.1.0a1`–`a4` with `--pre`, read the two entries below
|
|
27
|
+
before upgrading.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **§5.3's expansion limits now have two tiers, and passing the spec's
|
|
32
|
+
threshold is a `shiran` rather than an `akan`.** Resolution depth 32 and
|
|
33
|
+
rendered size 1 MiB were hard errors, which forced one implementation's
|
|
34
|
+
resource bound onto every consumer of the format. They are now the
|
|
35
|
+
specification's **warn tier**: passing one emits a warning and resolution
|
|
36
|
+
continues. Refusal moved to an implementation-chosen **ceiling**, which this
|
|
37
|
+
implementation sets at `4x` the warn tier — **depth 128, rendered size
|
|
38
|
+
4 MiB** — and which `loads`/`load` still move via `max_depth` / `max_size`.
|
|
39
|
+
Those two arguments keep their names and their meaning of *the point at
|
|
40
|
+
which loading fails*; what changed is that they now default above the
|
|
41
|
+
warning rather than sitting on it.
|
|
42
|
+
|
|
43
|
+
**This is a behaviour change.** A document between the two tiers — deeper
|
|
44
|
+
than 32, or rendering more than 1 MiB — previously raised `AkanError` and
|
|
45
|
+
now loads successfully while emitting a shiran to `warn=`. Code relying on
|
|
46
|
+
the old failure should pass `max_depth=32, max_size=1048576` to restore it
|
|
47
|
+
exactly.
|
|
48
|
+
|
|
49
|
+
The ceiling is deliberately finite: chained doubling is exponential, so a
|
|
50
|
+
resolver with no ceiling is a denial-of-service in anything parsing
|
|
51
|
+
untrusted configuration. The spec now declines to fix one ceiling for every
|
|
52
|
+
implementation while requiring that one exist and be documented.
|
|
53
|
+
|
|
54
|
+
**Unrelated and unchanged: a cycle or a missing target is still an `akan`**
|
|
55
|
+
at any depth. Those share §5.3 with the limits but are a different thing —
|
|
56
|
+
the document can never resolve, so the error belongs at the point of the
|
|
57
|
+
mistake.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
- **Identifiers are pinned to Unicode 14.0.0 and may not contain invisible
|
|
62
|
+
characters.** The identifier tables were the interpreter's, so a key with a
|
|
63
|
+
character from Unicode 15 or later loaded on Python 3.13 and was akan on
|
|
64
|
+
3.11. yapyon now ships a generated Unicode 14 table (`tools/gen_xid_table.py`)
|
|
65
|
+
and `UNICODE_VERSION` is `"14.0.0"` on every install. **Breaking:** keys and
|
|
66
|
+
hole names may no longer contain `Default_Ignorable_Code_Point` characters.
|
|
67
|
+
Those include the Hangul fillers, which made a key with no visible glyph
|
|
68
|
+
legal, variation selectors, the combining grapheme joiner, and, on Python
|
|
69
|
+
3.13+, ZWJ and ZWNJ.
|
|
70
|
+
- **Width variants and canonically equivalent spellings are one name.** A name
|
|
71
|
+
is folded, width variants to their standard form and then NFC, and two
|
|
72
|
+
names that fold alike are the same: `name: 1` loads as `{"name": 1}`,
|
|
73
|
+
`{name}` finds it, and `name` beside it is a duplicate key. Previously
|
|
74
|
+
`café` precomposed and decomposed were two keys that rendered identically.
|
|
75
|
+
No other NFKC merge applies (`fi` stays distinct from `fi`). **Breaking**
|
|
76
|
+
wherever a document relied on two such spellings being different keys.
|
|
77
|
+
- A keyword, string prefix or dunder name in another spelling (`True`,
|
|
78
|
+
`b"s"`, `__ROOT__`) is akan: reserved words have exactly one spelling.
|
|
79
|
+
|
|
80
|
+
- **SPEC §1's laws restated after review; no behaviour changes.** Law 2 now
|
|
81
|
+
says what the format does: a record computes nothing, and the full form's
|
|
82
|
+
only computation is named application of its declared encodings — the
|
|
83
|
+
serializers, which the old "never … transform" wording contradicted. "No
|
|
84
|
+
environment" moved from law 2 to law 3, its one home. Law 7 says "during
|
|
85
|
+
loading" rather than "at parse time", "never *merely* warned about" rather
|
|
86
|
+
than "never a warning", and names both kinds of `shiran`.
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- **Negative list indices, with Python's meaning.** `{xs[-1]}` is the last
|
|
91
|
+
element and `{xs[-3]}` the third from the end; `{xs[-0]}` is `{xs[0]}`,
|
|
92
|
+
because `-0` is the integer 0. They were reserved in SPEC §9 on the
|
|
93
|
+
reasoning "refuse now, allow later", which was never a ruling. Allowing them
|
|
94
|
+
is a widening: no document that loaded before changes meaning. Useful on a
|
|
95
|
+
multimap's list view, where `{mm.k[-1]}` is the last value filed under `k`
|
|
96
|
+
without knowing how many there are. `+` is still not an index sign.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- **A negative index reached through a reference was neither refused nor
|
|
101
|
+
range-checked.** With `i: -1`, `{xs[i]}` quietly took Python's meaning while
|
|
102
|
+
the written `{xs[-1]}` was refused as reserved; with `i: -5` on a short list
|
|
103
|
+
it raised a bare `IndexError` instead of an akan. A position outside the
|
|
104
|
+
list is now akan at either end, whichever way the index is spelled.
|
|
105
|
+
|
|
106
|
+
## 0.1.0a4 — 2026-09-19
|
|
107
|
+
|
|
108
|
+
A fix release. Two of the three entries below are bugs in `0.1.0a3` that could
|
|
109
|
+
corrupt or misreport data, and both were found by checking the library against
|
|
110
|
+
something it had not been written for rather than by a new test case. Nothing
|
|
111
|
+
here is breaking; code written against `0.1.0a3` keeps working.
|
|
112
|
+
|
|
113
|
+
### Fixed
|
|
114
|
+
|
|
115
|
+
- **`__AS_TOML__()` refuses a mapping nested in a sequence instead of
|
|
116
|
+
corrupting it.** A list of mappings — TOML's `[[table-array]]`, or an inline
|
|
117
|
+
table inside an array — reached `str()` and was written as a quoted string,
|
|
118
|
+
so `{"count": [{"name": "x"}]}` emitted `count = ["{'name': 'x'}"]` and read
|
|
119
|
+
back as text rather than a table. That is a silent reinterpretation, which
|
|
120
|
+
§5.1.2 forbids: where a format cannot carry a value faithfully the encoding
|
|
121
|
+
is akan, never approximate. The value now raises at the point of the
|
|
122
|
+
mistake. Emitting real `[[name]]` headers is a separate feature and is not
|
|
123
|
+
part of this change. Affects `0.1.0a3`.
|
|
124
|
+
- **A stalled serializer is reported as the cycle it is.** Two serializers
|
|
125
|
+
whose targets hold each other — `b1.m` encoding `b2` while `b2.m` encodes
|
|
126
|
+
`b1` — failed with *"a YString cannot be serialized"*, naming the wrong
|
|
127
|
+
problem at the wrong line and mentioning neither the cycle nor the other
|
|
128
|
+
reference. The encoder conflated *has no faithful encoding* (a multimap,
|
|
129
|
+
bytes — permanent) with *is not resolved yet* (still holds a y-string), so a
|
|
130
|
+
hard error pre-empted the resolver before it could detect the cycle. Affects
|
|
131
|
+
`0.1.0a3`.
|
|
132
|
+
- **The refusal to serialize a bare anchor says why.**
|
|
133
|
+
`{__PARENT__.__AS_JSON__()}` reported *"needs a value to encode"*, which is
|
|
134
|
+
false — an anchor names a value — and suggested the spelling the author had
|
|
135
|
+
already written. The refusal is correct (an anchor always contains the hole
|
|
136
|
+
naming it, so the reference is circular in every document); only the message
|
|
137
|
+
was wrong. Behaviour is unchanged.
|
|
138
|
+
|
|
139
|
+
### Documentation
|
|
140
|
+
|
|
141
|
+
- **The specification is published**, as two documents under `docs/`:
|
|
142
|
+
`GRAMMAR.md` (*are these bytes well-formed yapyon?* — self-contained,
|
|
143
|
+
normative for syntax) and `SPEC.md` (*what do well-formed bytes mean?*).
|
|
144
|
+
References run one way, so a parser can be written from the grammar alone.
|
|
145
|
+
Both ship in the sdist.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 0.1.0a3 — 2026-09-14
|
|
150
|
+
|
|
151
|
+
The first release to carry breaking changes. It also fixes a parse bug present
|
|
152
|
+
in **every** previously published version, described under *Fixed* below.
|
|
153
|
+
|
|
154
|
+
### Removed — breaking
|
|
155
|
+
|
|
156
|
+
- **`Template.fill(mapping)` is gone.** A `yt` no longer defers its holes to a
|
|
157
|
+
mapping supplied later by the consumer; it resolves them against the
|
|
158
|
+
document at parse time, by the ordinary scope rules, exactly as `y` does.
|
|
159
|
+
The difference between the two is now delivery alone — `y` joins the parts
|
|
160
|
+
into a `str`, `yt` hands over the literal text and the resolved values
|
|
161
|
+
separately, so that a consumer which knows the destination (shell, SQL,
|
|
162
|
+
HTML) can escape each value for it. `y` : `yt` :: f-string : t-string.
|
|
163
|
+
`Template.render()` produces the joined string where that is what is wanted;
|
|
164
|
+
there is deliberately no `__str__` that joins implicitly. (§6)
|
|
165
|
+
|
|
166
|
+
Consequences, all of them simplifying: `__ROOT__` has one meaning rather
|
|
167
|
+
than two, an unbound hole is an error at parse time where the author can see
|
|
168
|
+
it rather than at fill time, and a `yt` value is determined by the
|
|
169
|
+
document's own bytes.
|
|
170
|
+
|
|
171
|
+
*Migrating:* code that built a `Template` and called `.fill(data)` was using
|
|
172
|
+
late binding, which this release does not replace. Resolve the values in the
|
|
173
|
+
document, or join with `.render()`.
|
|
174
|
+
|
|
175
|
+
- **Dunder keys are akan.** `__foo__: "x"` loaded in `0.1.0a1` and `0.1.0a2`;
|
|
176
|
+
it is now an error. The `__dunder__` namespace belongs to yapyon rather than
|
|
177
|
+
to the document, which is what makes "every key of a mapping is addressable
|
|
178
|
+
from a hole" true — previously `__foo__: "x"` loaded while `y"{__foo__}"`
|
|
179
|
+
could never name it, and `__ROOT__` itself was a legal key. `__` and `___`
|
|
180
|
+
count as dunders too. (§7)
|
|
181
|
+
|
|
182
|
+
- **The shadowing warning no longer fires.** A local definition shadowing an
|
|
183
|
+
outer one used to emit a `shiran:`. Nothing about it was ambiguous — the
|
|
184
|
+
scope search is total and deterministic — and it fired on correct code,
|
|
185
|
+
since layered configuration is deliberate shadowing. The fix it suggested
|
|
186
|
+
(`{__ROOT__.x}`) resolved to a *different value*, which makes it a trap
|
|
187
|
+
rather than a warning. Anything reading warnings will see fewer.
|
|
188
|
+
|
|
189
|
+
### Fixed
|
|
190
|
+
|
|
191
|
+
- **`\"` inside a string now parses.** `x: "a\"b"` failed as *"dangling
|
|
192
|
+
backslash in string"* in `0.1.0a1` and `0.1.0a2`, so **no document could put
|
|
193
|
+
a double quote inside a double-quoted string** — though `\"` and `\'` have
|
|
194
|
+
been in the escape table since the first release. The fix is a scanning rule
|
|
195
|
+
(a backslash consumes the character it protects), so it applies to raw and
|
|
196
|
+
bytes literals equally, and it is strictly widening: nothing that parsed
|
|
197
|
+
before stops parsing.
|
|
198
|
+
|
|
199
|
+
- **`python -m yapyon.<stage>` no longer leaks a traceback** on a document
|
|
200
|
+
error. Running an already-imported module under `-m` executes it a second
|
|
201
|
+
time, giving the stage a different `AkanError` class from the one being
|
|
202
|
+
caught. The per-module entry points delegate through the package path now.
|
|
203
|
+
**`python -m yapyon <stage>` is the documented spelling.**
|
|
204
|
+
|
|
205
|
+
### Added
|
|
206
|
+
|
|
207
|
+
- **Subscripts — `a.b` is sugar for `a["b"]`.** The bracket is the general
|
|
208
|
+
form, and its contents yield a key: a quoted literal, or a reference whose
|
|
209
|
+
*value* is the key. So `{d[p]}` means "the value of `d` at the key stored in
|
|
210
|
+
`p`", and the inner reference resolves in the scope of the y-string's own
|
|
211
|
+
position. This also gives list indices (`{xs[0]}`) and quoted segments
|
|
212
|
+
(`{a["b.c"]}`), which were previously reserved. (§5.1)
|
|
213
|
+
|
|
214
|
+
- **Multimap traversal yields a list view.** `{mm.registry}` names *every*
|
|
215
|
+
value filed under `registry`, in order, as a list — 0 entries give an empty
|
|
216
|
+
list, N give N — and `{mm.registry[0]}` indexes it. Splicing the view into
|
|
217
|
+
text stays akan exactly as for any list. (§5.2)
|
|
218
|
+
|
|
219
|
+
- **Relative references `__PARENT__` and `__KEY__`.** A block can name its own
|
|
220
|
+
position, so the same text can appear in every block and resolve to that
|
|
221
|
+
block, and a block can be renamed or moved without editing its contents.
|
|
222
|
+
(§5.1.1)
|
|
223
|
+
|
|
224
|
+
store: "/cfg"
|
|
225
|
+
personas:
|
|
226
|
+
kimi:
|
|
227
|
+
id: y"{__PARENT__.__KEY__}"
|
|
228
|
+
prompt: y"{__ROOT__.store}/{__PARENT__.__KEY__}.md"
|
|
229
|
+
|
|
230
|
+
- **Named serializers — `__AS_JSON__()`, `__AS_TOML__()`, `__AS_YAML__()`.**
|
|
231
|
+
Splicing a container into text is barred because there is no one text form
|
|
232
|
+
for it; a named serializer is the author saying which they mean, in the
|
|
233
|
+
document, where a reader can see it. It stands to a container as `b64"..."`
|
|
234
|
+
does to bytes. A serializer is an *encoding*, not a function: no arguments,
|
|
235
|
+
no composition, and the set belongs to the format. (§5.1.2)
|
|
236
|
+
|
|
237
|
+
body: y"payload={cfg.__AS_JSON__()}"
|
|
238
|
+
|
|
239
|
+
All three writers are ours and the core remains dependency-free. **yapyon
|
|
240
|
+
does not prune** — dropping a key because its value is `None` or `""` would
|
|
241
|
+
be a silent reinterpretation — and because nothing is pruned, `None` reaches
|
|
242
|
+
the TOML writer, where it is akan rather than approximated, TOML having no
|
|
243
|
+
null.
|
|
244
|
+
|
|
245
|
+
- **An emitter — `yapyon.emit()` and `python -m yapyon record`** — rendering a
|
|
246
|
+
record back to canonical yapyon, with canonical spelling now normative
|
|
247
|
+
(§4.4). It reads the AST and never the loaded values, which is what keeps
|
|
248
|
+
`3.10` from coming back as `3.1` and a `b"..."` literal from being respelled
|
|
249
|
+
as base64. It is a **normalizer, not a round-tripper**: comments do not
|
|
250
|
+
survive.
|
|
251
|
+
|
|
252
|
+
- **Shirans are shown at the command line**, on stderr, and never change the
|
|
253
|
+
exit status — so a warning cannot corrupt `record`'s output when it is
|
|
254
|
+
redirected into a file, nor break a pipeline on a legal document.
|
|
255
|
+
|
|
256
|
+
- **`yapyon.UNICODE_VERSION`**, reporting the Unicode data version this
|
|
257
|
+
install's identifier tables come from (it follows the host interpreter).
|
|
258
|
+
This is the one place a document's meaning leans on something outside its
|
|
259
|
+
own bytes, so it is a machine-readable fact rather than a sentence in a
|
|
260
|
+
README that can go stale.
|
|
261
|
+
|
|
262
|
+
- **Diagnostics can name their source file.** Every stage accepts `source=`;
|
|
263
|
+
with one, an error reads `name:line:col`.
|
|
264
|
+
|
|
265
|
+
- **A `shiran:` for an avoidable bracket.** `{a["bar"]}` and `{a.bar}` mean
|
|
266
|
+
identically the same thing, so the warning is about spelling and taking the
|
|
267
|
+
fix changes nothing. It stays silent wherever the bracket was needed.
|
|
268
|
+
|
|
269
|
+
- A worked record in `examples/`.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 0.1.0a2 — 2026-09-13
|
|
274
|
+
|
|
275
|
+
### Added
|
|
276
|
+
|
|
277
|
+
- **The yapyon record (§12)** — a second, restricted form in which every leaf
|
|
278
|
+
has a literal value: no y-strings, nothing deferred. `loads_record` /
|
|
279
|
+
`load_record` run the pipeline without the resolver and refuse the y-family;
|
|
280
|
+
`is_record` tests a document by lexing. It gives a minimal implementation
|
|
281
|
+
something genuinely conforming to ship, and it is a safe mode by design
|
|
282
|
+
rather than an after-the-fact carve-out.
|
|
283
|
+
|
|
284
|
+
### Changed
|
|
285
|
+
|
|
286
|
+
- **Names are UAX #31 identifiers** — `XID_Start | "_"` then `XID_Continue`,
|
|
287
|
+
exact codepoints, no normalization. The previous test admitted `a²`, which
|
|
288
|
+
was a legal key no hole could name, and rejected a decomposed `é`. Keys and
|
|
289
|
+
hole segments now answer to one rule. (§7)
|
|
290
|
+
- **Multimaps compare by entry sequence**, and a multimap is never equal to a
|
|
291
|
+
mapping. Previously `OrderedMultimap` had no `__eq__` at all, so two loads
|
|
292
|
+
of the same bytes compared unequal. (§7.1)
|
|
293
|
+
- **Holes are recognised before escapes decode**, so `\x7b` is content rather
|
|
294
|
+
than a live hole — matching Python's f-string layering.
|
|
295
|
+
- Akan messages say what was expected, not only what was found.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 0.1.0a1 — 2026-09-11
|
|
300
|
+
|
|
301
|
+
First public release. A config/data format: Python-style typed literals with
|
|
302
|
+
YAML-like block structure, and parse-time splicing (y-strings) in place of
|
|
303
|
+
YAML's anchors.
|
|
304
|
+
|
|
305
|
+
- Lexer, parser, resolver and loader; `y` / `yb` / `yt` / `ry` splicing;
|
|
306
|
+
ordered multimaps; a conformance suite over the splice matrix and the
|
|
307
|
+
documented divergences from Python and YAML.
|