Themen
AND - Antra Notation for Data
AND ist das Datenformat fuer serialisierbare Antra-Werte in Antra.
Die Idee dahinter ist einfach: Wenn Antra Werte zwischen Prozessen schicken, auf Platte ablegen oder spaeter cachen soll, dann ist ein sprachnaeheres Format hilfreicher als JSON.
Dieses Kapitel ist die praezise v1-Spezifikation fuer die aktuelle Implementierung in Antra::AND. Es beschreibt den unterstuetzten Wertebereich, die beabsichtigte Lesart und die bewussten Grenzen von v1.
Grundregel: AND ist Antra#
Jedes AND-Dokument ist ein gueltiger Antra-Ausdruck, und als Antra
ausgewertet ergibt es genau den Wert, den AND.decode(...) liefert. AND
ist also eine Teilmenge der Antra-Literale — es gibt keine eigene Syntax, die
in Antra etwas anderes bedeuten wuerde.
Die Richtung ist bewusst einseitig: Nicht jedes Antra-Literal ist AND. Was
nur im Quelltext sinnvoll ist (Interpolation, doppelte Map-Keys, Variablen,
Ausdruecke), lehnt AND.decode(...) mit einem Fehler ab.
Status#
ANDist als Formatname festgelegt.- Ausgeschrieben bedeutet der Name
Antra Notation for Data. Antra::ANDstelltAND.tokenize(...),AND.tokenize(...).!,AND.encode(...),AND.encode(...).!,AND.decode(...)undAND.decode(...).!bereit.System.send(...)undSystem.receive(...)nutzenANDbereits als Protokoll auf Fork-/Process-Kanaelen.- Fuer rohe Zeilenprotokolle ohne AND stehen in
Antra::Systemzusaetzlichsend_text/receive_textbereit.
API#
Die aktuelle Moduloberflaeche ist klein gehalten:
import Antra::AND as AND
println(AND.tokenize("%User{name: \"Ada\"}").!)
println(AND.encode(%User{name: "Ada", tags: [:admin, :active]}).!)
println(AND.decode("%{name: \"Ada\", active: true}").!)AND.tokenize(...), AND.encode(...) und AND.decode(...) liefern Result-Werte. Mit dem postfixen .! (z.B. AND.decode(text).!) entpackst du direkt und wirfst bei Fehlern wie ueblich einen Interpreter-Fehler.
AND.tokenize(...) ist vor allem fuer Debugging und Parser-Entwicklung gedacht. AND.decode(...) nutzt diese Tokenisierung intern und baut daraus den eigentlichen Laufzeitwert.
Zielsetzung#
AND soll vier Dinge gleichzeitig leisten:
- sprachnahe Darstellung normaler Antra-Daten
- gute Lesbarkeit fuer Menschen beim Debuggen
- stabile Serialisierung fuer IPC und Persistenz
- klar abgegrenzte Fehlermeldungen fuer nicht serialisierbare Runtime-Werte
Wichtig ist die bewusste Abgrenzung: AND ist ein Datenformat, kein allgemeiner Dump der gesamten Runtime.
Nicht-Ziele#
AND v1 ist absichtlich nicht fuer diese Dinge gedacht:
- Funktionen, Lambdas oder Closures serialisieren
- laufende Prozesse serialisieren
- offene Datei- oder Stream-Handles serialisieren
- beliebige native Opaque Values verlustfrei durchreichen
- beliebige Interna der Runtime sichtbar machen
Wenn ein Wert eine laufzeitgebundene Ressource repraesentiert, soll AND.encode(...) dafuer einen klaren Fehler liefern statt eine halbgueltige Darstellung zu erzeugen.
Grundprinzip#
AND beschreibt nur Werte, die als reine Daten gelten.
Dazu gehoeren in v1:
- Zahlen
- rationale Zahlen
- Strings
true,false,undef- Keywords
- Arrays
- Maps
- Sets und Tagged Sets
- Ranges (
1..10,'a'..'z') - Tagged Arrays (auch in der Kurzform
30sec) - Tagged Maps
Nicht dazu gehoeren in v1:
ProcessFileHandleByteString, solange dafuer keine explizite Literalsyntax festgelegt wurde- Funktionen
- andere opake native Werte
Allgemeine Regeln#
Textformat#
ANDist ein textuelles Format.- Das Encoding ist UTF-8.
- Fuehrende und nachfolgende Leerzeichen um den Gesamtwert sind erlaubt.
- Leerzeichen, Zeilenumbrueche und Einrueckung sind ausserhalb von Strings frei fuer Lesbarkeit nutzbar.
#leitet ausserhalb von Strings einen Kommentar bis zum Zeilenende ein; er zaehlt wie Leerraum. Innerhalb eines Strings ist#normaler Text.AND.encode(...)schreibt keine Kommentare.
# Bibliotheken fuer das Spiel
%{raylib: "6.2", # Grafik
sdl2: "2.32.10"} # nur fuer Windows-BuildsEin Wert pro Dokument#
Ein AND-Dokument enthaelt genau einen Top-Level-Wert.
Gueltig sind also zum Beispiel:
42[1, 2, 3]%User{name: "Ada", age: 36}Nicht gueltig waere dagegen ein Text mit zwei getrennten Top-Level-Werten hintereinander.
Deterministische Ausgabe#
AND.encode(...) soll fuer denselben serialisierbaren Wert immer dieselbe Zeichenfolge erzeugen.
Das ist wichtig fuer:
- reproduzierbare Tests
- Debugging
- spaetere Vergleiche
- moegliche Caches oder Hashing
Wertebereich in v1#
Zahlen#
Zahlen stehen in ihrer Antra-Form, mit optionalem - davor:
0
-7
42
3.5
1_000_000
0xFF_FF
0b1010_0101
0o755
1e300
1.5e-3- Ganzzahlen dezimal, hexadezimal (
0x/0X), binaer (0b/0B) oder oktal (0o/0O). _darf zur Gliederung zwischen zwei Ziffern stehen (1_000,0xFF_FF,3.141_59), nicht am Anfang, am Ende oder doppelt.- Eine Dezimalzahl ist wie im Antra-Quelltext eine exakte rationale Zahl:
3.5ist 7/2,0.1ist 1/10 — keine Gleitkommazahl. - Zahlen mit Exponent (
1e300,6.02e23,1.5e-3,2E+10) sind ebenfalls exakt, solange sie in 64 Bit passen (1e3ist 1000,1.5e-3ist 3/2000), sonst Gleitkommazahlen.5emist wie in Antra der Tagged Scalar%em[5]. - Ganzzahlen jenseits von 64 Bit und Dezimalzahlen mit mehr als 18 Stellen vor oder nach dem Punkt werden, wie in Antra, zu Gleitkommazahlen.
AND.encode(...) schreibt Ganzzahlen immer dezimal ohne _, Brueche in
Bruchform (7/2) und Gleitkommazahlen bei Bedarf mit Exponent (1e+300;
Unendlich und NaN haben kein Literal und sind ein Fehler) — 0xFF und 1_000 sind Schreibweisen fuer Menschen, die
der Wert selbst nicht kennt.
Rationale Zahlen#
Rationale Zahlen stehen als Zaehler/Nenner — in Antra ist das die Division
zweier Ganzzahlen und ergibt denselben Bruch:
1/2
3/4
-7/9Die Ausgabe soll normalisiert sein. Das heisst insbesondere:
- kein gemeinsamer Teiler in Zaehler und Nenner
- negatives Vorzeichen hoechstens am Zaehler
- Nenner nie
0
Strings#
Strings stehen in doppelten Quotes mit denselben Escapes wie in Antra:
\n, \r, \t, \", \\, \$ und \u{HEX} (Unicode-Codepoint).
"hello"
"line\nbreak"
"quote: \"x\""
"\u{3b1} und \u{1F600}"
"Preis: \$name bleibt Text"Ein $ vor einem Namen oder vor { wuerde in Antra interpolieren
("hi $name"). Daten interpolieren nicht: AND verlangt dort \$ und lehnt
ein ungeschuetztes $name oder ${...} mit einem Fehler ab. Ein $, dem
etwas anderes folgt ("5 $", "$5"), ist auch in Antra nur Text. Andere
Backslash-Folgen (\d, \x) sind kein AND.
Einfach gequotete Strings sind wie in Antra roh: kein Escape ausser \' fuer
ein Apostroph, ein \ bleibt stehen.
'C:\Users\ada'
'it\'s'Der Encoder verwendet immer die doppelt gequotete Form mit vollem
Escaping (auch \$ vor Namen) — bewusst unabhaengig davon, wie viele Anfuehrungszeichen der Inhalt
enthaelt. AND ist ein Serialisierungsformat: es braucht eine einzige
kanonische, stabile String-Darstellung, die der AND-Parser exakt
zurueckliest (wie "..." in JSON). Das unterscheidet sich bewusst von der
menschlichen Wert-Anzeige (dd(...), geschachtelte Strings in println),
die die Quote-Art nach minimalem Escaping waehlt ('...' als Default, "..."
wenn der Inhalt Apostrophe enthaelt) — dort zaehlt Lesbarkeit, hier
Format-Stabilitaet.
Booleans und undef#
Diese Werte werden als nackte Literale geschrieben:
true
false
undefKeywords#
Keywords werden mit ihrem normalen Antra-Praefix geschrieben und folgen
denselben Zeichenregeln: nach dem : ein Buchstabe oder eines von
_ * + - / ? ! . $ % = < > &, danach zusaetzlich Ziffern (aber kein < oder
> mehr).
:ok
:error
:user/id
:ready?
:x-model
:<=AND.encode(...) lehnt ein Keyword ab, das sich so nicht schreiben laesst
(etwa eines mit Leerzeichen aus keyword("a b")).
AND behandelt Keywords bewusst als eigenen Datentyp und nicht nur als Strings.
Arrays#
Arrays verwenden die uebliche eckige Klammerform:
[]
[1, 2, 3]
["Ada", true, :admin]Verschachtelung ist erlaubt:
[1, [2, [3]], %{status: :ok}]Maps#
Maps schreiben sich wie im Antra-Quelltext mit %{...}. Damit ist jedes
AND-Dokument zugleich ein gueltiger Antra-Ausdruck — {...} waere in Antra
ein Block, keine Map:
%{}
%{name: "Ada", age: 36}
%{status: :ok, tags: [:admin, :active]}{name: "Ada"} ohne % ist kein AND (und kein Antra-Ausdruck);
AND.decode(...) lehnt es mit einem Hinweis auf %{...} ab.
Fuer v1 gilt folgende Regel fuer Map-Keys:
- erlaubt sind nur
string- undkeyword-Keys
Das ist keine Einschraenkung von AND, sondern Antras: Map-Keys sind dort
immer Strings oder Keywords. Ein Map-Literal nimmt nur diese Keys an
(%{1 => "a"} ist ein Parse-Fehler), und zur Laufzeit wird ein anderer Key
zu Text — m[1] und m["1"] sind derselbe Eintrag. Andere Key-Typen kann
AND erst abbilden, wenn Antra-Maps sie unterscheiden.
Pragmatisch bedeutet das fuer v1:
- String-Keys sind erlaubt
- Keyword-Keys sind erlaubt
- die Kurzschreibweise
name: "Ada"ist nur die lesbare Schreibform fuer einen Keyword-Key (fuer Namen aus Buchstaben, Ziffern und_, optional mit?oder!am Ende) - jedes andere Keyword schreibt sich
:x-model => 1, Strings"display name" => "Ada" "display name": "Ada"ist kein Key: wie in Antra waere unklar, ob ein String- oder ein Keyword-Key gemeint ist;AND.decodelehnt es mit einem Hinweis ab:role: :administ kein Key — in Antra ein Syntaxfehler- ein Key darf nur einmal vorkommen; Antra liesse den letzten gewinnen, AND lehnt doppelte Keys als Fehler ab
- andere Key-Typen fuehren bei
AND.encode(...)zu einem Fehler
Das haelt die erste Version lesbar und vermeidet unnoetige Parser-Komplexitaet.
Beispiele fuer erlaubte Keys:
%{role: :admin, :x-model => 1, "display_name" => "Ada", "full name" => "Ada L."}Beispiele fuer v1-nicht erlaubte Keys:
%{42: "answer"}%{%User{id: 1}: "nested"}Tagged Values#
Tagged Values sind fuer Antra wichtig und sollen deshalb nicht auf JSON-artige Ersatzstrukturen reduziert werden.
Tagged Maps#
Tagged Maps behalten ihre sichtbare Form:
%User{name: "Ada", age: 36}
%Point{x: 10, y: 20}Sets und Tagged Sets#
Sets schreiben sich wie in Antra mit spitzen Klammern; doppelte Elemente
fallen weg, AND.encode(...) sortiert die Elemente kanonisch:
%<1, 2, 3>
%Seen<:a, :b>Ranges#
Ranges stehen wie in Antra zwischen zwei Zahlen oder zwei einzelnen
Zeichen; ..< schliesst das Ende aus, ..* laesst es offen:
1..10
1..<10
-3..3
'a'..'z'
1..*Wie in Antra ist ein Array, das nur eine endliche Range enthaelt, die
Liste ihrer Werte: [1..3] ist [1, 2, 3], ['a'..'c'] ist
["a", "b", "c"]. Mit weiteren Elementen ([1..3, 5]) oder in einem Tagged
Array (%Ok[1..3]) bleibt die Range ein Wert.
AND.encode(...) schreibt Ranges in der inklusiven Form (1..<5 wird
1..4). Abgelehnt werden Ranges mit Schrittweite (range(0, 10, 2)) und
ein Array, das nur eine Range haelt — beides hat kein Antra-Literal.
Tagged Arrays#
Tagged Arrays behalten ebenfalls ihre eigene Form:
%Pair[1, 2]
%Some[42]
%Err["kaputt"]Eine Zahl mit direkt angehaengtem Namen ist wie in Antra die Kurzform eines Tagged Arrays mit einem Element (Tagged Scalar) — praktisch fuer Einheiten in Konfigurationen:
30sec
2.5kg
%{timeout: 30sec, size: 1.5mb}30sec ist %sec[30], -30sec ist %sec[-30]. Der Name beginnt mit einem
Buchstaben und folgt ohne Leerzeichen (30 sec ist kein AND).
AND.encode(...) schreibt immer die ausfuehrliche Form %sec[30].
Damit bleibt die Datenform fuer Leser sofort erkennbar und muss nicht ueber Hilfsfelder wie %{"tag" => ..., "data" => ...} rekonstruiert werden.
Nicht serialisierbare Werte#
Folgende Werte sollen in AND v1 einen klaren Fehler ausloesen:
#Process<...>
#FileHandle<...>
|x| -> x + 1Die Fehlermeldungen folgen aktuell dieser Richtung:
- der Fehler nennt
AND.encode - der Fehler nennt den nicht unterstuetzten Werttyp
- der Fehler versucht nicht, Ressourcen stillschweigend in Strings umzubiegen
Kanonische Ausgabe#
Damit AND als robustes Austauschformat taugt, soll die Encoder-Seite kanonisch ausgeben.
Das betrifft mindestens:
- Strings mit konsistenter Escape-Darstellung
- rationale Zahlen in normalisierter Form
- Maps in stabiler Reihenfolge
- keine bedeutungslose Formatierungsvarianz in der Standardausgabe
Die wichtigste offene Detailentscheidung fuer v1 ist die Reihenfolge von Map-Eintraegen.
Die Spezifikation fuer v1 legt deshalb fest:
AND.encode(...)gibt zuerst alle Keyword-Keys aus- danach folgen alle String-Keys
- innerhalb beider Gruppen wird lexikographisch nach der kanonischen Textdarstellung des Keys sortiert
Damit ist die Ausgabe nicht von der internen Map-Reihenfolge abhaengig.
Warum nicht JSON?#
JSON ist fuer Aussenwelt-Interop weiterhin sinnvoll, aber als Kernformat fuer Antra-Daten ist es zu schmal.
JSON verliert oder verbiegt wichtige Antra-Eigenschaften:
- Keywords werden zu Strings
- Tagged Values brauchen Hilfskonstruktionen
- Maps mit nichttrivialen Keys sind unklar
- rationale Zahlen haben keine natuerliche Darstellung
- die Lesart ist weniger sprachnah
AND soll genau diese semantischen Verluste vermeiden.
Warum nicht Bencode?#
bencode ist technisch einfach, aber fuer Antra zu arm als primaeres Sprachformat.
Es ist gut fuer Transporte, aber schlecht fuer:
- direkte Lesbarkeit
- sprachnahe Literale
- Tagged Values
- Keywords
- spaetere Erweiterbarkeit ohne Zusatzkonventionen
Beispielwerte#
Die folgenden Beispiele illustrieren den geplanten Umfang von v1.
423/4"hello":ok[1, 2, 3]%{name: "Ada", age: 36, admin: true}%User{name: "Ada", tags: [:admin, :active]}%Ok[%User{id: 1, name: "Ada"}][
%Task{id: 1, status: :open},
%Task{id: 2, status: :done}
]Beziehung zu Transport-APIs#
Die aktuelle API trennt weiterhin zwischen Textformat und Transportform:
AND.encode/decodearbeiten explizit auf TextSystem.send/receiveverwenden dieses Format intern fuer Fork-/Process-Kanaele
Ein spaeterer Ausbau koennte zusaetzlich niedrigere Raw- oder Binary-Transport-APIs anbieten, ohne die Rolle von AND als Datenformat zu aendern.
Empfohlener Start fuer v1#
Eine kleine erste Version ist besser als eine halb offene General-Loesung.
Deshalb ist dieser Zuschnitt fuer v1 absichtlich eng:
- Zahlen
- rationale Zahlen
- Strings
true,false,undef- Keywords
- Arrays
- Maps mit stabilen Key-Formen
- Sets und Tagged Sets
- Ranges
- Tagged Arrays (auch als Tagged Scalar
30sec) - Tagged Maps
Und explizit nicht:
- Funktionen
ProcessFileHandle- beliebige andere Opaque Values
Wenn spaeter weitere Werte dazukommen sollen, sollte das bewusst als v2 oder als klar dokumentierte Erweiterung passieren statt stillschweigend.
