expr(W):- W :-> level1 lchainedBy (symbolA("+") <: symbolA("-")). level1(W):- W :-> level0 lchainedBy (symbolA("*") <: symbolA("/")). level0(W):- W :-> fact lchainedBy (symbolA("mod") <: symbolA("//")).Představme si však situaci, kdy máme mnohem více úrovní priority nebo můžeme operátory přidávat jako v Prologu. V takovém případě jsme nuceni vytvářet stejně velké množství téměř identického kódu -- tomu se však lze velice snadno vyhnout pomocí generátoru, který z daného seznamu operátorů stejné precedence vygeneruje parser a ten aplikuje na vstup:
exprGen(Operators,P,W):- mapList(sfx(tokenA),Operators,OpList), selection(OpList,SepParser), W :-> P lchainedBy SepParser.Vytvoření parseru pro libovolný počet úrovní precedence je pak opravdu jednoduché:
expr(W):- foldR(sfx2(exprGen),fact(expr), [["+","-"],["*","/"],["//","mod"]], ExprParser), W :-> ExprParser.Nejdříve se zkonstruuje pomocí
foldR/4
parser, který bude
na jednotlivých úrovních precedence volat generátor.
Druhý parametr obsahuje parser entity s nejvyšší precedencí a třetí seznam, v jehož položkách jsou vždy operátory stejné precedence.
Tyto podseznamy jsou seřazeny s klesající precedencí. Na každé úrovni
se pak v průběhu rozkladu volá generátor:?- expr(s("1+10mod3//2*100+2*5//2")+L) L= [[]> 5] YesRozšíření o další úroveň priority nyní spočívá v pouhém přidání podseznamu do třetího parametru predikátu
foldR/4
.
Obdobným způsobem, jakým jsme v této kapitole vytvářely nové konstruktory,
by bylo možné pokračovat dál a to v oblasti arity operátorů a typů jejich
asociativity.
V knihovně kombinátorů parserů byl vytvořen obecný
parser výrazů obsahujících unární i binární operátory s libovolným
počtem priorit, u kterých je navíc možné specifikovat
typ jejich asociativity (a to xf
, yf
, fx
,
fy
, xfx
, xfy
, yfx
nebo yfy
).
Z uživatelského zápisu je nejdříve vygenerován parser ve formě
environmentu, jenž je následně použit pro rozklad vstupního textu.
Pro vyhodnocování v době rozkladu se navíc používá vyhodnocovač, který
je možné specifikovat v jednom z jeho parametrů (podobně jako tomu bylo
u chainL
v části ).
Alespoň pro představu si na závěr této kapitoly ukažme, jak by
vypadalo volání tohoto konstruktoru, pokud by jsme jej chtěli použít
pro analýzu podmnožiny jazyka Prolog:
?- s("saveTerm(File,Term):- | openFile(File,Stream,write), | write(Term), | closeFile(Stream,write)")+L | :-> | expression([[fx(":-",':-'),fx("?-",'?-')], | [xfx(":-",':-')], | [xfy(";",';')], | [xfy("->",'->')], | [xfy(",",',')], | [fy("not",'not')] | ], | factProlog(id)).Pro zkonstruování syntaktického stromu je zde použita místo vyhodnocovače identita
id/2
.
dvorka
2013-12-31