Nechť jsou v něm terminální symboly reprezentovány jako řetězce znaků a číslic:
terminal(W):- W :-> (fulfil(isDigitChar_) <&> terminal <:> fulfil(isDigitChar_) <&> epsilon).a neterminální symboly jako řetězce uzavřené mezi znaky
<
a >
:nonTerminal(W):- W :-> (angled(terminal) <@ mkNonTerminal).Pravidlo se skládá z hlavy a těla, které jsou odděleny tokenem
"::="
:rule(W):- W :-> ((whole nonTerminal <&>> (token(" ::= ") &> (ruleBody <@ choice)) <& symbol(".") ) <@ mkRule).V hlavě pravidla je vždy neterminál, zatímco tělo je tvořeno seznamem alternativ oddělených tokenem
"|"
:ruleBody(W):- W :-> (((ruleAlt <@ sequence) <& symbol("|")) <&> ruleBody <:> ruleAlt <&> epsilon <@ sequence).kde jsou jednotlivé alternativy tvořeny posloupností terminálů a neterminálů:
ruleAlt(W):- W :-> (minals <&> (symbol(" ") &> ruleAlt) <:> minals <&> epsilon <:> symbol(" ") <@ mkEpsilon). minals(W):- W :-> ((terminal<@ mkTerminal)<:>nonTerminal).Z pravidla v Backus-Naurově formě je vygenerován zdrojový text parseru. Ten lze následně zavést do interpretu a použít pro analýzu jazyka definovaného pravidlem. Přesněji řečeno, pravidlo je přeloženo na parser -- výsledek analýzy pravidla prováděné parserem není syntaktický strom, ale kód parseru pro jazyk přijímaný pravidlem. 2.1
Generátor rule
může být použit třeba takto:
?- s("<block> ::= begin <block> end| .")+L :-> rule. L= [s([])> (block:-token("begin")<&>block<&>token("end")<:>epsilon)] Yes
Tento oddíl věnovaný generátorům parserů ukončil část, která byla úvodem do světa konstruktorů parserů a měla spíše seznamovací charakter. Nicméně jsme v ní položili celkem solidní základ pro naši další práci, na němž bude možné dále stavět.
Konkrétně jsme se seznámili s filozofií techniky kombinátorového vytváření parserů, kterou je ruční konstruování parserů pomocí vhodných kombinátorů, mutátorů a generátorů. Konstruktory jsou definovány takovým způsobem, aby jejich vhodnou parametrizací bylo možné rychle vytvářet efektivně fungující parsery. Naším cílem je vytvořit sadu právě takových konstruktorů.
Dále jsme si ukázali, jak přínosné je použití uživatelsky definovaných operátorů, které nám umožnily přehledný zápis. Příklad v části věnované kombinátorům naznačil, že v určitém smyslu může zápis parseru nahrazovat specifikaci pomocí gramatiky.
Parsery je tak možné vytvářet stejně snadno, jako bychom konstruovali gramatiku. Její zápis je totiž v tomto případě zároveň funkčním parserem.
V následujících kapitolách se pustíme do vytváření dalších kombinátorů, mutátorů a generátorů pro typické úlohy syntaktické analýzy tak, jak to bylo nastíněno zde.
dvorka 2013-12-31