Sei sulla pagina 1di 63

Construccin de compiladores con Haskell

Jos Mara Carmona Cejudo Briseida Sarasola Gutirrez

ndice

Motivacin Qu es un compilador?

Historia Esquema de un compilador


Anlisis mondico Alex Happy (Frown) Parsec

Tcnicas en Haskell estndar

Herramientas software

Qu podemos concluir? Bibliografa

Qu es un compilador?

Programa que traduce texto escrito en un lenguaje de programacin (cdigo fuente) a otro (cdigo objeto). Cdigo fuente escrito en un lenguaje de alto nivel (Haskell, Java, C++), que queremos pasar a un lenguaje de bajo nivel (ensamblador, lenguaje mquina).

Un poco de historia (I)


En principio, se programaba en cdigo binario. Aos 40: Se crean mnemotcnicos para las operaciones binarias, usando los ordenadores para traducirlos a cdigo mquina. Aos 50: Nacen lenguajes de alto nivel, para crear programas ms independientes de la mquina. Primer compilador: Fortran, 1.957. Equipo de J. Backus, de IBM.

Un poco de historia (II)

Aos 60: Se establecen muchos de los principios del diseo de compiladores. An se suelen programar en ensamblador Aos 70: Se usan lenguajes de alto nivel, como Pascal y C. Otros tipos: intrpretes (realizan el proceso sentencia a sentencia). Programas resultantes ms lentos, pero ms fciles de depurar.

Esquema de un compilador

Dos fases

Anlisis: se lee el programa fuente y se estudia la estructura y el significado del mismo. Sntesis: se genera el programa objeto.

Otros elementos: tabla de smbolos, rutinas de tratamiento de errores, etc.

Esquema de un compilador

Dos fases

Anlisis: se lee el programa fuente y se estudia la estructura y el significado del mismo. Sntesis: se genera el programa objeto.

Otros elementos: tabla de smbolos, rutinas de tratamiento de errores, etc.

Esquema de un compilador

Dos fases

Anlisis: se lee el programa fuente y se estudia la estructura y el significado del mismo. Sntesis: se genera el programa objeto.

Otros elementos: tabla de smbolos, rutinas de tratamiento de errores, etc.

Fase de anlisis

Tres fases

Anlisis lxico Anlisis sintctico

Anlisis semntico

Fase de anlisis

Tres fases

Anlisis lxico:

identificar smbolos, eliminar separadores, eliminar comentarios, crear smbolos de entrada al anlisis sintctico (tokens), descubrir errores.

Anlisis sintctico Anlisis semntico

Fase de anlisis

Tres fases

Anlisis lxico Anlisis sintctico:

comprobar que las sentencias que componen el texto fuente son correctas en el lenguaje, creando una representacin interna que corresponde a la sentencia analizada.

Anlisis semntico

Fase de anlisis

Tres fases

Anlisis lxico Anlisis sintctico Anlisis semntico:

Se ocupa de analizar si la sentencia tiene algn significado. Incluye anlisis de tipos, o en general, sentencias que carecen se sentido.

Anlisis lxico en Haskell

Pretendemos reconocer expresiones regulares, que pueden ser reconocidas por un autmata finito determinista (AFD). Implementacin de los estados del AFD
f :: String -> (String, Token)

Implementacin de transicin de A a B: la funcin fA llama a fB despus de leer un carcter y pasarle el resto a fB.

Anlisis lxico en Haskell

Ejemplo:

Anlisis lxico en Haskell

Ejemplos

funciones analizadoras simples

xito :: a -> ReadS a xito x = \s -> [(x, s)] psilon :: ReadS () psilon = xito () fallo :: ReadS a fallo = \s -> []

Alternativa:

infixl 5 -+(-+-) :: ReadS a -> ReadS a -> ReadS a p1 -+- p2 = \s -> p1 s ++ p2 s

Lectura condicional del primer carcter

rSat :: (Char -> Bool) -> ReadS Char rSat p = \s -> case s of [] -> [] x:xs -> if p x then [(x,xs)] else [] MAIN> rSat isUpper ABC [(A, BC)]

Anlisis lxico en Haskell

Ejemplos

combinacin de analizadores para conseguir uno ms complejo (parser combinator)

7 &>< (&><) :: ReadS a -> ReadS b -> ReadS (a,b) p1 &>< p2 = \s -> [ ((x1,x2),s2) | (x1,s1) <- p1 s, (x2,s2) <- p2 s1 ]
infixl

MAIN> (rChar a &>< rChar b) abcd [((a, b), cd)]

Anlisis sintctico en Haskell

En un lenguaje funcional como Haskell, es fcil traducir las reglas gramaticales directamente a especificacin funcional.

exp -> term rest rest -> + exp | epsilon

exp = term <*> rest rest = token AddOp <*> exp <|> epsilon

