joetjen.net
EN DE
Grammatiksprache

Ausdrücke

Der Rumpf jeder Definition ist ein Ausdruck aus derselben kleinen Menge von Operatoren. Was darin vorkommen darf, hängt davon ab, ob der Rumpf zu einem Token oder zu einer Regel gehört.

Ichor 0.3

Operatoren

FormBedeutung
a bFolge: a, dann b
a | bgeordnete Auswahl: a, und wenn das scheitert, b
a* a+ a?null oder mehr, eins oder mehr, null oder eins — immer gierig
a{3} a{3,} a{3,7}genau 3, mindestens 3, 3 bis 7
&a !anur wenn a folgt, nur wenn nicht — ohne etwas zu verbrauchen
name:aa unter name erfassen
~akein übersprungener Leerraum vor a
( a )Gruppierung

Die Auswahl bindet am schwächsten, dann die Folge; eine Erfassung, ~, & und ! gelten für den einen Term dahinter, ein Quantor für den Term davor. & und ! selbst können keinen Quantor tragen.

Tokens und Regeln

ElementIm TokenIn der Regel
"literal"jaja — wird zum anonymen Token
Token-Namejaja
Regel-Nameneinja
[class] . /regex/janein
name: ~ @indent @samecolneinja

Die Trennung folgt aus den zwei Stufen: Eine Regel sieht immer nur Tokens, nie Zeichen. Ein Muster, das eine Regel braucht, bekommt zuerst einen Token-Namen.

a := [a-z]+
3:6: rules may not contain an inline character class -- give it a named TOKEN instead
A := a
3:6: a token body may only reference other tokens, not rule "a"

Zeichenketten-Literale

Zeichenketten stehen in doppelten Anführungszeichen und dürfen jedes Zeichen enthalten. Die Escapes sind \n, \r, \t, \\, \", \[, \], \/, \xHH mit genau zwei Hex-Ziffern und \u{H…} mit einer oder mehr; jedes andere escapte Satzzeichen steht für sich selbst, \- und \* funktionieren also auch.

Ein Suffix entscheidet die Groß-/Kleinschreibung für ein Literal: "lit"i ignoriert sie immer, "lit"cs nie. Ohne Suffix folgt ein Literal @case_insensitive, falls die Grammatik es setzt. Zeichenklassen sind davon nicht betroffen.

@grammar "literal"
@root value
@case_insensitive
value := "true" | "false" | "null"cs
"TRUE"  → ok
"False" → ok
"null"  → ok
"NULL"  → 1:1: no token matches here

Zeichenklassen

[…] erkennt ein Zeichen aus einer Menge von Zeichen und a-z-Bereichen; [^…] ein Zeichen außerhalb davon, aber nie das Ende der Eingabe. Ein - direkt vor ] ist ein wörtlicher Bindestrich. In den Klammern stehen [:alpha:], [:alnum:], [:digit:], [:space:] und [:hex:] für die vordefinierten Tokens. . erkennt ein beliebiges Zeichen.

IDENT := [[:alpha:]_][[:alnum:]_]*
"_x1" → ok
"1x"  → 1:1: no token matches here

Wiederholung und Vorausschau

Wiederholung ist gierig und gibt nie zurück, was sie genommen hat. Auch innerhalb eines Tokens ist die Auswahl geordnet, wie dieses Farb-Token zeigt: #ff88 sind weder sechs Ziffern noch drei, also nimmt HEX{3} ff8, und die letzte 8 bleibt übrig.

COLOR := "#" (HEX{6} | HEX{3})
"#ff8800" → ok
"#f80"    → ok
"#ff88"   → 1:5: no token matches here

Vorausschau prüft, ohne zu verbrauchen. Hier passt IF auf if nur, wenn kein Buchstabe und keine Ziffer folgt; iffy bleibt also ein Bezeichner:

IF    := "if" !ALNUM
IDENT := ALPHA ALNUM*
stmt  := IF IDENT | IDENT
"if x" → ok
"iffy" → ok

Regex-Literale

In einem Token-Rumpf ist /muster/ eine Kurzschreibweise für den gleichwertigen Aether-Ausdruck. Unterstützt werden Zeichen, ., Klassen, |, Gruppen, alle Quantoren, (?=…) und (?!…) sowie \d \w \s \h mit ihren Negationen, die für DIGIT, ALNUM, SPACE und HEX stehen. Anker, Rückverweise und erfassende Gruppen haben in PEG keine Bedeutung und werden rundweg abgelehnt:

NUMBER := /\d+(\.\d+)?/
"3.14" → ok
"3."   → 1:2: no token matches here
A := /^a+$/
3:6: anchors (^) are not supported in /pattern/