joetjen.net
EN DE
Grammatiksprache

Grammatik

Ichor liest .aether-Dateien mit einem handgeschriebenen Lexer und Reader. Die folgende Grammatik formuliert dieselbe Syntax in Aether selbst.

Ichor 0.3

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.

aether.aether
@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, @skip zusammen mit @noskip und eine @engine außer peg, lr oder glr;
  • einen doppelt deklarierten Namen und @keywords zusammen mit @refine auf einem Token;
  • in @hint andere Schlüssel als nullable und leading und einen leading:-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

ReferenzIchor 0.3.0Schreibweise
Erfassungen gehören nur in RegelnEine Erfassung in einem Token-Rumpf wird beim Lesen akzeptiert und bringt dann den Matcher zum AbsturzTeile eines Tokens nie benennen
Beide Backends führen jede @engine ausDas interpretierte Backend führt nur peg aus; lr und glr brauchen eigene Runnerlr- und glr-Grammatiken vorab kompilieren