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

Werbung
Grundlagen der Programmierung
Compiler: Parser (5C)
Prof. Dr Manfred Schmidt-Schauß
Sommersemester 2013
Syntaktische Analyse (Parsen)
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
Grundlagen der Programmierung 2 Parser
– 2/53 –
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
– 3/53 –
Syntaktische Analyse bzgl einer CFG
•
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
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 und Beschränkungen
Beschränkung in dieser Vorlesung auf
•
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:
Top-Down: Es wird versucht eine Herleitung vorwärts,
vom Startsymbol aus, zu bilden ( forward-chaining“)
”
Bottom-Up: Es wird versucht eine Herleitung rückwärts,
vom Wort aus, zu bilden ( backward-chaining“).
”
Grundlagen der Programmierung 2 Parser
– 7/53 –
Parse-Methoden: Vorgehensweisen:
Weiteres Unterscheidungsmerkmal:
R : Konstruktion einer Rechtsherleitung
L : Konstruktion einer Linksherleitung
Gängige Kombinationsmöglichkeiten:
•
•
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
::=
::=
::=
AB
0|1
8|9
Frage: Kann 09“ aus dieser Grammatik hergeleitet werden?
”
Grundlagen der Programmierung 2 Parser
– 9/53 –
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.
Ziel
NT-Wort
Herleitung
09
S
S
Das ergibt eine Linksherleitung.
Beachte
09“ wird von links nach rechts bearbeitet
”
Jedes Eingabezeichen bestimmt eindeutig die Produktion
Grundlagen der Programmierung 2 Parser
– 10/53 –
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.
Ziel
NT-Wort
Herleitung
09
S
S
→
09
AB
AB
Das ergibt eine Linksherleitung.
Beachte
09“ wird von links nach rechts bearbeitet
”
Jedes Eingabezeichen bestimmt eindeutig die Produktion
Grundlagen der Programmierung 2 Parser
– 10/53 –
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.
Ziel
NT-Wort
Herleitung
09
S
S
→
09
AB
AB
→
9
B
0B
Das ergibt eine Linksherleitung.
Beachte
09“ wird von links nach rechts bearbeitet
”
Jedes Eingabezeichen bestimmt eindeutig die Produktion
Grundlagen der Programmierung 2 Parser
– 10/53 –
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.
Ziel
NT-Wort
Herleitung
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
– 10/53 –
09-Beispiel: Bottom-up:
Vorgehen:
Regeln rückwärts auf den gegebenen String anwenden
das Startsymbol der Grammatik ist zu erreichen
09
Eine Rechtsherleitung wurde konstruiert
Beachte:
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

B
2
– 11/53 –
09-Beispiel: Bottom-up:
Vorgehen:
Regeln rückwärts auf den gegebenen String anwenden
das Startsymbol der Grammatik ist zu erreichen
09 ← A9
Eine Rechtsherleitung wurde konstruiert
Beachte:
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

B
2
– 11/53 –
09-Beispiel: Bottom-up:
Vorgehen:
Regeln rückwärts auf den gegebenen String anwenden
das Startsymbol der Grammatik ist zu erreichen
09 ← A9 ← AB
Eine Rechtsherleitung wurde konstruiert
Beachte:
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

B
2
– 11/53 –
09-Beispiel: Bottom-up:
Vorgehen:
Regeln rückwärts auf den gegebenen String anwenden
das Startsymbol der Grammatik ist zu erreichen
09 ← A9 ← AB ← S
Eine Rechtsherleitung wurde konstruiert
Beachte:
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

