Stand der Folien: SoSe 2011 Praktikum Funktionale Programmierung Teil 1: Lexen und Parsen Dr. David Sabel Sommersemester 2011
Übersicht FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 2/20
Übersicht FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 2/20
Übersicht FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 2/20
Übersicht Bearbeitungszeit: 3 Wochen FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 2/20
Der Lexer Format des Quelltexts: Zwei Teile: 1 Datentypdefinitionen (optional, kann auch fehlen) 2 Schlüsselwort expression gefolgt von einem Ausdruck FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 3/20
Erster Teil: Datentypdefinitionen data Typname =Konstruktordefinition 1... Konstruktordefinition n Konstruktordefinition ist von der Form: KonstruktorName 1 Argumenttyp 1... Argumenttyp m KonstruktorName und Typname müssen mit einem Großbuchstaben beginnen und dürfen nur Buchstaben enthalten. Argumenttypen entsprechend der Grammatik: Typ ::= (Typ -> Typ) Typname Syntaktische Einheiten: data, =, (, ), ->, sowie Namen der Typen- und Datenkonstruktoren Der Parser (!) prüft noch: Alle Konstruktornamen und Typnamen müssen verschieden sein, alle verwendeten Typnamen müssen definiert sein Ausnahme: Unit, wird automatisch eingefügt: data Unit = Unit FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 4/20
Zweiter Teil: Ausdruck Eingeleitet mit dem Schlüsselwort expression Wie in der Syntax, jedoch -> statt, \ statt λ. Semikolons und {, } müssen vorhanden sein beliebige ( und )-Klammern case statt case T Variablennamen: Beginnen mit Kleinbuchstaben und bestehen aus Buchstaben und Zahlen Syntaktische Einheiten: expression, case, of, letrec, in, seq, newmvar, putmvar, takemvar, return, forkio, \, >>=, ;, =, ->, (, ), {, } und Variablennamen. FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 5/20
Kommentare Im Quelltext sind überall Zeilenkommentare erlaubt Syntax: -- Kommentar bis zum Zeilende FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 6/20
Ausgabe des Lexer Liste von Token In CHF.Parse.Lexer ist der Typ für Token definiert > data Token label = TokenKeyword String label > TokenSymbol String label > TokenVar String label > TokenCons String label > TokenUnknown Char label > type TokenType = Token SourceMark > type SourceMark = (Int,Int) -- (Zeile, Spalte) TokenKeyword: gefundenes Schlüsselwort TokenSymbol: gefundenes Symbol TokenVar: gefundene Variable TokenCons: gefundener Daten- oder Typkonstruktor TokenUnknown: unbekanntes Zeichen im Quelltext Zweites Argument label gibt die Position im Quelltext als SourceMark an. FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 7/20
Aufgabe 1 lexchf :: String -> [TokenType] implementieren getlabel :: Token label -> label implementieren showtoken :: Token label -> String implementieren *CHF.Parse.Lexer> lexchf "data Bool = True False expression \\x -> x" [TokenKeyword "data" (1,1),TokenCons "Bool" (1,6),TokenSymbol "=" (1,11), TokenCons "True" (1,13),TokenSymbol " " (1,18),TokenCons "False" (1,20), TokenKeyword "expression" (1,26),TokenSymbol "\\" (1,37), TokenVar "x" (1,38),TokenSymbol "->" (1,40),TokenVar "x" (1,43)] *CHF.Parse.Lexer> lexchf "expression 1+1" [TokenKeyword "expression" (1,1),TokenUnknown 1 (1,12), TokenUnknown + (1,13),TokenUnknown 1 (1,14)] *CHF.Parse.Lexer> lexchf "expression (return True) >>= \\x -> putmvar x True" [TokenKeyword "expression" (1,1),TokenSymbol "(" (1,12), TokenKeyword "return" (1,13),TokenCons "True" (1,20),TokenSymbol ")" (1,24), TokenSymbol ">>=" (1,26),TokenSymbol "\\" (1,30),TokenVar "x" (1,31), TokenSymbol "->" (1,33),TokenKeyword "putmvar" (1,36),TokenVar "x" (1,44), TokenCons "True" (1,46)] *CHF.Parse.Lexer> getlabel (TokenVar "x" (1,38)) (1,38) *CHF.Parse.Lexer> showtoken (TokenVar "x" (1,38)) "x" *CHF.Parse.Lexer> showtoken (TokenKeyword "letrec" (20,20)) "letrec" *CHF.Parse.Lexer> getlabel (TokenKeyword "letrec" (20,20)) (20,20) FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 8/20
Parsen mit Happy Ziel des Parsens: Tokenstrom in Datentyp (Parsebaum) verwandeln Dabei syntaktische Fehler entdecken Tool: happy, Parsergenerator, erstellt automatisch einen Parser Größtenteils bereits vorgegeben. Happy erstellt CHF.Parse.Parser, beinhaltet: parsechf :: String -> ParserOutput parse :: [TokenType] -> ParserOutput FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 9/20
Ausgabe des Parsers > type ParserOutput = ParseTree () ConsName VarName > data ParseTree a cname vname = > ParseTree [TDef cname cname] (Expr a cname vname) > deriving(eq,show) > type ConsName = (SourceMark,String) > type VarName = (SourceMark,String) FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 10/20
Darstellung von Ausdrücken > data Expr label cname v = > Var v > Lam v (Expr label cname v) > App (Expr label cname v) (Expr label cname v) > Seq (Expr label cname v) (Expr label cname v) > Cons (Either MAction cname) [Expr label cname v] > Case (Expr label cname v) [CAlt label cname v] > Letrec [Binding label cname v] (Expr label cname v) > Label label (Expr label cname v) > data MAction = Fork Return Take Put New Bind > data CAlt label cname v = > CAlt cname [v] (Expr label cname v) > data Binding label cname v = > v :=: (Expr label cname v) FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 11/20
Darstellung der Datentyp-Definitionen > data TDef tname cname = TDef tname [(cname,type tname)] > data Type tname = > TC tname > (Type tname) :->: (Type tname) > TVar String > TIO (Type tname) > TMVar (Type tname) FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 12/20
Beispiel Eingabe: data Bool = True False data ListBool = NilBool ConsBool Bool ListBool expression (seq True True) Ausgabe: ParseTree [TDef ((1,6),"Bool") [(((1,13),"True"),TC ((1,6),"Bool")), (((2,13),"False"),TC ((1,6),"Bool"))], TDef ((3,6),"ListBool") [(((3,17),"NilBool"),TC ((3,6),"ListBool")), (((4,17),"ConsBool"), TC ((4,26),"Bool") :->: (TC ((4,31),"ListBool") :->: TC ((3,6),"ListBool")))], TDef ((1,6),"Unit") [(((1,13),"Unit"),TC ((1,6),"Unit"))] ] (Seq (Cons (Right ((7,6),"True")) []) (Cons (Right ((7,11),"True")) []) ) FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 13/20
Die Parserdefinition (Auszüge) (1) > %token > var { TokenVar } > cons { TokenCons } > { TokenSymbol " " _ } > \\ { TokenSymbol "\\" _ } > >>= { TokenSymbol ">>=" _ } > ; { TokenSymbol ";" _ } > = { TokenSymbol "=" _ } > -> { TokenSymbol "->" _ } > ( { TokenSymbol "(" _ } > { { TokenSymbol "{" _ } > ) { TokenSymbol ")" _ } > } { TokenSymbol "}" _ } > case { TokenKeyword "case" _ } > of { TokenKeyword "of" _ } > letrec { TokenKeyword "letrec" _ } > in { TokenKeyword "in" _ } > seq { TokenKeyword "seq" _ } > newmvar { TokenKeyword "newmvar" _ } > putmvar { TokenKeyword "putmvar" _ } > takemvar { TokenKeyword "takemvar" _ } > forkio { TokenKeyword "forkio" _} > return { TokenKeyword "return" _ } > data { TokenKeyword "data" _ } > expr { TokenKeyword "expression" _ } FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 14/20
Die Parserdefinition (Auszüge) (2) > %right -> > %right >>= > %left seq > %nonassoc in > %nonassoc of > %nonassoc case > %nonassoc letrec > %nonassoc forkio > %nonassoc takemvar > %nonassoc putmvar > %nonassoc return > %nonassoc newmvar > %% FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 15/20
Die Parserdefinition (Auszüge) (3) > SOURCECODE :: {ParserOutput} > SOURCECODE : EXPR { checkparsetree $ ParseTree [] $1 } > TDEFS EXPR { checkparsetree $ ParseTree $1 $2 } Grammatik f"ur die Datentypdefinitionen: > TDEFS : TDEFSIT { checktdefs $1 } > TDEFSIT : TDEF { [$1] } > TDEF TDEFSIT { $1:$2 } > TDEF : data cons = CDEFS { (TDef > (mkcons $2) > (liststotypes $4 (TC $ mkcons $2))) } > CDEFS : CDEF { [$1] } > CDEF CDEFS { $1:$3 } > CDEF : cons { (mkcons $1,[]) } > cons TYPE { (mkcons $1,$2) } > TYPE : TK { [$1] } > TK TYPE { $1:$2 } > TK : cons { TC $ mkcons $1 } > ( TK -> TK ) { $2 :->: $4 } FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 16/20
Die Parserdefinition (Auszüge) (4) Grammatik f"ur Ausdr"ucke: > EXPR : expr REXPR { $2 } > REXPR : letrec BINDS in REXPR { checkbinds $4 $2 $1 } > \\ var -> REXPR { Lam (mkvar $2) $4 } > REXPR >>= REXPR { Cons (Left Bind) [$1,$3] } > AEXPR { $1 } > AEXPR : IEXPR { $1 } > cons CONSARGS { Cons (Right (mkcons $1)) $2 } > IEXPR CONSARGS { mkapp $1 $2} > cons { Cons (Right (mkcons $1)) [] } > BINDS : BIND { [$1] } > BIND ; BINDS { $1:$3 } > BIND : var = REXPR { (mkvar $1) :=: $3 } > IEXPR : seq IEXPR IEXPR { Seq $2 $3}... > forkio IEXPR { Cons (Left Fork) [$2] } > ( REXPR ) { $2 } > var { Var (mkvar $1) } > case REXPR of { ALTS } { checkalts $1 $2 $5 } > ALTS : ALT { [$1] } > ALT ; ALTS { $1:$3 } > ALT : PAT -> REXPR { checkalt $2 (CAlt (fst $1) (snd $1) $3) } > PAT : cons { (mkcons $1,[]) } > ( cons VARARGS ) { (mkcons $2,$3) }... FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 17/20
Aufgabe 2 Implementieren: checkalt :: TokenType -> CAlt () ConsName VarName -> CAlt () ConsName VarName checkalts :: TokenType -> (Expr () ConsName VarName) -> [CAlt () ConsName VarName] -> (Expr () ConsName VarName) checkbinds :: Expr () ConsName VarName -> [Binding () ConsName VarName] -> TokenType -> Expr () ConsName VarName FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 18/20
Aufgabe 3 Implementieren Sie in der Parser-Definition Parser.ly eine Funktion: checkparsetree :: ParserOutput -> ParserOutput, die den geparsten Ausdruck rekursiv durchläuft und für alle dort auftretenden Konstruktoren (auch in den Pattern der case-alternativen) prüft, ob die Konstruktornamen in den Typdefinitionen (erste Komponente von ParseTree) definiert werden. Ist dies der Fall, so wird der ParseTree unverändert zurückgegeben. Anderenfalls soll eine aussagekräftige Fehlermeldung (mit Position des falschen Namens) generiert werden. Ein Beispiel ist: parsechf "expression True" *** Exception: Constructor "True" not defined Zeile: 1 Spalte: 12 FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 19/20
Aufgabe 4 Erstellen Sie mit happy den Parser, indem Sie happy -iinfo.txt Parser.ly aufrufen. Dies erstellt die Haskell-Quelltext-Datei Parser.hs sowie eine Datei info.txt die Auskunft über die Zustände des erstellten Shift-Reduce-Parsers gibt. Schauen Sie sich den Inhalt von info.txt an, um die Wirkungsweise des Parsers (etwas) zu verstehen. Testen Sie anschließend den Parser mit einigen Eingaben. Welche Ausdrücke können geparst werden? Welche Klammern kann der Benutzer weglassen, welche nicht? FP-PR Teil 1: Lexen & Parsen Sommersemester 2011 D. Sabel 20/20