Skip to content

Commit

Permalink
Initial commit 2.
Browse files Browse the repository at this point in the history
  • Loading branch information
ningit committed Jan 24, 2017
1 parent 1cf7f02 commit 983f56c
Show file tree
Hide file tree
Showing 14 changed files with 861 additions and 0 deletions.
63 changes: 63 additions & 0 deletions EDA/Arbus.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- | Módulo de implementación del tipo abstracto de datos árbol de búsqueda.
module EDA.Arbus (
Arbus(ArbusVacio),
inserta,
consulta,
esta,
esVacio
) where

-- | 'Arbus' es un árbol de búsqueda con clave y valor.
--
-- Los elementos de tipo 'a' se denominarán «claves» y los del tipo 'b' «valores».
-- El tipo 'a' ha de ser ordenable (ser instancia de la clase 'Ord').
--
-- Se ha omitido la operación de borrado.
data Arbus a b =
-- | Árbol de búsqueda vacío.
ArbusVacio |
-- | Construye un árbol de búsqueda a partir de unos hijos, una clave y un valor.
Nodo (Arbus a b) a b (Arbus a b)

-- | Inserta un elemento en el árbol de búsqueda.
inserta :: (Ord a) => a -> b -> Arbus a b -> Arbus a b
inserta c v ArbusVacio = Nodo ArbusVacio c v ArbusVacio
inserta c v (Nodo iz c' v' dr)
| c == c' = Nodo iz c v dr
| c < c' = Nodo (inserta c v iz) c' v' dr
| otherwise = Nodo iz c' v' (inserta c v dr)

