22. 41 }
‘0’–‘9’
‘ -’
start I F ‘0’–‘9’ R
‘0’–‘9’
Ganzzahlen in Java
Endlicher Automat für den Ganzzahlparser in Long.parseLong
18
23. 1 public static long parseLong(String s, int radix) throws NumberFormatException
{
3 if (s == null) throw new NumberFormatException(quot;nullquot;);
[...]
5 int max = s.length();
[...]
7 // State 1: Treat the sign
if (s.charAt(0) == ’-’) {
9 negative = true;
limit = Long.MIN_VALUE;
11 i++;
} else
13 limit = -Long.MAX_VALUE;
15 // State 2: Treat first digit
if (i < max) {
17 digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) // Not a digit
19 throw NumberFormatException.forInputString(s);
else
21 result = -digit;
}
23
// State 3: Treat all other digits
19
25 while (i < max) {
digit = Character.digit(s.charAt(i++),radix);
24. 21 result = -digit;
}
23
// State 3: Treat all other digits
25 while (i < max) {
digit = Character.digit(s.charAt(i++),radix);
27 if (digit < 0) // Not a digit
throw NumberFormatException.forInputString(s);
29 result *= radix;
// Check if out-of-bounds [...]
31 result -= digit;
}
33 if (negative) {
if (i > 1)
35 return result;
else /* Only got quot;-quot; */
37 throw NumberFormatException.forInputString(s);
} else
39 return -result;
41 }
‘0’–‘9’
‘ -’
start I F ‘0’–‘9’ R
20
‘0’–‘9’
26. en, die unser Parser erkennen soll:
2.1 Regulärer Ausdruck für Gleitpunktzahlen
(‘Für -’)? digit+ (‘.’ digit+)? ((‘e’|‘E’) digit+)? Aus
+’|‘ unser Beispiel beginnen wir mit einem regulären
Gleitpunktzahlen, die unser Parser erkennen soll:
0’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘5(‘+’|‘-’ | ‘7’ | ‘8’.|’ ‘9’
’ | ‘6 ’)? digit+ (‘ digit+)? ((‘e’|‘E’) digit
digit digit*
digit := ‘0’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘5’ | ‘6’ | ‘7’ | ‘8’ | ‘9’
digit+ := digit digit*
tion ist fast wie in den Übungen behandelt, allerdin
omma-Anteil wegfallenfast wie in den der Exponent). a
Die Spezifikation ist kann (wie Übungen behandelt,
dass der Nachkomma-Anteil wegfallen kann (wie der Expon
Gleitpunktzahlen in Java
utomat für Gleitpunktzahlen
2.2 Endlicher Automat für Gleitpunktzahlen
Spezifikation: Regulärer Ausdruck
22
ache können wir durch folgenden Automaten besch
Die gleiche Sprache können wir durch folgenden Automate
27. ‘+’, ‘-’
start S s,d Sd
‘0’–‘9’ ‘.’ ‘0’–‘9’
‘0’–‘9’
S d ,p,e ‘0’–‘9’ Fd ‘0’–‘9’ F d ,e
‘e’, ‘E’ ‘e’, ‘E’
E s,d
‘0’–‘9’
‘+’, ‘-’
Ed ‘0’–‘9’ E d ,#
‘0’–‘9’ 23
Der Automat verwendet die folgenden Zustände, benannt nach d
28. gibt. Dabei nennen wir für eine Gleitpunktzahl s. f · 10 : s den significand (oder
Vorkomma-Anteil der Mantisse), f die fraction (oder Nachkomma-Anteil der Man-
tisse), und e den exponent.
Zustand Teil der Zahl Nachfolgerzeichen Beschreibung
S s,d significand Ziffer (d ) oder Vorzeichen (s ) Anfangszustand, erkennt optionales Vor-
zeichen
Sd –”– Ziffer stellt sicher, dass zumindest eine Ziffer im
significand vorkommt
S d , f ,e –”– Ziffer, Punkt (p ) oder e erkennt beliebige Folgen von Ziffern, fährt
mit Erkennung von fraction oder expo-
nent fort, Endzustand
Fd fraction Ziffer stellt sicher, dass zumindest eine Ziffer in
fraction vorkommt
F d ,e –”– Ziffer or e erkennt beliebige Folgen von Ziffern, fährt
mit Erkennung von exponent fort, Endzu-
stand
E s,d exponent Ziffer oder Vorzeichen Ziffer oder Vorzeichen als Beginn des ex-
ponent
Ed –”– digit stellt sicher, dass zumindest eine Ziffer in
exponent vorkommt
Ed –”– digit erkennt beliebige Folgen von Ziffern, End-
zustand
24
29. Implementierung Endlicher Automaten
Ansätze …
Implicit state: wie Long.parseLong(); Zustand implizit
Bereich im Programm identifiziert den aktuellen Zustand (PC)
für einfach, im wesentlichen sequentielle Automaten geeignet
Loop-and-switch:
Schleife über Eingabe + goto/switch je nach akt. Zustand und Symbol
effizient nur wenn Sprünge berechnet werden können
typisch für komplexere Automaten mit oft wiederkehrenden Zuständen
Loop-and-lookup: Schleife + Nachschlagen in Tabellenrepr. von δ
Zeilen: Zustände, Spalten: Eingabesymbole, Zelleninhalt: Folgezustand
Zeit-effizient auf Kosten von Speicher, nur bei kleinem Eingabealphabet
z.B. reguläre Ausdrücke über Basispaarsequenzen (nur A, T, G, C).
25
30. Implementierung Endlicher Automaten
Umgebung für FP-Parser …
FPParser: abstrakte Oberklasse für alle Floating-Point-Parser
Attribute & Methoden zur Verwaltung & Berechnung der FP-Zahl
FPPaser.FPNumberPart: hält ganzzahligen Anteil einer FP-Zahl
significand (Vorkomma-Anteil), fraction (Nachkomma-Antail), exponent
#parse(char): Transition & Berechnung des Automaten
bei Eingabe eines einzelnen Zeichens
#parse(String): Schleife über die Eingabe
für jedes Zeichen wird #parse(char) aufgerufen
26
31. Implementierung Endlicher Automaten
Umgebung für FP-Parser …
FPParser: abstrakte Oberklasse für alle Floating-Point-Parser
Attribute & Methoden zur Verwaltung & Berechnung der FP-Zahl
FPPaser.FPNumberPart: hält ganzzahligen Anteil einer FP-Zahl
significand (Vorkomma-Anteil), fraction (Nachkomma-Antail), exponent
#parse(char): Transition & Berechnung des Automaten
bei Eingabe eines einzelnen Zeichens
#parse(String): Schleife über die Eingabe
für jedes Zeichen wird #parse(char) aufgerufen
FPParser
26
32. einer Ziffer bleiben wir im gleichen Zustand und fügen die Ziffer zur aktu-
ellen Repräsentation des significand hinzu. Bei einem Punkt gehen wir in
den Zustand F d , also erwarten die erste Ziffer der fraction (würden wir hier
Implementierung Endlicher Automaten
in F d ,e übergehen, wären auch Zahlen der Form 12.E10 erlaubt). Bei einem
e oder E gehen wir in Zustand E s,d (STATE_Exponent). In den beiden letzten
Loop-and-Switch …
Fällen dienen die Zeichen nur als Trennzeichen und werden nicht zur aktu-
ellen Repräsentation hinzugefügt.
1 case STATE_SignifDigit_Fraction_Exponent:
if(Character.isDigit(c)) {
3 state = STATE_SignifDigit_Fraction_Exponent;
significand.addDigit(c);
5 }
else if(c == ’.’) {
7 state = STATE_FractionDigit;
}
9 else if(c == ’e’ || c == ’E’) {
state = STATE_Exponent;
11 }
else state = STATE_Failure;
13 break;
Loop-and-Lookup: realisiert in MatrixFPParser. Die Idee von loop-and-lookup Im-
plementierungen ist, in der Schleife über die Eingabezeichen mit Hilfe ei- 27
ner (konstanten) Lookup-Tabelle den Folgezustand (und eventuelle Berech-
33. einer Ziffer bleiben wir im gleichen Zustand und fügen die Ziffer zur aktu-
ellen Repräsentation des significand hinzu. Bei einem Punkt gehen wir in
den Zustand F d , also erwarten die erste Ziffer der fraction (würden wir hier
Implementierung Endlicher Automaten
in F d ,e übergehen, wären auch Zahlen der Form 12.E10 erlaubt). Bei einem
e oder E gehen wir in Zustand E s,d (STATE_Exponent). In den beiden letzten
Loop-and-Switch …
Fällen dienen die Zeichen nur als Trennzeichen und werden nicht zur aktu-
ellen Repräsentation hinzugefügt.
1 case STATE_SignifDigit_Fraction_Exponent:
if(Character.isDigit(c)) {
3 state = STATE_SignifDigit_Fraction_Exponent;
significand.addDigit(c);
5 }
else if(c == ’.’) {
7 state = STATE_FractionDigit;
}
9 else if(c == ’e’ || c == ’E’) {
state = STATE_Exponent;
11 }
else state = STATE_Failure;
13 break;
PlainFPParser
Loop-and-Lookup: realisiert in MatrixFPParser. Die Idee von loop-and-lookup Im-
plementierungen ist, in der Schleife über die Eingabezeichen mit Hilfe ei- 27
ner (konstanten) Lookup-Tabelle den Folgezustand (und eventuelle Berech-
36. nungsschritte) zu spezifizieren. In der Lookup-Tabelle sind den Zuständen
Zeilen, den Eingabezeichen Spalten zugeordnet.
Implementierung Endlicher Automaten
Die Lookup-Tabelle findet sich in MatrixFPParser#stateMatrix und ist eine
ziemlich direkte Umsetzung der δ Übergangsfunktion des Automaten. Für
Loop-and-Lookup …
den gleichen Zustand wie oben ergibt sich z.B. folgender Eintrag in der Ma-
trix:
1 // STATE_SignifDigit_Fraction_Exponent
{ // (STATE_SignifDigit_Fraction_Exponent, SCLASS_Digit)
3 new DigitEffect(STATE_SignifDigit_Fraction_Exponent,←
significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Sign)
5 new Effect(STATE_Failure, significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Expn)
7 new Effect(STATE_Exponent, significand),
// (STATE_Initial, SCLASS_Period)
9 new Effect(STATE_FractionDigit, significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Rest)
11 new Effect(STATE_Failure, significand)
},
In jeder Zelle der Lookup-Tabelle findet sich ein Effect Objekt, dass
den nächsten Zustand und die auszuführenden Berechnungsschritte
enthält. Um die Berechnungsschritte zu paramerterisieren verwendet 29
MatrixFPParser Funktionsobjekte (wo Sprachen wie C++ Funktionspointer
37. nungsschritte) zu spezifizieren. In der Lookup-Tabelle sind den Zuständen
Zeilen, den Eingabezeichen Spalten zugeordnet.
Implementierung Endlicher Automaten
Die Lookup-Tabelle findet sich in MatrixFPParser#stateMatrix und ist eine
ziemlich direkte Umsetzung der δ Übergangsfunktion des Automaten. Für
Loop-and-Lookup …
den gleichen Zustand wie oben ergibt sich z.B. folgender Eintrag in der Ma-
trix:
1 // STATE_SignifDigit_Fraction_Exponent
{ // (STATE_SignifDigit_Fraction_Exponent, SCLASS_Digit)
3 new DigitEffect(STATE_SignifDigit_Fraction_Exponent,←
significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Sign)
5 new Effect(STATE_Failure, significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Expn)
7 new Effect(STATE_Exponent, significand),
// (STATE_Initial, SCLASS_Period)
9 new Effect(STATE_FractionDigit, significand),
// (STATE_SignifDigit_Fraction_Exponent, SCLASS_Rest)
11 new Effect(STATE_Failure, significand)
},
In jeder Zelle der Lookup-Tabelle findet sich MatrixFPParser
ein Effect Objekt, dass
den nächsten Zustand und die auszuführenden Berechnungsschritte
enthält. Um die Berechnungsschritte zu paramerterisieren verwendet 29
MatrixFPParser Funktionsobjekte (wo Sprachen wie C++ Funktionspointer
49. 17 ;
19 atom
Arithmetik & Parsergeneratoren
: INT
|ID
Lexer-Regeln der Grammatik
21
|’(’ expr ’)’
23 ;
25 /*------------------------------------------------------------------
* LEXER RULES
27 *------------------------------------------------------------------*/
29 ID : (’a’..’z’|’A’..’Z’)+ ;
INT : ’0’..’9’+ ;
31 NEWLINE:’r’? ’n’ ;
WS : (’ ’|’t’)+ {skip();} ;
Was ist die Aufgabe eines Lexers?
Beispiel: -666.797E-14
Zum Ausprobieren der Parser einfach folgendem Rezept folgen:
41
• FPParser.jar herunterladen und mit
50. direkte Ausgaben,
Fehler
Lexer Parser
Character Token Abstract
Strom Strom Syntax Tree
(auch Scanner,
Tokenizer)
Struktur eines ANTLR-Parser
Lexer, Parser, AST, Token vs. Character-Strom
42
51. w i d t h = 2 0 0 ; n Characters
ID = INT ; Tokens
WS WS WS
Von Characters zu Tokens
Aufgabe des Lexers
43
52. void multExpr() {
try {
atom();
while( <<next input symbol is '*' >> ) {
match('*');
atom();
}
}
[... error handling]
}
void atom() {
try {
int alt=3;
switch (<< next input symbol >>) {
case INT: alt = 1; break;
case ID: alt = 2; break;
case '(': alt = 3; break;
default: [error]
}
switch(alt){
case 1: match(INT); break;
case 2: match(ID); break;
case 3: match('('); expr(); match(')'); break;
}
Parser.java
Was hinten herauskommt …
}
[... error handling]
}
44
55. grammar IntExpr_Var;
options {
language = Java;
superClass = RunnableParser; }
@lexer::header{
package de.lmu.ifi.pms.parsergenerators; }
@header{
package de.lmu.ifi.pms.parsergenerators;
import java.util.Set;
import java.util.HashSet; }
@members {
/** Set for maintaining already declared variables. */
Set declaredVariables = new HashSet();
public static RunnableParser getParserInstance(ANTLRStringStream stream){
[...]
}
public void run(ASTProcessor... processors){
try {
Prolog
prog();
} catch (Exception e) [...]
}
} 46
56. prog: stat+ ;
stat: // Semantic action for printing out the value returned by the evaluation of each expression
expr NEWLINE
{ System.out.println(quot;Expression in line quot; +
input.get(input.index()-1).getLine() + quot;: quot; + $expr.value);}
// Semantic action for constructing the set of declared variables.
| ID '=' expr NEWLINE
{ varValues.put($ID.text, new Integer($expr.value)); }
| NEWLINE
;
// All rules of the grammar get a return value, the computed value of their matching expression
expr returns [int value]
: e=multExpr {$value = $e.value;}
// We need to distinguish addition and subtraction
( '+' e=multExpr {$value += $e.value;}
| '-' e=multExpr {$value -= $e.value;} )*
;
multExpr returns [int value]
: e=atom {$value = $e.value;} ('*' e=atom {$value *= $e.value;})*
;
atom returns [int value]
: INT {$value = Integer.parseInt($INT.text);}
| ID
{ Integer v = varValues.get($ID.text);
if ( v!=null ) $value = v.intValue();
else throw new FailedPredicateException(input,
quot;atomquot;, quot;undeclared variable quot;+$ID.text); }
| '(' expr ')' {$value = $expr.value;}
47
;
57. prog: stat+ ;
stat: // Semantic action for printing out the value returned by the evaluation of each expression
expr NEWLINE
{ System.out.println(quot;Expression in line quot; +
input.get(input.index()-1).getLine() + quot;: quot; + $expr.value);}
// Semantic action for constructing the set of declared variables.
| ID '=' expr NEWLINE
{ varValues.put($ID.text, new Integer($expr.value)); }
| NEWLINE
;
// All rules of the grammar get a return value, the computed value of their matching expression
expr returns [int value]
: e=multExpr {$value = $e.value;}
// We need to distinguish addition and subtraction
( '+' e=multExpr {$value += $e.value;}
| '-' e=multExpr {$value -= $e.value;} )*
;
multExpr returns [int value]
: e=atom {$value = $e.value;} ('*' e=atom {$value *= $e.value;})*
;
atom returns [int value]
: INT {$value = Integer.parseInt($INT.text);}
| ID
{ Integer v = varValues.get($ID.text);
if ( v!=null ) $value = v.intValue();
else throw new FailedPredicateException(input,
quot;atomquot;, quot;undeclared variable quot;+$ID.text); }
| '(' expr ')' {$value = $expr.value;}
47
;
58. public int expr() {
int value = 0; // return value
int e = 0;
try {
e = multExpr();
value = e;
loop3: while(true) {
int alt = 3;
if (<<next input symbol is '+'>>) alt = 1;
else if (<<next input symbol is '-'>>) alt = 2;
switch(alt) {
case 1: match('+');
e = multExpr();
value += e;
break;
case 2: [...]
}
}
}
}
[...] Parser + Aktionen
Was hinten herauskommt …
48
60. Jenseits von Syntax
Semantische Analyse
Trennung syntaktische / semantische Analyse
effiziente, kontext-freie Parser für syntaktische Analyse
Semantische Analyse
volle Programmiersprache mittels Traversierung/Transformation des AST
aber: schwer, oft weitgehend unabhängig von Sprache, ineffizient
Attribute Grammars (Attributgrammatiken), SDDs
Formalismus zur Einbettung der semantischen in die syntaktische Analyse
Anreicherung des ASTs durch “semantische” Attribute
in der Praxis: oft auch Seiteneffekte
50
64. /quot;0quot;%1quot;%(2)34$05
9 + # 9'+F,(-7%,8'D%8%7,9
F'.,-(%,.8-),0-6,)F%,>'8
9! A 9H '))6*D3)%
# L-)%,)F%,7%/%(7%(+*%.
*()B 5 J 9< + K
9@ A
9B
*()H 2 *()< 3
Prof. Aiken CS 143 Lecture 6 HI
52
65. /quot;0quot;%1quot;%(2)34$05
9 + # 9'+F,(-7%,8'D%8%7,9
F'.,-(%,.8-),0-6,)F%,>'8
9! A 9H '))6*D3)%
# L-)%,)F%,7%/%(7%(+*%.
*()B 5 J 9< + K
/quot;0quot;%1quot;%(2)34$05
9@ A
9B 9
+-&/3)%7,'0)%6,'88,*)., 10
%(7%(+1,N6'/F,F'>%,D%%(,
*() 2 H *()< 3 9! 5 A 9H 5
)6*D3)%.,+'(,D%,+-&/3)%7, Prof. Aiken CS 143 Lecture 6 HI
*()B 5 J 9< 5 K
QF%(,)F%6%,'6%,(-,+1+8%. 9@ 2 A
9B 3
D3)%.,'6%,(-),8%N'8
*()H 2 *()< 3
52
143 Lecture 6 H! Prof. Aiken CS 143 Lecture 6
66. Jenseits von Syntax
Attribute Grammars
Attribute grammar :=
kontextfreie Grammatik mit Attributen und Regeln
Attribute zu Symbolen, Regeln zu Produktionen
S.a bezeichnet das Attribut ‘a’ des Symbols ‘S’
Synthetisiertes Attribut (“Rückgabewerte) a von S
definiert nur unter Verwendung von Attributen von S und Kinder von S
Vererbtes Attribut (“Parameter”) b von S
definiert nur unter Verwendung von Attributen von S, des Vaters von S
und der Geschwister von S
53
67. Jenseits von Syntax
Attribute Grammars
Ergebnis: Gleichungssystem
Auswertungsordnung ist nicht fixiert
e.g., E3.val = E4.val + E5.val
E4.val und E5.val nötig um E3.val zu berechnen (dependency)
aber: ob erst E4.val oder E5.val berechnet wird ist offen
Parser muß Auswertungsordnung festlegen
Problem: zyklische Abhängigkeiten
EXPTIME: für eine gegebene Grammatik feststellen, ob Abhängigkeiten
für alle Parsebäume zyklenfrei sind.
daher: Einschränkung von Attribute Grammars
54
68. Jenseits von Syntax
Attribute Grammars
S-attributed attribute grammar
entählt ausschließlich synthetisierte Attribute
L-attributed attribute grammar: alle Attribute entweder
synthetisiert oder
vererbt, aber dann, für Regel A ::= X1 X2 … Xn
Berechnung von Xi.a nur durch
vererbte Attribute von A oder
(synthetisierte oder vererbte) Attribute von Xj fuer j < i oder
(synthetisierte oder vererbte) Attribute von Xi selbst falls nicht abhängig von a
verwendet vor allem in Recursive Descent Parsern wie ANTLR
55
72. Zusammenfassung
Parsergeneratoren
1. Implementierung von Endlichen Automaten
loop-and-switch, loop-and-lookup, Lexer,
Automatenbibliotheken
manuelle Implementierung oft in Bibliotheken und bei zeitkritischem
Parsing (Basispaarsequenzen) verwendet
Lexer erlauben auch komplexe endliche Automaten
ohne signifikanten Effizienzverlust
Automatenbibliotheken
ineffizienter als Lexer oder manuelle Implementierung
aber: Konstruktion und Manipulation der Automaten zur Laufzeit möglich
58
73. Zusammenfassung
Parsergeneratoren
2. Implementierung von Kellerautomaten
Parsergeneratoren
manuelle Implementierung meist zu aufwendig
Parsergeneratoren erzeugen Lexer und Parser
Mehr als kontextfreie Sprachen dank semantischer Aktionen
kontext-sensitive Eigenschaften “von Hand” programmieren
Recursive-descent oder LL(k)/LL(*) Parser wie ANTLR
einfach zu lesender Code, leichte manuelle Anpassung
Andere verbreitete Ansätze: LR, LALR, Left-Corner, Earley
59
74. Zusammenfassung
Parsergeneratoren
Beispielprogramme + Dokumentation
im Laufe der Woche auf der Webseite
Mehr Details zu Attributgrammatiken in den Übungen
Kapitel 5, Drachenbuch (“Compilers …”, Aho et al., 2007)
Selber ausprobieren!
60