Anlisis sintctico en Haskell

El paradigma funcional nos da una expresividad a la hora de representar reglas gramaticales impensable en el paradigma imperativo. Ejemplo: funcin many
many :: Parser a b -> Parser a [b]
exp = term <*> many (token addOp <*> term <@ f4) <@ f5

Anlisis sintctico en Haskell

Lo que hemos visto se refiere a anlisis de arriba a abajo. Realizar anlisis de abajo a arriba es ms complejo. Happy es una herramienta que nos facilita la creacin de un analizador abajo a arriba.

Anlisis semntico.

Una vez construido al rbol sintctico, los dems algoritmos se pueden expresar como recorridos en ese rbol. La programacin funcional es muy potente a la hora de realizar recorridos en un rbol, como veremos.

Anlisis semntico.

Atributos de los nodos del rbol:

Se usan para asignar un valor parcial a cada nodo del rbol, para ir calculando, por ejemplo, los valores de una expresin paso a paso. Para calcularlo, necesitamos calcular antes los atributos de los sucesores. Ejemplo: Inferencia de Tipos Se corresponde a un recorrido de abajo a arriba. Funciones de orden superior como foldTree son muy tiles, y nos dan una sencillez y expresividad grandes.

Atributo sintetizado:

Anlisis semntico.

Anlisis semntico.

Atributos heredados.

Su valor ya est calculado, arriba o al mismo nivel en el rbol. Se corresponden a un recorrido de arriba a abajo. Se puede representar mediante una funcin recursiva (posiblemente de cola), acumulando los atributos. Veamos en el rbol anterior cules seran atributos heredados.

Anlisis semntico

Analizadores mondicos

Wadler, en 1995, introdujo el uso de las mnadas para implementar analizadores. Usando el parser combinator &>< que hemos visto, tenemos tuplas anidadas, engorrosas de manipular. La funcin mondica bind (>>=) junto con el uso de lambda-abstracciones nos permite una notacin ms manejable. Adems, podemos usar otros combinadores mondicos.

Analizadores mondicos
Ejemplo: secuencia Como se ha visto en clase, algo bueno de las mnadas es que permiten simular secuenciacin al estilo imperativo:
aplica :: Analiz a -> String ->[(a, Estado)] aplica (AN p) ent = p ent dosElementos::Analiz String dosElementos=do a <- elemento b <- elemento return[a,b] MAIN> aplica dosElementos abcdca [(ab, cdca)] :: [(String, String)]

Analizadores mondicos

Mediante MonadPlus, podemos implementar el concepto de alternancia. Mplus toma dos analizadores, y concatena el resultado de ambos sobre la cadena entrada; mzero falla siempre.
Instance MonadPlus analiz where mplus (AN p)(AN q) = AN(\ent -> p ent ++ q ent) mzero = AN (\ent -> [])

Analizadores mondicos

Tomando (!+) como sinnimo de mplus, podemos construir lo siguiente: elemento !+ dosElementos, que captura un solo carcter, o dos.
unoODosElementos = elemento !+ dosElementos > aplica unoODosElementos "abcdca" [("a","bcdca"),("ab","cdca")]

Otro ejemplo: filtros

(!>) ::Analiz a -> (a -> Bool) -> Analiz a k !> p = do a <- k if p a then return a else mzero

Analizadores mondicos

Reconocimiento de una letra, o bien de un nmero:


letra::AnalizChar letra=elemento !> isAlpha digito::AnalizChar digito=elemento !> isDigit

letraODigito = letra !+ digito.

Analizadores mondicos

Ejemplo: reconocimiento de expresiones:

term ::= constante | ( term + term ) | ( term / term )

Analizadores mondicos

Ejemplo: reconocimiento de expresiones:


anaConst::AnalizTerm anaConst=do a <- nmero return(Const a) anaSum::AnalizTerm anaSum=do _ <- literal ( u <- term _ <- literal + v <- term _ <- literal ) return(u:+:v) anaDiv::AnalizTerm anaDiv=do _ <- literal ( u <- term _ <- literal / v <- term _ <- literal ) return(u:/:v) term::AnalizTerm term=anaConst !+ anaSum !+ anaDiv

Software especfico

Alex Happy Frown Parsec

Alex

Analizador lxico (Lex). Caractersticas


Basado en expresiones regulares Y en autmatas finitos deterministas (DFAs) Definir

Macros Reglas Contextos Expresiones start

Facilita envoltorios (wrappers)

Alex. Wrappers

basic

El ms simple: dada una cadena, devuelve una lista de Tokens. Da ms funcionalidades (nmero de lnea/columna) El ms flexible Es una plantilla para construir nuestras propias mnadas

posn

monad

gscan

Presente por razones histricas

Alex. Ejemplo
module Main (main) where %wrapper "basic" -- Each action has type :: String -> Token

