Syntaxanalys Douglas Wikström KTH Stockholm popup-help@csc.kth.se
Reguljära uttryck Reguljära uttryck förutsätter att en mängd bokstäver är givna, ett så kallat alfabet, som oftast betecknas med Σ. Uttryck formas enligt: ǫ a Σ R 1 R 2 R 1 R 2 R tomma strängen bokstav konkatenering union (alternativ) Kleene-slutning (noll eller fler konkatenerade) Varje reguljärt uttryck definierar en mängd strängar, ett så kallat språk.
Reguljära uttryck Syntaktiskt socker: en eller fler konkatenerade R? noll eller en [abc] någon bokstav i mängden {a,b,c} [a z] någon bokstav i mängden {a,b,...,z} [ˆabc] någon bokstav i mängden Σ {a, b, c} R +
Reguljära uttryck i praktiken C Gnu C Library (saknas på Kattis och på tävlingar) C++ Boost regex (saknas på Kattis och på tävlingar) Java API, java.util.regex.* Finns på Kattis och normalt på tävlingar där Java används.
Exempel i Java import java. u t i l. regex. ; public class RegexpExample { public static void main( String [ ] args ) throws ParseException { Pattern pattern = Pattern. compile ( ( [A Z ] [ a z ]?) + (\ \() + (\ \)) + ([1 9][0 9] ) + (\\ $) ) ; Matcher m = pattern. matcher ( args [ 0 ] ) ; } } int pos = 0; while (m. find ( pos )) { System. out. p r i n t l n (m. group ( ) ) ; pos = m. end ( ) ; }
Utvidgad Backus-Naur form (EBNF) En grammatik är en tupel (Σ,N, start,r) där: Σ = {a 1,...,a s } alfabet av slutsymboler inklusive tomma strängen. N = {n 1,...,n t } mängd av icke-slutsymboler. start är en startsymbol. För varje icke-slutsymbol a innehåller R precis en regel a ::= β, där β ([a 1 a n ] [n 1 n t ]) + ( ([a 1 a n ] [n 1 n t ]) + ) Mängden strängar i språket skapas genom att stegvis ersätta varje icke-slutsymbol med något av de alternativ som separeras med i dess produktionsregel.
Exempel: molekylformler C12H22O11 (socker), C60 (Buckyboll) Slutsymboler: atom (en atomförkortning), num (ett positivt heltal), lpar (vänsterparentes), rpar (högerparentes). Produktionsregler: start ::= molecule $ molecule ::= part part molecule part ::= simple simple num simple ::= atom lpar molecule rpar
Rekursiv nedåkning Skriv en metod för varje icke-slutsymbol i grammatiken, och låt metoderna äta upp indata genom att anropa varandra. Bestäm vilken metod som skall anropas vid varje tillfälle genom att tjuvkika vad som kommer härnäst i indata (gör rekursiv sökning med backtracking i allmänhet). Förutsätter att endast begränsad sökning krävs för att avgöra vilket alternativ i en regel som skall tillämpas.
Exempel: lexikal analys Från bokstäver till slutsymboler Indata i vårt molekylexempel erhålls som en följd bokstäver, så innan vi börjar måste de först översättas till en följd av slutsymboler. Detta görs till exempel med reguljära uttryck. Vi lagrar vilken slutsymbol en delsträng svarar mot, och strängens värde, tex atomnummer eller positivt heltal
Exempel: reguljära uttryck för slutsymboler i Java Pattern pattern = Pattern. compile ( ( [A Z ] [ a z ]?) + (\\() + (\\)) + ([1 9][0 9] ) + (\\ $) ) ; Matcher m = pattern. matcher ( s t r ) ; Vi använder Javas escape-sekvens för att skicka ett enkelt \ till Pattern-klassen. Den förväntar sig i sin tur att vi använder en escape-sekvens för ( och $ eftersom dessa har särskild mening i reguljära uttryck.
Exempel: syntaktisk analys Input: Sträng s. Output: Syntaxträd. Parser(s) (1) t Tokenizer(s) (2) return start(t) start ::= molecule $ (regel att översätta) start(t) (1) m molecule(t) (2) d peek(t) (3) if d = $ (4) return Node(m,$) (5) else (6) error()
Exempel: syntaktisk analys molecule ::= part part molecule (regel att översätta) molecule(t) (1) p part(t) (2) d peek(t) (3) if d {atom,lpar} (4) return Node(p,molecule(t)) (5) else (6) return Node(p, )
Exempel: syntaktisk analys part ::= simple simple num (regel att översätta) part(t) (1) s simple(t) (2) d peek(t) (3) if d = num (4) next(t) (5) return Node(s,d) (6) else (7) return Node(s, )
Exempel: syntaktisk analys simple ::= atom lpar molecule rpar (regel att översätta) simple(t) (1) d peek(t) (2) if d = atom (3) next(t) (4) return Node(, d, ) (5) else if d = lpar (6) next(t) (7) m molecule(t) (8) d peek(t) (9) if d = rpar (10) next() (11) return Node(lpar, m, rpar)) (12) else (13) error() Popup(14) 2008 else 27 oktober 2008
Kontextfria grammatiker och Chomskys normalform En grammatik är kontextfri om varje produktionsregel kan tillämpas utan hänsyn till närliggande symboler. Om en grammatik är kontextfri kan den skrivas om Chomskys normalform, dvs alla regler är på någon av formerna: a ::= b c a ::= x där a är icke-slutsymboler b och x är en slutsymbol (det finns då flera produktionsregler med samma vänsterled).
Parsning av grammatik på Chomskys normalform (CYK-algoritmen) Vi skall svara på om x 0,...,x n 1 ligger i språket. Vi använder dynamisk programmering! Vi bygger en boolesk tabell (p j,i,k ). p j,i,k är sann omm a j kan generera x i,...,x i+(k 1). Vi har rekursivt samband: p j,i,k = true det finns en regel a j ::= a s a t och ett 1 r k 1 så att p s,i,r = true och p t,i+r,k r = true.
CYK-algoritmen CYK() (1) P[j,i,k] false i,j,k (2) if X i = b and regel a j ::=b (3) P[j,i,1] true (4) for k = 2 to n + 1 (5) for i = 0 to n 1 (6) for r = 1 to k 1 (7) if regel a j ::= a s a t så att P[s,i,r] and P[t,i + r,k r] (8) P[j,i,k] true (9) return P[0, 1, n]