-- | Consulta el valor de una clave.
consulta :: (Ord a) => a -> Arbus a b -> Maybe b
consulta _ ArbusVacio = Nothing
consulta c (Nodo iz c' v dr)
| c == c' = Just v
| c < c' = consulta c iz
| otherwise = consulta c dr

-- | Comprueba si una clave está en el árbol de búsqueda.
esta :: (Ord a) => a -> Arbus a b -> Bool
esta _ ArbusVacio = False
esta x (Nodo iz c _ dr)
| x == c = True
| x < c = esta x iz
| otherwise = esta x dr

-- | Comprueba si el árbol es vacío.
esVacio :: Arbus a b -> Bool
esVacio ArbusVacio = True
esVacio _ = False

-- | Obtiene el recorrido en inorden del árbol, que se corresponde con
-- la lista de pares clave valor ordenada por clave.
inorden :: Arbus a b -> [(a, b)]
inorden ArbusVacio = []
inorden (Nodo iz c v dr) = inorden iz ++ (c, v):inorden dr

-- Igualdad por comparación de inórdenes
instance (Eq a, Eq b) => Eq (Arbus a b) where
x == y = inorden x == inorden y

-- Muestra la lista de pares en inorden.
instance (Show a, Show b) => Show (Arbus a b) where
show arb = "Arbus " ++ show (inorden arb)
91 changes: 91 additions & 0 deletions Main.lhs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
% Intérprete de Rec

Para documentación general y sobre uso del programa véase la documentación
adjunta separada.

Detalles de implementación
--------------------------

El intérprete se ha dividido en diversos módulos que se encargan de distintas
tareas, como generar el árbol sintáctico abstracto a partir de la sintaxis
concreta o evaluar semánticamente dicho árbol.

> import EDA.Arbus
> import REC.Base
> import REC.Sintaxis
> import qualified REC.PasoValor as Valor
> import qualified REC.PasoNombre as Nombre
>
> import System.Environment

Para el análisis sintáctico se ha utilizado la biblioteca de Haskell
*Parsec* (versión 3).

> import Text.Parsec (parse)
> import Text.Parsec.String (Parser)

El estado viene representado por un árbol de búsqueda binario que asocia nombres
de función a pares lista de variables-término, que es la representación escogida
para las funciones de Rec.

> -- | Estado del intérprete de Rec.
> type EstadoRec = Arbus String FuncionRec

Inicialmente no hay función alguna predefinida.

> -- | Estado inicial
> estadoInicial :: EstadoRec
> estadoInicial = ArbusVacio

> -- | Parseador de términos cerrados
> tcerrado :: Parser ExpresionRec
> tcerrado = termino []


La función principal lee texto de la entrada estándar y muestra el resultado en
la salida por defecto.

> -- | Alias para el tipo de las funciones semánticas
> type FnSemantica = Arbus String FuncionRec -> ExpresionRec -> Resultado
>
> -- | Función principal
> main :: IO ()
> main = do
> espec <- getContents
> fn <- averiguaFn getArgs
> mapM_ putStrLn (interpretaLinea fn estadoInicial $ lines espec)
>
> where averiguaFn :: IO [String] -> IO FnSemantica
>
> averiguaFn = fmap (\x -> if x then Nombre.valor else Valor.valor)
> . fmap (any (\arg -> arg == "-n"))
El programa intentará interpretar cada línea en primer lugar como una declaración de
función. En caso de error se intentará interpretar como término cerrado.
> interpretaLinea :: FnSemantica -> EstadoRec -> [String] -> [String]
> interpretaLinea _ _ [] = []
> interpretaLinea fn st (x:xs) = either (\_ -> term:(interpretaLinea fn nst xs))
> (\_ -> interpretaLinea fn nst xs) decl
> where decl = parse declaracion "" x
> term = interpretaTermino fn st x
> nst = either (\_ -> st) (\(nombre, def) -> inserta nombre def st) decl
Esta función analiza sintácticamente la expresión dada como un término cerrado
y devuelve el resultado de evaluarla o un mensaje de error apropiado.
> -- | Intenta parsear un término
> interpretaTermino :: FnSemantica -> EstadoRec -> String -> String
> interpretaTermino fn st exp = either (show) (evaluaTermino fn st) (parse tcerrado "" exp)
Esta función evalúa semánticamente un término, mostrando el mensaje de error apropiado
en caso de fallo.
> -- | Evalúa (semánticamente) un término ya parseado y convierte la salida
> evaluaTermino :: FnSemantica -> EstadoRec -> ExpresionRec -> String
> evaluaTermino fn st exp = case (fn st exp) of
> Valor v -> show v
> ENoVar id -> "Error: variable desconocida «" ++ id ++ "»."
> ENoFunc id -> "Error: función desconocida «" ++ id ++ "»."
> EAridad id ao ad -> "Error: aridad incorrecta en la función «" ++
> id ++ "» (" ++ show ao ++ " en lugar de " ++ show ad ++ ")."
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Makefile para el intérprete de REC

PANDOC := $(shell pandoc -v 2> /dev/null)
FUENTES := $(shell find . -type f -name '*.hs' -or -name '*.lhs')

.PHONY: info clean clean-doc doc

# Compilar con GCH
make:
ghc -O Main.lhs -fno-warn-tabs -o rec

# Eliminar tabulaciones en los archivos fuente
destab:
$(foreach f, ${FUENTES}, expand ${f} > ${f}.expanded; mv ${f}.expanded ${f};)

# Generar la documentación a nivel de código (con Haddock)
doc:
haddock -h --optghc=-fno-warn-tabs -o doc/ref Main.lhs -t "Ecuaciones recursivas - Lenguaje Rec"

# Generar la documentación adjunta
ifdef PANDOC
info: Main.htm doc/manual.es.htm doc/manual.en.htm doc/manual.fr.htm

else
info:
$(error Se necesita Pandoc para generar la documentación)
endif

%.htm: %.md
pandoc --from=markdown -s --to=html5 $< -o $@

%.htm: %.lhs
pandoc --from=markdown+lhs -s --to=html5 $< -o $@

# Borrar archivos intermedios generados por GHC
clean:
$(RM) REC/*.hi EDA/*.hi REC/*.o EDA/*.o Main.hi Main.o

# Borra la documentación generada
clean-doc:
$(RM) -r doc/ref
$(RM) Main.htm doc/*.htm
76 changes: 76 additions & 0 deletions REC/Base.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
-----------------------------------------------------------------------------
-- |
-- Module : REC.Base
--
-- Definiciones básicas de Rec.
--
-- Sintaxis abstracta y tipo del valor semántico.
--
-----------------------------------------------------------------------------

module REC.Base (
ExpresionRec(..),
FuncionRec,
Resultado(..),
esValor
) where

-- | Representación abstracta de un término de Rec.
data ExpresionRec =
Literal Integer |
Variable String |
Suma ExpresionRec ExpresionRec |
Resta ExpresionRec ExpresionRec |
Producto ExpresionRec ExpresionRec |
Condicional ExpresionRec ExpresionRec ExpresionRec |
Llamada String [ExpresionRec]
deriving (Show)

-- | Definición de función del lenguaje Rec.
--
-- Es un par con la lista de nombres de variables y el
-- término que constituye su definición.

type FuncionRec = ([String], ExpresionRec)

-- | Resultado de la evaluación de un término Rec.
--
-- Desglosa las posibles causas de error como sigue:
data Resultado =
-- | Resultado correcto
Valor Integer |
-- | Referencia a una función desconocida
ENoFunc String |
-- | Referencia a una variable desconocida
ENoVar String |
-- | Llamada a función con una aridad diferente de la de su definición
-- (se indican por ese orden)
EAridad String Int Int
deriving (Show, Eq)

-- | Aritmética numérica por comodidad.
-- En caso de error prevalece el generado en el término más a la izquierda.
instance Num Resultado where
(Valor n) + (Valor m) = Valor (n + m)
(Valor _) + res = res
res + _ = res

(Valor n) * (Valor m) = Valor (n * m)
(Valor _) * res = res
res * _ = res

abs (Valor n) = Valor (abs n)
abs res = res

signum (Valor n) = Valor (signum n)
signum _ = Valor 1

fromInteger n = Valor n

negate (Valor n) = Valor (negate n)
negate res = res

-- | Detecta si un resultado es correcto (es decir, no es un error)
esValor :: Resultado -> Bool
esValor (Valor _) = True
esValor _ = False
63 changes: 63 additions & 0 deletions REC/PasoNombre.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-----------------------------------------------------------------------------
-- |
-- Module : REC.PasoNombre
--
-- Función semántica de paso por nombre.
--
-- La definición de la función semántica es prácticamente igual a la de paso
-- por valor. Los errores de función desconocida en los parámetros que no
-- lleguen a evaluarse pasarán desapercibidos.
--
-----------------------------------------------------------------------------

module REC.PasoNombre (
valor
) where

import REC.Base
import EDA.Arbus

-- | Función semántica de paso por nombre,
-- acorde a la definición del capítulo 9 de
-- /Formal Semantics of Programming Languages/ de Glynn Winksel.

valor :: Arbus String FuncionRec -- ^ Entorno de funciones
-> ExpresionRec -> Resultado

valor = evaluar ArbusVacio


-- | Definición completa de la función semántica.

evaluar :: Arbus String Resultado -- ^ Entorno de variables (a resultados)
-> Arbus String FuncionRec -- ^ Entorno de funciones
-> ExpresionRec -> Resultado

evaluar _ _ (Literal m) = Valor m
evaluar vs fs (Suma izq dr) = evaluar vs fs izq + evaluar vs fs dr
evaluar vs fs (Resta izq dr) = evaluar vs fs izq - evaluar vs fs dr
evaluar vs fs (Producto izq dr) = evaluar vs fs izq * evaluar vs fs dr
evaluar vs fs (Condicional cn rt rf)
| not (esValor cond) = cond
| cond == (Valor 0) = evaluar vs fs rt
| otherwise = evaluar vs fs rf
where cond = evaluar vs fs cn

evaluar vs _ (Variable id) = comoRes $ consulta id vs
where comoRes (Just res) = res
comoRes Nothing = ENoVar id

evaluar vs fs (Llamada id ps) = evaluarAux (consulta id fs)
where evaluarAux :: Maybe FuncionRec -> Resultado

evaluarAux Nothing = ENoFunc id
evaluarAux (Just (args, cuerpo))
| length ps == length args = evaluar nuevoVar fs cuerpo
| otherwise = EAridad id (length ps) (length args)

where nuevoVar = evParams args (map (evaluar vs fs) ps)

evParams :: [String] -> [Resultado] -> Arbus String Resultado

evParams (n:ns) (r:rs) = inserta n r (evParams ns rs)
evParams _ [] = ArbusVacio
Loading

0 comments on commit 983f56c

Please sign in to comment.