joetjen.net
EN DE
Datenformat

Grammatik

Das DXN-Textformat ist durch eine PEG-Grammatik in Aether definiert, der Grammatiksprache von Ichor. Der Parser von dextrin wird daraus erzeugt statt von Hand geschrieben.

Format 1.0

Pragmas und Tokens

Dies ist die Grammatik, aus der der Parser von dextrin erzeugt wird: priv/grammar/dxn.aether, wie mit dextrin 0.1.3 veröffentlicht, ohne Kommentare. Namen in Großbuchstaben sind Tokens, längste Übereinstimmung zuerst, bei Gleichstand gewinnt die frühere Deklaration; Namen in Kleinbuchstaben sind Regeln, die per geordneter Auswahl über den Tokens arbeiten.

dxn.aether
@grammar "dxn"
@root document
@skip TRIVIA

SPACE   := [ \t\r\n,]+
COMMENT := "#" (!"\n" .)* "\n"?
TRIVIA  := (SPACE | COMMENT)*

NIL   := "nil"
TRUE  := "true"
FALSE := "false"

EXPONENT := [eE] [+-]? DIGIT+
FLOAT    := ("-"? DIGIT+ (("." DIGIT+ EXPONENT?) | EXPONENT)) | "NaN" | "Infinity" | "-Infinity"
DECIMAL  := "-"? DIGIT+ ("." DIGIT+)? "M"
RATIONAL := "-"? DIGIT+ "/" DIGIT+
INTEGER  := "-"? DIGIT+

IDENT_START := [_\u{41}-\u{5A}\u{61}-\u{7A}\u{AA} … \u{30000}-\u{3134A}\u{31350}-\u{33479}]
IDENT_CONT  := IDENT_START | [\u{30}-\u{39}\u{41}-\u{5A}\u{5F}\u{61}-\u{7A} … \u{E0100}-\u{E01EF}\-?!]
IDENTIFIER  := IDENT_START IDENT_CONT* ("/" IDENT_START IDENT_CONT*)?

MAP_KEY := IDENTIFIER ":"
KEYWORD := ":" (IDENTIFIER | STRING)

AT_DISCARD := "@_"
AT         := "@"

ESCAPE := "\\" ("\"" | "\\" | "n" | "t" | "r" | "0" | "a" | "b" | "f" | "v" | ("x{" HEX+ "}"))
STRING := "\"" (ESCAPE | (![\"\\] .))* "\""
CHAR   := "?" (ESCAPE | "s" | (![ \t\r\n] .))

DATE_SIGIL    := "~D[" (!"]" .)* "]"
TIME_SIGIL    := "~T[" (!"]" .)* "]"
INSTANT_SIGIL := "~U[" (!"]" .)* "]"
REGEX_SIGIL   := "~r/" (("\\" .) | (!"/" .))* "/" [imsuxfr]*

Die Reihenfolge der Deklarationen leistet hier echte Arbeit. NIL, TRUE und FALSE stehen vor IDENTIFIER und gewinnen so den Gleichstand. MAP_KEY verschmilzt einen Namen mit seinem Doppelpunkt zu einem Token, damit name:value ohne Leerzeichen nie als Name mit folgendem Keyword :value gelesen wird. AT_DISCARD schlägt AT durch seine Länge, also ist @_ immer ein Verwerfen und nie ein eigenes Tag namens _.

Regeln

document := header? value TRIVIA?
header   := "@dxn" version:STRING

value   := discard* value_body
discard := AT_DISCARD value

value_body := nil | boolean | number | string | char | keyword | symbol
            | list | tuple | map_lit | struct_lit | set_lit
            | sigil | tag_form

nil     := NIL
boolean := TRUE | FALSE
number  := DECIMAL | RATIONAL | FLOAT | INTEGER
string  := STRING
char    := CHAR
symbol  := IDENTIFIER
keyword := KEYWORD

list    := "[" value* "]"
tuple   := "{" value* "}"
set_lit := AT "{" value* "}"

map_lit   := "%" "{" map_entry* "}"
map_entry := (short_key:MAP_KEY short_val:value) | (arrow_key:value "=>" arrow_val:value)

struct_lit        := "%" name:IDENTIFIER (keyed:struct_keyed | positional:struct_positional)
struct_keyed      := "{" map_entry* "}"
struct_positional := "[" value* "]"

sigil := DATE_SIGIL | TIME_SIGIL | INSTANT_SIGIL | REGEX_SIGIL

tag_form := AT name:IDENTIFIER value

Die Sigil-Tokens erfassen ihren Inhalt als Ganzes; Datum, Uhrzeit und reguläre Ausdrücke werden nach dem Parsen von den Parsern der Host-Sprache geprüft. Weil "@dxn" überall im Dokument ein längeres Token ist als AT, nicht nur am Anfang, kann @dxn nie ein eigenes Tag sein.

Wo die Implementierung abweicht

An einigen Stellen akzeptiert dextrin 0.1.3 mehr als die Spezifikation oder legt etwas fest, das sie offenlässt. Dies sind die portablen Entscheidungen:

Spezifikationdextrin 0.1.3Schreibweise
~U[…] ist nur UTC, mit Z am EndeAkzeptiert einen Offset und rechnet nach UTC umImmer Z; @datetime für Offsets
@datetime braucht einen Offset ungleich UTCAkzeptiert Z und +00:00 und schreibt den Wert als Zeitstempel zurück~U[…] für UTC
Die Kopfzeile nennt die FormatversionJede Versionszeichenkette wird ungeprüft akzeptiert@dxn "1.0"
Binäre Structs sind positionell, in Schema-ReihenfolgeOhne Schema wird ein Struct mit Schlüsseln in Quellreihenfolge geschrieben und verliert seine Namen: %Point{y: 2, x: 1} kommt als %Point[2 1] zurückPositionelle Form, oder mit geladenem Schema kodieren
Tag 29 verweist auf „das n-te markierte Element“Nummeriert markierte Elemente, wenn sie fertig dekodiert sind, innerste zuerst; die registrierte CBOR-Erweiterung nummeriert sie beim ÖffnenTeilen abschalten für Dateien, die andere CBOR-Werkzeuge lesen