$digit = 0-9 $alpha = [a-zA-Z] tokens :$white+ ; "--".* ; let \s -> Let in \s -> In $digit+ \s -> Int (read s) [\=\+\-\*\/\(\)] \s -> Sym (head s) $alpha [$alpha $digit \- \']* \s -> Var s

-- The token type: data Token = Let | In | Sym Char | Var String | Int Int deriving (Eq,Show)
main = do s <- getContents print (alexScanTokens s)

Alex. Fichero resultante


-- The token type: data Token = Let | In | Sym Char | Var String | Int Int deriving (Eq,Show) main = do s <- getContents print (alexScanTokens s) alex_action_2 alex_action_3 alex_action_4 alex_action_5 alex_action_6 = = = = = \s \s \s \s \s -> -> -> -> -> Let In Int (read s) Sym (head s) Var s type AlexInput = (Char,String) alexGetChar (_, []) = Nothing alexGetChar (_, c:cs) = Just (c, (c,cs)) alexInputPrevChar (c,_) = c

-- alexScanTokens :: String -> [token] alexScanTokens str = go ('\n',str) where go inp@(_,str) = case alexScan inp 0 of AlexEOF -> [] AlexError _ -> error "lexical error" AlexSkip inp' len -> go inp' AlexToken inp' len act -> act (take len str) : go inp'

Happy

Utiliza anlisis LALR(1). Trabaja en conjuncin con un analizador lxico. Genera distintos tipos de cdigo:

Haskell 98 Haskell estndar con arrays Haskell con extensiones GHC Haskell GHC con arrays codificados como cadenas

Flexibilidad Velocidad

Happy

Utiliza anlisis LALR(1). Trabaja en conjuncin con un analizador lxico. Genera distintos tipos de cdigo:

Haskell 98 Haskell estndar con arrays Haskell con extensiones GHC Haskell GHC con arrays codificados como cadenas

Flexibilidad Velocidad

Happy

Utiliza anlisis LALR(1). Trabaja en conjuncin con un analizador lxico. Genera distintos tipos de cdigo:

Haskell 98 Haskell estndar con arrays Haskell con extensiones GHC Haskell GHC con arrays codificados como cadenas

Flexibilidad Velocidad

Happy

Utiliza anlisis LALR(1). Trabaja en conjuncin con un analizador lxico. Genera distintos tipos de cdigo:

Haskell 98 Haskell estndar con arrays Haskell con extensiones GHC Haskell GHC con arrays codificados como cadenas

Flexibilidad Velocidad

Happy

Utiliza anlisis LALR(1). Trabaja en conjuncin con un analizador lxico. Genera distintos tipos de cdigo:

Haskell 98 Haskell estndar con arrays Haskell con extensiones GHC Haskell GHC con arrays codificados como cadenas

Flexibilidad Velocidad

Happy. Ejemplo
{ module Main where } %name calc %tokentype { Token } %token let in int var '=' '+' '-' '(' ')' %% Exp : let var '=' Exp in Exp { Let $2 $4 $6 } | Exp1 { Exp1 $1 } : Exp1 '+' Term | Exp1 '-' Term | Term : int | var | '(' Exp ')' { E } { Plus $1 $3 } { Minus $1 $3 } { Term $1 } { Int $1 } { Var $1 } { Brack $2 }

Exp1 { { { { { { { { { TokenLet } TokenIn } TokenInt $$ } TokenVar $$ } TokenEq } TokenPlus } TokenMinus } TokenOB } TokenCB }

Term

: t_1 .. t_n

Happy. Ejemplo
{ happyError :: [Token] -> a happyError _ = error "Parse error" data Exp = Let String Exp Exp | Exp1 Exp1 data Exp1 = Plus Exp1 Term | Minus Exp1 Term | Term Term lexer :: String -> [Token] lexer [] = [] lexer (c:cs) | isSpace c = lexer cs | isAlpha c = lexVar (c:cs) | isDigit c = lexNum (c:cs) lexer ('=':cs) = TokenEq : lexer cs lexer ('+':cs) = TokenPlus : lexer cs lexer ('-':cs) = TokenMinus : lexer cs lexer ('(':cs) = TokenOB : lexer cs lexer (')':cs) = TokenCB : lexer cs lexNum cs = TokenInt (read num) : lexer rest where (num,rest) = span isDigit cs lexVar cs = case span isAlpha cs of ("let",rest) -> TokenLet : lexer rest ("in",rest) -> TokenIn : lexer rest (var,rest) -> TokenVar var : lexer rest main = getContents >>= print . calc . lexer }

data Term = Int Int | Var String | Brack Exp


data Token = TokenLet | TokenIn | TokenInt Int | TokenVar String | TokenEq | TokenPlus | deriving Show

Frown

Utiliza anlisis LALR(k) Eficiencia Funcionales

Frown

Utiliza anlisis LALR(k) Eficiencia Funcionales

Frown

Utiliza anlisis LALR(k) Eficiencia Funcionales

Parsec

Es una librera de combinadores mondicos. Se trabaja directamente en Haskell. Est includo en GHC y en Hugs. Es ms eficiente con gramticas LL(1).

Parsec. Un ejemplo

El cdigo
module Main where import Text.ParserCombinators.Parsec simple :: Parser Char simple = letter

ejecuta :: Show a => Parser a -> String -> IO () ejecuta p input = case (parse p "" input) of Left err -> do{ putStr "error al analizar " ; print err } Right x -> print x

Parsec. Un ejemplo

Ejecucin
*Main> ejecuta simple "" Loading package parsec-1.0 ... linking ... done. error al analizar (line 1, column 1): unexpected end of input expecting letter *Main> ejecuta simple "123" error al analizar (line 1, column 1): unexpected "1" expecting letter *Main> ejecuta simple "a" 'a'

Parsec. Otro ejemplo

Cdigo
parens parens :: Parser () = do{ char '(' ; parens ; char ')' ; parens } <|> return ()

Parsec. Otro ejemplo

Ejecucin
Main> ejecuta parens "((()())())()" Reading file "C:\Documents and Settings\Brise\Mis documentos\PDA\parsec.hs": ()
Main> ejecuta parens "(()" error al analizar (line 1, column 4): unexpected end of input expecting "(" or ")"

Qu podemos concluir?

Los LF respetan la estructura en fases: lexing, parsing, anlisis semntico, etc.

Qu podemos concluir?

Los LF respetan la estructura en fases: lexing, parsing, anlisis semntico, etc.

Qu podemos concluir?

Los LF respetan la estructura en fases: lexing, parsing, anlisis semntico, etc.

lexer :: String -> [Token] parser :: [Token] -> rbolAbstracto semntica :: rbolAbstracto -> rbolEtiquetado generacinDeCdigo :: rbolEtiquetado -> [Cdigo mquina] compilador = generacinDeCdigo . semntica . parser . lexer

Qu podemos concluir?

Evaluacin perezosa

La salida del analizador lxico sera una lista perezosa de tokens.

Qu podemos concluir?

Evaluacin perezosa

La salida del analizador lxico sera una lista perezosa de tokens.

lexer :: String -> [Token] parser :: [Token] -> rbolAbstracto semntica :: rbolAbstracto -> rbolEtiquetado generacinDeCdigo :: rbolEtiquetado -> [Cdigo mquina] compilador = generacinDeCdigo . semntica . parser . lexer

Qu podemos concluir?

Tipos de datos

Tokens: tipo enumerado

data Token = Id String | IntConst Int | SumaOp | ProdOp | PuntoYComa

Qu podemos concluir?

Tipos de datos

Tokens: tipo enumerado

data Token = Id String | IntConst Int | SumaOp | ProdOp | PuntoYComa

Tabla de smbolos: rboles AVL o Tablas Hash

Qu podemos concluir?

Tipos de datos

Tokens: tipo enumerado

data Token = Id String | IntConst Int | SumaOp | ProdOp | PuntoYComa


Tabla de smbolos: rboles AVL o Tablas Hash rboles abstractos: tipos mutuamente recursivos

Qu podemos concluir?

Tipos de datos

Tokens: tipo enumerado

data Token = Id String | IntConst Int | SumaOp | ProdOp | PuntoYComa


Tabla de smbolos: rboles AVL o Tablas Hash rboles abstractos: tipos mutuamente recursivos Combinadores de analizadores.

Funciones de orden superior

Qu podemos concluir?

Recursin

La recursin y el reconocimiento de patrones que existen en Haskell son un mtodo potente para tratar este tipo de estructuras recursivas.

Qu podemos concluir?

Recursin

La recursin y el reconocimiento de patrones que existen en Haskell son un mtodo potente para tratar este tipo de estructuras recursivas.

Polimorfismo
data Instr a = Asignar String a (Expr a) | While (Expr a) (Instr a) | If (Expr a) (Instr a) (Instr a) | ...

Bibliografa

Blas C. Ruiz, Francisco Gutirrez, Paco Guerrero, Jos E. Gallardo. Razonando con Haskell. Thomson, Madrid 2004. Cap. 14, Analizadores. Ricardo Pea. Compile Construction in a Functional Setting. Universidad Complutense de Madrid. Jeroen Fokker. Functional Parsers. Universidad de Utrecht. Graham Hutton y Erik Meijer. Monadic Parser Combinators. Universidades de Nottingham y Utrecht. Referencias web

http://www.haskell.org http://www.informatik.uni-bonn.de/~ralf/frown http://www.cs.uu.nl/~daan/parsec.html

Potrebbero piacerti anche