V řadě případů můžeme pomocí nich jednoduchými transformacemi
získat ekvivalentní efektivněji pracující parsery. Vzhledem
k rozsahu této problematiky a tématu této práce si zde ukážeme
pouze levou faktorizaci, se kterou se při konstrukci parserů
setkáme nejčastěji. Problémům s levou rekurzí se
budeme částečně věnovat v části
.
Programátorskou techniku levé faktorizace parserů si ukážeme na parseru jednoduchého aritmetického výrazu, který přijímá součin nebo podíl dvou čísel a jeho úkolem je vydat výsledek výrazu dle použité operace:
fooLeftFactorization(W):-
W :-> (double <&>> symbolA("*") <&>> double
<:
double <&>> symbolA("/") <&>> double) <@ evalLeftFact.
evalLeftFact(X>(Op>Y),R):- Op == '*', R is X*Y ; R is X/Y.
Parser je přímým přepisem úvodních vět, v nichž jsme specifikovali účel
jeho použití. Podívejme se na to, jak funguje -- například pro vstup
"3.14/2.72". Nejdříve je prvním parserem v alternativní kompozici
přijato číslo "3.14". Pak však tato první alternativa selže, protože
přijímá součin a nikoli podíl. Dojde tedy k navrácení a v druhé
alternativě se analyzuje číslo "3.14" zbytečně znovu. Zbytek vstupu je
již tentokrát zpracován bez problémů.
Příčinu tohoto typu navracení můžeme z parserů odstranit jejich levou faktorizací. Rozvětvíme výpočet až v místě, kde to má skutečný význam:
fooLeftFactorization(W):-
W :-> (double <&>>
(symbolA("*") <: symbolA("/"))
<&>> double) <@ evalLeftFact.
Tato varianta vydává stejné výsledky jako původní parser. Na rozdíl od
něj však nedochází při jejím výpočtu k navracení a vstup je zpracován
v lineárním čase. Faktorizace druhé části parseru sice nebyla nezbytně
nutná, ale jeho zápis zčitelnila.
V následujících kapitolách se budeme snažit vytvářet takové konstruktory parserů, které využívají levou faktorizaci již ve své definici, aby jí programátor nemusel věnovat pozornost v takové míře.
Na příklad z této části navážeme v další kapitole, v níž se budeme věnovat kromě jiného analýze aritmetických výrazů.
dvorka 2013-12-31