Grammatik
Ichor liest .aether-Dateien mit einem handgeschriebenen Lexer und Reader. Die folgende Grammatik formuliert dieselbe Syntax in Aether selbst.
Pragmas und Tokens
Diese Grammatik wurde für diese Seiten aus Aether.Lexer und Aether.Reader in Ichor 0.3.0 geschrieben. Mit Ichor kompiliert, akzeptiert sie unter den 30 Aether-Grammatiken in den Repositories von Ichor, Cooper, dextrin, logos und xeger genau die Dateien, die Ichors eigener Reader akzeptiert, und lehnt dieselben fehlerhaften Köpfe, Namen, Pragmas und Schranken ab.
@grammar "aether"
@root grammar_file
@skip TRIVIA
COMMENT := ";" (!"\n" .)*
TRIVIA := (SPACE | COMMENT)*
UPPER_NAME := [A-Z_] [A-Z0-9_]*
LOWER_NAME := [a-z] [a-z0-9_\-]*
NUMBER := DIGIT+
ESCAPE := "\\" ("x" HEX HEX | "u{" HEX+ "}" | [nrt] | [^a-zA-Z0-9])
STRING := "\"" (ESCAPE | !"\"" .)* "\"" (("cs" | "i") ![a-zA-Z0-9_])?
POSIX := "[:" ("alpha" | "alnum" | "digit" | "space" | "hex") ":]"
CHAR_CLASS := "[" "^"? (POSIX | ESCAPE | !"]" .)* "]"
REGEX := "/" ("\\" . | "[" ("\\" . | !"]" .)* "]" | ![/\[] .)* "/"Kopf und Definitionen
grammar_file := TRIVIA? header definition* TRIVIA?
header := "@grammar" name:STRING "@root" root:LOWER_NAME pragma*
pragma := "@skip" UPPER_NAME | "@noskip" | "@case_insensitive" | "@engine" LOWER_NAME
definition := token_def | rule_def | keywords_def
token_def := UPPER_NAME ":=" choice refine?
rule_def := LOWER_NAME ":=" choice
keywords_def := "@keywords" UPPER_NAME "{" keyword ("," keyword)* "}"
keyword := STRING "->" UPPER_NAME
refine := "@refine" "(" STRING "," STRING ("," UPPER_NAME)* ")"sequence hält vor NAME := an. Diese Vorausschau ist der ganze Grund, warum eine Definition kein Abschlusszeichen braucht.
Ausdrücke
choice := sequence ("|" sequence)*
sequence := (!def_head item)+
def_head := (UPPER_NAME | LOWER_NAME) ":="
item := "~"? term
term := (LOWER_NAME ":")? postfix
postfix := ("&" | "!") primary | primary quantifier?
quantifier := "*" | "+" | "?" | "{" NUMBER ("," NUMBER?)? "}"
primary := STRING | CHAR_CLASS | REGEX | "." | UPPER_NAME | LOWER_NAME
| "(" choice ")" | layout | native
layout := ("@indent" | "@samecol") ("(" choice ")" | postfix)
native := "@native" "(" STRING "," STRING ("," LOWER_NAME)* ")" hint?
hint := "@hint" "(" hint_entry ("," hint_entry)* ")"
hint_entry := LOWER_NAME ":" (LOWER_NAME | "(" (LOWER_NAME ("," LOWER_NAME)*)? ")")Jenseits der Syntax
Ichors Reader prüft mehr, als diese Grammatik ausdrücken kann. Er lehnt außerdem ab:
- eine Zeichenklasse,
.oder/regex/in einer Regel und einen Regelnamen in einem Token; ~an jeder Stelle außer direkt vor einem bloßen Namen, in einem Token oder unter@noskip;- ein doppelt angegebenes Pragma,
@skipzusammen mit@noskipund eine@engineaußerpeg,lroderglr; - einen doppelt deklarierten Namen und
@keywordszusammen mit@refineauf einem Token; - in
@hintandere Schlüssel alsnullableundleadingund einenleading:-Namen, der keine Abhängigkeit ist.
Undefinierte Verweise, neu deklarierte vordefinierte Tokens und Linksrekursion werden geprüft, wenn die ganze Grammatik gelesen ist.
Wo die Implementierung abweicht
| Referenz | Ichor 0.3.0 | Schreibweise |
|---|---|---|
| Erfassungen gehören nur in Regeln | Eine Erfassung in einem Token-Rumpf wird beim Lesen akzeptiert und bringt dann den Matcher zum Absturz | Teile eines Tokens nie benennen |
Beide Backends führen jede @engine aus | Das interpretierte Backend führt nur peg aus; lr und glr brauchen eigene Runner | lr- und glr-Grammatiken vorab kompilieren |