Grundlagen der Programmierung [1.5ex] Compiler: Parser (5C) [1.5

Werbung
Syntaktische Analyse (Parsen)
Grundlagen der Programmierung
Compiler: Parser (5C)
Prof. Dr Manfred Schmidt-Schauß
Gegeben:
eine kontextfreie Grammatik G und ein String w.
Fragen:
(1) gehört w zu L(G)?
(2) Welchen Syntaxbaum hat w?
(3) Welche Bedeutung hat w?
Vorgehen:
Konstruiere Herleitungsbaum zu w
Sommersemester 2013
1
Grundlagen der Programmierung 2 Parser
Syntaktische Analyse eines Programms
Gegeben:
Syntax einer Programmiersprache
und der Quelltext eines Programms.
Fragen:
Ist das Programm syntaktisch korrekt?
Was soll dieses Programm bewirken?
Aufgabe:
Ermittle Bedeutung“ des Programms,
”
Konstruktionsverfahren für Herleitungsbäume
(bzw. Syntaxbäume)
Grundlagen der Programmierung 2 Parser
– 2/53 –
Syntaktische Analyse bzgl einer CFG
– 3/53 –
•
Für jede CFG gibt es einen Parse-Algorithmus
mit worst case Laufzeit O(n3 )
(n : Anzahl der Eingabesymbole)
CYK: Cocke, Younger, Kasami,
falls Grammatik in Chomsky-Normalform
(Alle Regeln von der Form N → W mit |W | ≤ 2
oder Earley-Algorithmus
•
CYK benutzt dynamisches Programmieren.
erzeugt eine Tabelle:
pro Paar (N, w) von Nichtterminal N und Subwort w der Eingabe
ein Eintrag True wenn N →∗G w, sonst False
Grundlagen der Programmierung 2 Parser
– 4/53 –
Syntaktische Analyse bzgl einer CFG
Parse-Methoden und Beschränkungen
Beschränkung in dieser Vorlesung auf
Praxis:
Für jede Programmiersprache
gibt es einen Parser, der effizient arbeitet,
d.h. in O(n), oder in O(n ∗ log(n))
Grundlagen der Programmierung 2 Parser
– 5/53 –
Parse-Methoden: Vorgehensweisen:
•
einfach implementierbare oder effiziente Parser
•
Nur für eingeschränkte CFGs
•
Verarbeitung des Zeichenstroms bzw. des Eingabewortes
von links nach rechts
•
evtl. auch mit Vorausschau um einige Zeichen.
Grundlagen der Programmierung 2 Parser
– 6/53 –
Parse-Methoden: Vorgehensweisen:
Weiteres Unterscheidungsmerkmal:
Top-Down: Es wird versucht eine Herleitung vorwärts,
vom Startsymbol aus, zu bilden ( forward-chaining“)
”
L : Konstruktion einer Linksherleitung
Gängige Kombinationsmöglichkeiten:
Bottom-Up: Es wird versucht eine Herleitung rückwärts,
vom Wort aus, zu bilden ( backward-chaining“).
”
Grundlagen der Programmierung 2 Parser
R : Konstruktion einer Rechtsherleitung
•
•
– 7/53 –
Top-Down-Verfahren zur Konstruktion einer Linksherleitung
Bottom-Up-Verfahren zur Konstruktion einer Rechtsherleitung
Grundlagen der Programmierung 2 Parser
– 8/53 –
Beispiel
S
A
B
::=
::=
::=
09-Beispiel: Top-down:
Start mit Startsymbol S
Rate die Produktionen;
Nutze den zu parsenden String zur Steuerung
Bilde Restproblem
Ziel: Eingabestring bis zum Ende verarbeiten.
AB
0|1
8|9
Ziel
NT-Wort
Herleitung
Frage: Kann 09“ aus dieser Grammatik hergeleitet werden?
”
09
S
S
→
09
AB
AB
→
9
B
0B
ε
→
09
Das ergibt eine Linksherleitung.
Beachte
09“ wird von links nach rechts bearbeitet
”
Jedes Eingabezeichen bestimmt eindeutig die Produktion
Grundlagen der Programmierung 2 Parser
– 9/53 –
09-Beispiel: Bottom-up:
Vorgehen:
Regeln rückwärts auf den gegebenen String anwenden
das Startsymbol der Grammatik ist zu erreichen
Eine Rechtsherleitung wurde konstruiert
Manchmal sind mehrere Regeln anwendbar
zudem muss man i.a. den Teilstring raten,
auf den eine Produktion (rückwärts) anzuwenden ist
Im Beispiel: Gleicher Herleitungsbaum
S
A
1
Grundlagen der Programmierung 2 Parser

– 10/53 –
Beispiel: Suche nach der Herleitung
09 ← A9 ← AB ← S
Beachte:
Grundlagen der Programmierung 2 Parser
B
S
A
B
::=
::=
::=
A |B
0A | 1
0B | 2
Kann 002“ hergeleitet werden?
”
Ziel
002 002 02 2
NT-Wort
S
A
A
A
Herleitung S
A
0A 00A
?
002“ kann nur aus
”
Ziel
002
NT-Wort
S
Herleitung S
002
B hergeleitet werden:
002
B
B
02
B
0B
2
B
00B
2
– 11/53 –
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Bemerkungen
Parsemethoden
Ein deterministischer Top-Down-Parser
muss beim ersten Zeichen von 002“ entscheiden,
”
ob A, oder B.
Wir betrachten im folgenden:
rekursive absteigende Parser:
Allgemeine
optimierte: rekursive-prädiktive Parser (LL-Parser)
Diese Wahl kann falsch sein.
Misslingt eine Herleitung, so muss der Parser zurücksetzen:
Bottom-Up-Parser (LR-Parser)
Backtracking“
”
Grundlagen der Programmierung 2 Parser
– 13/53 –
Rekursiv absteigende Parser
Grundlagen der Programmierung 2 Parser
Struktur eines rekursiv absteigenden Parsers
•
Top-Down bzgl. der Grammatik.
Rekursiv absteigender Parser / Syntaxanalyse
ist an der Form der Regeln der Grammatik orientiert.
•
Eingabewort von links nach rechts
Methode: Top-Down-Prüfung der Anwendbarkeit der Regeln
•
Backtracking, falls Sackgasse
•
Konstruktion einer Linksherleitung
Grundlagen der Programmierung 2 Parser
– 14/53 –
– 15/53 –
Grundlagen der Programmierung 2 Parser
– 16/53 –
Struktur eines rekursiv absteigenden Parsers
Eigenschaften: rekursiv-absteigender Parser
•
Pro Nichtterminal N wird ein Parser PN programmiert.
Eingabe: String (bzw. Tokenstrom)
Ausgabe: Syntaxbaum zum Prefix der Eingabe; und Reststring
•
N → w1 | . . . | wn
(das sind alle Regeln zu N )
PN probiert alle wi aus
•
Prüfung, ob ein wi passt:
wi = wi1 wi2 . . . wim von links nach rechts durchgehen
Jeweils Parser Pwij aufrufen und Reststring weitergeben
I.a. rekursiver Aufruf, falls wij Nichtterminal.
Grundlagen der Programmierung 2 Parser
– 17/53 –
Rekursiv-absteigende Parser
•
Liefert alle Linksherleitungen für alle Präfixe des Tokenstroms
(wenn der Parser terminiert)
• Leicht implementierbar
• Leicht erweiterbar auf weitere Einschränkungen
• I.a. exponentiell oder sogar:
• Terminiert nicht für bestimmte (linksrekursive) Grammatiken,
obwohl eine Herleitung existiert:
Beispiel A ::= A+A | A-A | 1 | . . . | 9
Eingabe: 1+1 : Aber: nur die erste Regel wird (jeweils rekursiv)
versucht:
(A,1+1) → (A+A,1+1) → ((A+A)+A, 1+1) → . . .
Grundlagen der Programmierung 2 Parser
Funktionale Kombinator-Parser
Programme von Programmiersprachen kann man i.a.
in O(n) oder O(n ∗ log(n)) parsen,
Effiziente rekursiv-absteigende Parser benötigen i.a.:
• Erweiterungen wie Vorausschau
•
– 18/53 –
Programmierung
Umbau der Grammatik (Optimierung der Grammatik)
Grundlagen der Programmierung 2 Parser
– 19/53 –
Grundlagen der Programmierung 2 Parser
– 20/53 –
Funktionale Kombinator-Parser
Funktionale Kombinator-Parser
Implementierung von rekursiv-absteigenden Parsern in Haskell
Vorteile • relativ leicht verständliche Programmierung
• 1-1-Übersetzung der Regeln in Programmcode
• Nach Erweiterung und Optimierung kann der Parser Fehler
gut erkennen und deterministisch werden.
Um Backtracking zu implementieren:
Liste von erfolgreichen Ergebnissen
Pro Nichtterminal N eine Funktion
parserN:: String -> [(String, Syntaxbaum)]
bzw.
parserN:: [Token] -> [([Token], Syntaxbaum)]
verzögerte Auswertung ergibt richtige Reihenfolge der Abarbeitung.
Präfix der Eingabe 7→
(Rest der Eingabe, Resultat (z.B. Syntaxbaum) )
.....
Liste aller Möglichkeiten
Grundlagen der Programmierung 2 Parser
– 21/53 –
Haskell-Implementierung der Parser-Kombinatoren
– 22/53 –
Haskell: Parser-Kombinatoren (2)
Kombinator (kombiniert Parser)
Z.B. Alternative, Sequenz, Resultat-Umbau
erkennt einen String:
module CombParser where --- bzw. CombParserWithError
import Char
infixr 6 <*>, <*, *>
infixr 4 <|>, <!>
infixl 5 <@
type Parser a b = [a] -> [([a],b)]
token :: Eq s => [s] -> Parser s [s]
-- token :: Eq s => [s] -> Parser s [s]
token k xs | k == (take n xs)
= [(drop n xs, k)]
| otherwise
= []
where n = length k
testet ein Zeichen der Eingabe:
satisfy :: (s -> Bool) -> Parser s s
satisfy p [] = []
satisfy p (x:xs) = [(xs,x) | p x]
erkennt ein Zeichen:
symbol :: Eq s => s -> Parser s s
symbol a []
= []
symbol a (x:xs) | a ==x
= [(xs,x)]
| otherwise
= []
Grundlagen der Programmierung 2 Parser
Grundlagen der Programmierung 2 Parser
epsilon :: Parser s ()
epsilon xs = [(xs,())]
– 23/53 –
Grundlagen der Programmierung 2 Parser
– 24/53 –
Haskell: Parser-Kombinatoren (3)
Haskell: Parser-Kombinatoren (4)
Sequenzkombinator :
(<*>) :: Parser s a -> Parser s b -> Parser s (a,b)
(p1 <*> p2) xs = [(xs2, (v1,v2))
| (xs1,v1) <- p1 xs,
(xs2,v2) <- p2 xs1]
immer erfolgreich:
succeed :: r -> Parser s r
succeed v xs = [(xs,v)]
xs:
immer fehlschlagend:
p2
|
pfail :: Parser s r
pfail xs = []
•
•
•
Grundlagen der Programmierung 2 Parser
xs2
p1
– 25/53 –
Haskell: Parser-Kombinatoren (4b)
{z
}
p1 <*> p2
p1 parst den Anfang der Eingabe;
gibt den Reststring xs1 weiter an p2
p2 parst danach den Anfang des Reststrings
gibt den Reststring zurück
Gesamtresultat = Tupel aus den zwei Resultaten
Grundlagen der Programmierung 2 Parser
– 26/53 –
Haskell: Parser-Kombinatoren (6)
Operation auf dem Ergebnis des Parse :
Alternativkombinator :
(<@) :: Parser s a -> (a-> b) -> Parser s b
(<|>) :: Parser s a -> Parser s a -> Parser s a
(p1 <|> p2) xs = p1 xs ++ p2 xs
(p <@ f) xs = [(ys, f v) | (ys,v) <- p xs]
ignoriert rechtes Ergebnis:
Es werden beide Parser p1 und p2
auf die gleiche Eingaben angewendet
Alternativkombinator-2: nur das erste Ergebnis:
(<*) :: Parser s a -> Parser s b -> Parser s a
p <* q = p <*> q <@ fst
(<!>) :: Parser s a -> Parser s a -> Parser s a
(p1 <!> p2) xs = take 1 (p1 xs ++ p2 xs)
ignoriert linkes Ergebnis:
(*>) :: Parser s a -> Parser s b -> Parser s b
p *> q = p <*> q <@ snd
Grundlagen der Programmierung 2 Parser
– 27/53 –
Grundlagen der Programmierung 2 Parser
– 28/53 –
Funktionale Kombinator-Parser
Haskell: Parser-Kombinatoren (7)
erkennt Folge. d.h. entspricht *:
many :: Parser s a -> Parser s [a]
many p = p <*> many p <@ list
<|> succeed []
Aufgaben der Kombinatoren
1
Lesen und Prüfen,
2
Kombinatoren zum Aufbau des Syntaxbaums
many1 p = p <*> many p <@ list
Beispiel
p <* q = p <*> q <@ fst
digit :: Parser Char Int
digit = satisfy isDigit <@ f
where f c = ord c - ord ’0’
<*> ist ein Kombinator zum Lesen und Prüfen.
<@ bewirkt die Nachbearbeitung.
erkennt Zahl:
natural :: Parser Char Int
natural = many1 digit <@ foldl f 0
where f a b = a*10 + b
Grundlagen der Programmierung 2 Parser
– 29/53 –
Haskell: Parser-Kombinatoren (8)
Grundlagen der Programmierung 2 Parser
– 30/53 –
Haskell: Parser-Kombinatoren (9)
Nimmt nur die erste (maximale) Alternative des many: nur erlaubt,
wenn der Parser die weggelassenen Alternativen nicht benötigt
Erkennt Klammerung; Klammern kommen nicht in den
Syntaxbaum:
manyex :: Parser s a -> Parser s [a]
manyex p = p <*> many p <@ list
<!> succeed []
pack:: Parser s a -> Parser s b -> Parser s c -> Parser s b
pack s1 p s2 = s1 *> p <* s2
many1ex p = p <*> manyex p <@ list
option p = p <@ (\x->[x])
<!> epsilon <@ (\x-> [])
Erkennt Infix-Folge wie z.B. (1+2+3+4+5): Liste der Argumente:
opSeqInf psymb parg = (parg <*> many (psymb *> parg)) <@ list
Nimmt nur die erste (maximale) Alternative bei Zahlen:
naturalex :: Parser Char Int
naturalex = many1ex digit <@ foldl f 0
where f a b = a*10 + b
Grundlagen der Programmierung 2 Parser
– 31/53 –
Grundlagen der Programmierung 2 Parser
– 32/53 –
Einfaches Beispiel
Leicht komplexeres Beispiel
S → AB
A → aA
B → bB
parse S = parse A <*> parse B
parse A = (symbol ’a’) <*> parse A
parse B = (symbol ’b’) <*> parse B
Grammatik-Regeln:
S → AB
A→a
B→b
parse S = parse A <*> parse B
parse A = (symbol ’a’)
parse B = (symbol ’b’)
Grammatik-Regeln:
Programm:
Grundlagen der Programmierung 2 Parser
Programm:
Typgerecht programmieren mit Syntaxbaum-Erzeugung:
parse S = parse A <*> parse B <@ (\(x,y) -> [x,y])
parse A = many (symbol ’a’)
parse B = many (symbol ’b’)
– 33/53 –
Beispiel: Polymorphe Typ-Ausdrücke
::=
umgebaute Grammatik;
nicht linksrekursiv und optimiert für den Parser
AT -> AT | (AT)
AT
NOARNX
NOAR
TCT
KLRUND
KLECK
| [AT] | Var | TCA
TCA
::=
TC | (TC AT . . . AT) | (AT1 ,. . . ,ATn ), n > 1
Grammatik ist linksrekursiv!
Grundlagen der Programmierung 2 Parser
– 34/53 –
Beispiel: Polymorphe Typ-Ausdrücke
Grammatik
AT
Grundlagen der Programmierung 2 Parser
– 35/53 –
Grundlagen der Programmierung 2 Parser
::=
::=
::=
::=
::=
::=
NOAR { NOARNX | ε }
-> AT
Var | TCT | KLRUND | KLECK
TC NOAR . . . NOAR
(AT,. . . ,AT)
Mindestens 2-Tupel
[AT]
– 36/53 –
Kombinatorparser Mit Fehlerbehandlung
Kombinatorparser; Beispiele
AT
NOARNX
NOAR
TCT
KLRUND
KLECK
Erweiterte Bibliothek
mit neuen Kombinatoren
((p1 <*>!) errStr) p2
Ergibt Fehler mit Text errStr
Wenn p2 fehlschlägt
((p1 *>!) errStr) p2
Wie <*>! aber nur Ergebnis von p2
((p1 *<!) errStr) p2
Wie <*>! aber nur Ergebnis von p1
Grundlagen der Programmierung 2 Parser
– 37/53 –
Kombinatorparser mit Fehlerbehandlung
NOAR { NOARNX | ε }
-> AT
Var | TCT | KLRUND | KLECK
TC NOAR . . . NOAR
(AT,. . . ,AT)
Mindestens 2-Tupel
[AT]
parseKLRUND =
(parseSymbol ’(’ *> (parseINKLRUND
<*!
") erwartet")
(parseSymbol ’)’))
<@ id
parseINKLRUND = (parseAT <*> (manyex (((parseSymbol ’,’)
*>! "Typ nach , erwartet") parseAT)))
<@@ (\(t1,t2) er -> if null t2 then t1
else (Fn ("Tup"++(show ((length t2) +1))) (t1:t2) er))
Grundlagen der Programmierung 2 Parser
– 38/53 –
Kombinatorparser mit Fehlerbehandlung
Programme und Vorführung
typeUnifErr
combParserWithError
parseEquation
parseAT . prelex
printUnif "(a,a) = (b,[b])“
Grundlagen der Programmierung 2 Parser
::=
::=
::=
::=
::=
::=
Programme und Vorführung
html-parser.hs
main
prelex (linPosNumbering "<D> xxx </D>\n<br> text </br>")
– 39/53 –
Grundlagen der Programmierung 2 Parser
– 40/53 –
Fehler-Meldungen: Bemerkungen
Rekursiv-prädiktive Parser
Die Fehlererkennung und -meldung sollte spezifisch sein und
möglichst genau die Ursache und Stelle melden.
Optimierte rekursiv absteigende Parser
für eingeschränkte Grammatiken ( LL(1) ).
Schlecht:
Gut
Keine Alternativen mehr gefunden in Zeile... “
”
Fehler in Zeile ... Spalte... Möglicher Grund: ... “
”
Bei deterministischen Parsern
(und Kombinatorparser mit Fehlerbehandlung)
Die Fehlerstelle ist klar;
die Fehlerursache ist auch meist spezifisch genug
Eigenschaften:
•
Die anzuwendende Produktion ist immer eindeutig festgelegt
abhängig vom aktuellen Nichtterminal und
dem nächsten Symbol (Lookahead-Symbol) der Resteingabe
•
kein Zurücksetzen notwendig,
•
deterministische Abarbeitung der Eingabe von links nach
rechts
Bei Parsern mit Backtracking und ohne Fehlerbehandlung
Der richtige Fehlerstelle ist meist unklar
Der Backtracking-Parser kann meist nur melden:
keine Alternativen mehr
Grundlagen der Programmierung 2 Parser
Aber:
– 41/53 –
Rekursiv-prädiktive Parser
⇒
Grundlagen der Programmierung 2 Parser
– 42/53 –
Rekursiv-prädiktive Parser
Eindeutigkeitsbedingung:
Wenn A → w1 | . . . | wn alle Regeln zu A sind:
Falls Parser im Zustand A
Für jedes erste Symbol a der Eingabe:
nur eine Regel A → wi darf anwendbar sein!
Zweidimensionale Tabelle:
(Lookahead-Symbol, Nichtterminal)
man kann nicht für jede eindeutige kontextfreie Grammatik
einen rekursiv-prädiktiven Parser konstruieren.
7→
Regel oder Fehlereintrag
Tabellengesteuerter rekursiv-prädiktiver Parser:
Beispiel:
Grundlagen der Programmierung 2 Parser
– 43/53 –
Grundlagen der Programmierung 2 Parser
A → bCD | aEF | cG | H
H → dabc
...
– 44/53 –
Rekursiv-prädiktive Parser
Rekursiv-prädiktive Parser, ε-Fall
Beispiel:
Sonderfall:
Es gibt eine Regel A → wi mit wi
Diese wird ausgewählt, wenn:
•
•
•
→∗
S
A
H
...
B
C
ε:
keine passende rechte Seite für das Lookahead-Symbol und
das Lookahead-Symbol kann auf A folgen und
es gibt nur eine solche Regel für A
→ AB | AC
→ bCD | aEF | cG | H
→ ε
→ dA
→ eA
Im Zustand A und bei Eingabesymbol d:
A → H wird ausgewählt.
Grundlagen der Programmierung 2 Parser
– 45/53 –
FIRST- und FOLLOW-Mengen
:=
Terminal-Symbole die am Anfang eines
erkannten A-Wortes stehen können.
(auch ε)
follow(A)
:=
Terminal-Symbole die auf ein
erkanntes A-Wort folgen können.
Ex
Plus
PlusRest
SigZ
B
Z
::=
::=
::=
::=
::=
::=
Plus
SigZ Plusrest
+ SigZ PlusRest | ε
B|-B
Z | ( Ex )
0 | ... | 9
Man erhält als first-Mengen:
Diese Mengen kann man in allen rekursiv-absteigenden Parsern
zur Eindämmung, evtl. zur Vermeidung, von Backtracking
verwenden.
Grundlagen der Programmierung 2 Parser
– 46/53 –
Beispiel für first
Wenn Grammatik G gegeben ist:
first(A)
Grundlagen der Programmierung 2 Parser
– 47/53 –
Ex
Plus
Plus
Rest
SigZ
B
Z
0,...,9, (,-
0,...,9, (,-
+, ε
0,...,9, (,-
0,...,9, (
0,...,9
Grundlagen der Programmierung 2 Parser
– 48/53 –
Beispiel für follow :
Ex
Plus
PlusRest
SigZ
B
Z
::=
::=
::=
::=
::=
::=
Vorgehen des LL(1)-Parsers
Plus
SigZ Plusrest
+ SigZ PlusRest | ε
B|-B
Z | ( Ex )
0 | ... | 9
Bei Symbol a, und aktuellem Nichtterminal A:
• Ist a ∈ first(wi ) für eine Regel A ::= wi , dann nehme diese Regel.
(ε 6∈ first(wi ) für alle i muss gelten. )
• Ist a 6∈ first(wi ) für alle Regeln A ::= wi ,
dann gibt es maximal eine Regel A ::= w mit first(w) = ∅
Falls a ∈ follow(A), dann diese Regel.
• Wenn auch dann keine passende Alternative existiert,
wird mit Fehler abgebrochen.
Man erhält als follow- Mengen:
Ex
)
Plus
)
PlusRest
)
SigZ
+,)
B
+,)
Z
+,)
Vorteil: genaue und frühe Fehlererkennung
Grammatik ist LL(1) parsebar,
da: First-Mengen zu Regelalternativen passen und
first(PlusRest) ∩ follow(PlusRest) = ∅
Grundlagen der Programmierung 2 Parser
– 49/53 –
Beispiel: vereinfachte Grammatik für Ausdrücke
Expr
Rest
Term
•
•
•
•
•
•
•
::=
::=
::=
– 50/53 –
Beispielparser zur Grammatik
Parsebaum:
Term Rest
+ Term Rest | − Term Rest | ε
0 | ... | 9
Syntaxbaum:
PExp
first(Term Rest) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
first(+ Term Rest) = {+},
first(− Term Rest) = {−}
first(Expr ) = first(Term ) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
first(Rest) = {+, −, ε}
follow(Expr) = ∅.
follow(Rest) = ∅.
follow(Term) = {+, −}.
Diese Grammatik hat somit die LL(1)-Eigenschaft.
Grundlagen der Programmierung 2 Parser
Grundlagen der Programmierung 2 Parser
– 51/53 –
1
+
%
|
PRest
+
y
&
2
−
1

y
PRest
3
%
2
−
~
3
PLeer
Der Parsebaum entspricht der Grammatik,
aber noch nicht der gewünschten Struktur
des arithmetischen Ausdrucks.
Man braucht eine Nachbearbeitung des Parsebaumes.
Grundlagen der Programmierung 2 Parser
– 52/53 –
Prädiktiv vs. Kombinatoren
Meistens kann man für Grammatiken die geeignet sind für
rekursiv-prädiktive Parser (LL(1))
auch einen deterministischen Kombinator-Parser schreiben.
(Nach etwas Analyse und Nachdenken)
Dabei ist im Parserprogramm
überall der Parserkombinator <|> durch <!> ersetzt.
und man kann teilweise die um Fehlermeldungen erweiterten
Kombinatoren verwenden
D.h der Parser ist frei von Backtracking.
Grundlagen der Programmierung 2 Parser
– 53/53 –
Herunterladen