B
2
– 11/53 –
Beispiel: Suche nach der Herleitung
S
A
B
::=
::=
::=
A |B
0A | 1
0B | 2
Kann 002“ hergeleitet werden?
”
Ziel
002
NT-Wort
S
Herleitung S
002“ kann nur aus B hergeleitet werden:
”
Ziel
002
NT-Wort
S
Herleitung S
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Suche nach der Herleitung
S
A
B
::=
::=
::=
A |B
0A | 1
0B | 2
Kann 002“ hergeleitet werden?
”
Ziel
002 002
NT-Wort
S
A
Herleitung S
A
002“ kann nur aus B hergeleitet werden:
”
Ziel
002
NT-Wort
S
Herleitung S
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Suche nach der Herleitung
S
A
B
::=
::=
::=
A |B
0A | 1
0B | 2
Kann 002“ hergeleitet werden?
”
Ziel
002 002 02
NT-Wort
S
A
A
Herleitung S
A
0A
002“ kann nur aus B hergeleitet werden:
”
Ziel
002
NT-Wort
S
Herleitung S
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Suche nach der Herleitung
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 B hergeleitet werden:
”
Ziel
002
NT-Wort
S
Herleitung S
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Suche nach der Herleitung
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 B hergeleitet werden:
”
Ziel
002
NT-Wort
S
Herleitung S
Grundlagen der Programmierung 2 Parser
– 12/53 –
Beispiel: Suche nach der Herleitung
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
Grundlagen der Programmierung 2 Parser
?
B hergeleitet werden:
002
B
B
– 12/53 –
Beispiel: Suche nach der Herleitung
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
Grundlagen der Programmierung 2 Parser
?
B hergeleitet werden:
002
B
B
02
B
0B
– 12/53 –
Beispiel: Suche nach der Herleitung
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
Grundlagen der Programmierung 2 Parser
?
B hergeleitet werden:
002
B
B
02
B
0B
2
B
00B
– 12/53 –
Beispiel: Suche nach der Herleitung
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
Grundlagen der Programmierung 2 Parser
B hergeleitet werden:
002
B
B
02
B
0B
2
B
00B
– 12/53 –
Beispiel: Bemerkungen
Ein deterministischer Top-Down-Parser
muss beim ersten Zeichen von 002“ entscheiden,
”
ob A, oder B.
Diese Wahl kann falsch sein.
Misslingt eine Herleitung, so muss der Parser zurücksetzen:
Backtracking“
”
Grundlagen der Programmierung 2 Parser
– 13/53 –
Parsemethoden
Wir betrachten im folgenden:
rekursive absteigende Parser:
Allgemeine
optimierte: rekursive-prädiktive Parser (LL-Parser)
Bottom-Up-Parser (LR-Parser)
Grundlagen der Programmierung 2 Parser
– 14/53 –
Rekursiv absteigende Parser
Rekursiv absteigender Parser / Syntaxanalyse
ist an der Form der Regeln der Grammatik orientiert.
Methode: Top-Down-Prüfung der Anwendbarkeit der Regeln
Grundlagen der Programmierung 2 Parser
– 15/53 –
Struktur eines rekursiv absteigenden Parsers
•
Top-Down bzgl. der Grammatik.
•
Eingabewort von links nach rechts
•
Backtracking, falls Sackgasse
•
Konstruktion einer Linksherleitung
Grundlagen der Programmierung 2 Parser
– 16/53 –
Struktur eines rekursiv absteigenden Parsers
•
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 –
Eigenschaften: rekursiv-absteigender 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
– 18/53 –
Rekursiv-absteigende 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
•
Umbau der Grammatik (Optimierung der Grammatik)
Grundlagen der Programmierung 2 Parser
– 19/53 –
Funktionale Kombinator-Parser
Programmierung
Grundlagen der Programmierung 2 Parser
– 20/53 –
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.
Pro Nichtterminal N eine Funktion
parserN:: String -> [(String, Syntaxbaum)]
bzw.
parserN:: [Token] -> [([Token], Syntaxbaum)]
Präfix der Eingabe 7→
(Rest der Eingabe, Resultat (z.B. Syntaxbaum) )
.....
Liste aller Möglichkeiten
Grundlagen der Programmierung 2 Parser
– 21/53 –
Funktionale Kombinator-Parser
Um Backtracking zu implementieren:
Liste von erfolgreichen Ergebnissen
verzögerte Auswertung ergibt richtige Reihenfolge der Abarbeitung.
Grundlagen der Programmierung 2 Parser
– 22/53 –
Haskell-Implementierung der Parser-Kombinatoren
Kombinator (kombiniert Parser)
Z.B. Alternative, Sequenz, Resultat-Umbau
module CombParser where --- bzw. CombParserWithError
import Char
infixr 6 <*>, <*, *>
infixr 4 <|>, <!>
infixl 5 <@
type Parser a b = [a] -> [([a],b)]
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
– 23/53 –
Haskell: Parser-Kombinatoren (2)
erkennt einen String:
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]
epsilon :: Parser s ()
epsilon xs = [(xs,())]
Grundlagen der Programmierung 2 Parser
– 24/53 –
Haskell: Parser-Kombinatoren (3)
immer erfolgreich:
succeed :: r -> Parser s r
succeed v xs = [(xs,v)]
immer fehlschlagend:
pfail :: Parser s r
pfail xs = []
Grundlagen der Programmierung 2 Parser
– 25/53 –
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]
xs:
xs2
p1
p2
|
•
•
•
{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 (4b)
Alternativkombinator :
(<|>) :: Parser s a -> Parser s a -> Parser s a
(p1 <|> p2) xs = p1 xs ++ p2 xs
Es werden beide Parser p1 und p2
auf die gleiche Eingaben angewendet
Alternativkombinator-2: nur das erste Ergebnis:
(<!>) :: Parser s a -> Parser s a -> Parser s a
(p1 <!> p2) xs = take 1 (p1 xs ++ p2 xs)
Grundlagen der Programmierung 2 Parser
– 27/53 –
Haskell: Parser-Kombinatoren (6)
Operation auf dem Ergebnis des Parse :
(<@) :: Parser s a -> (a-> b) -> Parser s b
(p <@ f) xs = [(ys, f v) | (ys,v) <- p xs]
ignoriert rechtes Ergebnis:
(<*) :: Parser s a -> Parser s b -> Parser s a
p <* q = p <*> q <@ fst
ignoriert linkes Ergebnis:
(*>) :: Parser s a -> Parser s b -> Parser s b
p *> q = p <*> q <@ snd
Grundlagen der Programmierung 2 Parser
– 28/53 –
Funktionale Kombinator-Parser
Aufgaben der Kombinatoren
1
Lesen und Prüfen,
2
Kombinatoren zum Aufbau des Syntaxbaums
Beispiel
p <* q = p <*> q <@ fst
<*> ist ein Kombinator zum Lesen und Prüfen.
<@ bewirkt die Nachbearbeitung.
Grundlagen der Programmierung 2 Parser
– 29/53 –
Haskell: Parser-Kombinatoren (7)
erkennt Folge. d.h. entspricht *:
many :: Parser s a -> Parser s [a]
many p = p <*> many p <@ list
<|> succeed []
many1 p = p <*> many p <@ list
digit :: Parser Char Int
digit = satisfy isDigit <@ f
where f c = ord c - ord ’0’
erkennt Zahl:
natural :: Parser Char Int
natural = many1 digit <@ foldl f 0
where f a b = a*10 + b
Grundlagen der Programmierung 2 Parser
– 30/53 –
Haskell: Parser-Kombinatoren (8)
Nimmt nur die erste (maximale) Alternative des many: nur erlaubt,
wenn der Parser die weggelassenen Alternativen nicht benötigt
manyex :: Parser s a -> Parser s [a]
manyex p = p <*> many p <@ list
<!> succeed []
many1ex p = p <*> manyex p <@ list
option p = p <@ (\x->[x])
<!> epsilon <@ (\x-> [])
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 –
Haskell: Parser-Kombinatoren (9)
Erkennt Klammerung; Klammern kommen nicht in den
Syntaxbaum:
pack:: Parser s a -> Parser s b -> Parser s c -> Parser s b
pack s1 p s2 = s1 *> p <* s2
Erkennt Infix-Folge wie z.B. (1+2+3+4+5): Liste der Argumente:
opSeqInf psymb parg = (parg <*> many (psymb *> parg)) <@ list
Grundlagen der Programmierung 2 Parser
– 32/53 –
Einfaches Beispiel
Grammatik-Regeln:
Grundlagen der Programmierung 2 Parser
S → AB
A→a
B→b
– 33/53 –
Einfaches Beispiel
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
– 33/53 –
Leicht komplexeres Beispiel
Grammatik-Regeln:
Grundlagen der Programmierung 2 Parser
S → AB
A → aA
B → bB
– 34/53 –
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:
Programm:
Grundlagen der Programmierung 2 Parser
– 34/53 –
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:
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’)
Grundlagen der Programmierung 2 Parser
– 34/53 –
Beispiel: Polymorphe Typ-Ausdrücke
Grammatik
AT
::=
AT -> AT | (AT)
| [AT] | Var | TCA
TCA
::=
Grundlagen der Programmierung 2 Parser
TC | (TC AT . . . AT) | (AT1 ,. . . ,ATn ), n > 1
– 35/53 –
Beispiel: Polymorphe Typ-Ausdrücke
Grammatik
AT
::=
AT -> AT | (AT)
| [AT] | Var | TCA
TCA
::=
TC | (TC AT . . . AT) | (AT1 ,. . . ,ATn ), n > 1
Grammatik ist linksrekursiv!
Grundlagen der Programmierung 2 Parser
– 35/53 –
Beispiel: Polymorphe Typ-Ausdrücke
umgebaute Grammatik;
nicht linksrekursiv und optimiert für den Parser
AT
NOARNX
NOAR
TCT
KLRUND
KLECK
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
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; Beispiele
AT
NOARNX
NOAR
TCT
KLRUND
KLECK
::=
::=
::=
::=
::=
::=
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
– 39/53 –
Kombinatorparser mit Fehlerbehandlung
Programme und Vorführung
html-parser.hs
main
prelex (linPosNumbering "<D> xxx </D>\n<br> text </br>")
Grundlagen der Programmierung 2 Parser
– 40/53 –
Fehler-Meldungen: Bemerkungen
Die Fehlererkennung und -meldung sollte spezifisch sein und
möglichst genau die Ursache und Stelle melden.
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
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
– 41/53 –
Rekursiv-prädiktive Parser
Optimierte rekursiv absteigende Parser
für eingeschränkte Grammatiken ( LL(1) ).
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
Aber:
man kann nicht für jede eindeutige kontextfreie Grammatik
einen rekursiv-prädiktiven Parser konstruieren.
Grundlagen der Programmierung 2 Parser
– 42/53 –
Rekursiv-prädiktive Parser
Zweidimensionale Tabelle:
(Lookahead-Symbol, Nichtterminal)
⇒
7→
Regel oder Fehlereintrag
Tabellengesteuerter rekursiv-prädiktiver Parser:
Grundlagen der Programmierung 2 Parser
– 43/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!
Beispiel:
Grundlagen der Programmierung 2 Parser
A → bCD | aEF | cG | H
H → dabc
...
– 44/53 –
Rekursiv-prädiktive Parser
Sonderfall:
Es gibt eine Regel A → wi mit wi →∗ ε:
Diese wird ausgewählt, wenn:
•
•
•
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
Grundlagen der Programmierung 2 Parser
– 45/53 –
Rekursiv-prädiktive Parser, ε-Fall
Beispiel:
S
A
H
...
B
C
→ 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
– 46/53 –
FIRST- und FOLLOW-Mengen
Wenn Grammatik G gegeben ist:
first(A)
:=
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.
Diese Mengen kann man in allen rekursiv-absteigenden Parsern
zur Eindämmung, evtl. zur Vermeidung, von Backtracking
verwenden.
Grundlagen der Programmierung 2 Parser
– 47/53 –
Beispiel für first
Ex
Plus
PlusRest
SigZ
B
Z
::=
::=
::=
::=
::=
::=
Plus
SigZ Plusrest
+ SigZ PlusRest | ε
B|-B
Z | ( Ex )
0 | ... | 9
Man erhält als first-Mengen:
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
::=
::=
::=
::=
::=
::=
Plus
SigZ Plusrest
+ SigZ PlusRest | ε
B|-B
Z | ( Ex )
0 | ... | 9
Man erhält als follow- Mengen:
Ex
)
Plus
)
Grundlagen der Programmierung 2 Parser
PlusRest
)
SigZ
+,)
B
+,)
Z
+,)
– 49/53 –
Beispiel für follow :
Ex
Plus
PlusRest
SigZ
B
Z
::=
::=
::=
::=
::=
::=
Plus
SigZ Plusrest
+ SigZ PlusRest | ε
B|-B
Z | ( Ex )
0 | ... | 9
Man erhält als follow- Mengen:
Ex
)
Plus
)
PlusRest
)
SigZ
+,)
B
+,)
Z
+,)
Grammatik ist LL(1) parsebar,
da: First-Mengen zu Regelalternativen passen und
first(PlusRest) ∩ follow(PlusRest) = ∅
Grundlagen der Programmierung 2 Parser
– 49/53 –
Vorgehen des LL(1)-Parsers
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.
Vorteil: genaue und frühe Fehlererkennung
Grundlagen der Programmierung 2 Parser
– 50/53 –
Beispiel: vereinfachte Grammatik für Ausdrücke
Expr
Rest
Term
•
•
•
•
•
•
•
::=
::=
::=
Term Rest
+ Term Rest | − Term Rest | ε
0 | ... | 9
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
– 51/53 –
Beispielparser zur Grammatik
Parsebaum:
Syntaxbaum:
PExp
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