Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
ndice
Motivacin Qu es un compilador?
Herramientas software
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).
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.
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.
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.
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.
Fase de anlisis
Tres fases
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.
Fase de anlisis
Tres fases
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
Se ocupa de analizar si la sentencia tiene algn significado. Incluye anlisis de tipos, o en general, sentencias que carecen se sentido.
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.
Ejemplo:
Ejemplos
xito :: a -> ReadS a xito x = \s -> [(x, s)] psilon :: ReadS () psilon = xito () fallo :: ReadS a fallo = \s -> []
Alternativa:
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)]
Ejemplos
7 &>< (&><) :: ReadS a -> ReadS b -> ReadS (a,b) p1 &>< p2 = \s -> [ ((x1,x2),s2) | (x1,s1) <- p1 s, (x2,s2) <- p2 s1 ]
infixl
En un lenguaje funcional como Haskell, es fcil traducir las reglas gramaticales directamente a especificacin funcional.
exp = term <*> rest rest = token AddOp <*> exp <|> epsilon
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
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.
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")]
(!>) ::Analiz a -> (a -> Bool) -> Analiz a k !> p = do a <- k if p a then return a else mzero
Analizadores mondicos
Analizadores mondicos
Analizadores mondicos
Software especfico
Alex
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
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)
-- 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 }
Frown
Frown
Frown
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'
Cdigo
parens parens :: Parser () = do{ char '(' ; parens ; char ')' ; parens } <|> return ()
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?
Qu podemos concluir?
Qu podemos concluir?
lexer :: String -> [Token] parser :: [Token] -> rbolAbstracto semntica :: rbolAbstracto -> rbolEtiquetado generacinDeCdigo :: rbolEtiquetado -> [Cdigo mquina] compilador = generacinDeCdigo . semntica . parser . lexer
Qu podemos concluir?
Evaluacin perezosa
Qu podemos concluir?
Evaluacin perezosa
lexer :: String -> [Token] parser :: [Token] -> rbolAbstracto semntica :: rbolAbstracto -> rbolEtiquetado generacinDeCdigo :: rbolEtiquetado -> [Cdigo mquina] compilador = generacinDeCdigo . semntica . parser . lexer
Qu podemos concluir?
Tipos de datos
Qu podemos concluir?
Tipos de datos
Qu podemos concluir?
Tipos de datos
Tabla de smbolos: rboles AVL o Tablas Hash rboles abstractos: tipos mutuamente recursivos
Qu podemos concluir?
Tipos de datos
Tabla de smbolos: rboles AVL o Tablas Hash rboles abstractos: tipos mutuamente recursivos Combinadores de analizadores.
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