Not logged in. Login

Assignment 3 Solutions: Evaluator

The Primitive Function Library

module SExpression where

import Data.Char

data SExpression = NumAtom Int | 
                   SymAtom [Char] | 
                   List [SExpression]

first [List (a:more)] = Just a
first _ = Nothing

rest [List (a:more)] = Just (List more)
rest _ = Nothing

endp [List []] = Just (SymAtom "T")
endp [List _] = Just (SymAtom "F")
endp _ = Nothing

numberp [NumAtom _]  = Just (SymAtom "T")
numberp _  = Just (SymAtom "F")

symbolp [SymAtom _]  = Just (SymAtom "T")
symbolp _  = Just (SymAtom "F")

listp [List _]  = Just (SymAtom "T")
listp _  = Just (SymAtom "F")

cons [e, List es]  = Just (List (e:es))
cons _  = Nothing

eq [SymAtom n1, SymAtom n2]
    | n1 == n2    = Just (SymAtom "T")
    | otherwise   = Just (SymAtom "F")
eq _ = Nothing

plus [NumAtom n1, NumAtom n2] = Just (NumAtom (n1 + n2))
plus _ = Nothing

minus [NumAtom n1, NumAtom n2] = Just (NumAtom (n1 - n2))
minus _ = Nothing

times [NumAtom n1, NumAtom n2] = Just (NumAtom (n1 * n2))
times _ = Nothing

eqp [NumAtom n1, NumAtom n2]
    | n1 == n2    = Just (SymAtom "T")
    | otherwise   = Just (SymAtom "F")
eqp _ = Nothing

lessp [NumAtom n1, NumAtom n2]
    | n1 < n2    = Just (SymAtom "T")
    | otherwise   = Just (SymAtom "F")
lessp _ = Nothing

greaterp [NumAtom n1, NumAtom n2]
    | n1 > n2    = Just (SymAtom "T")
    | otherwise   = Just (SymAtom "F")
greaterp _ = Nothing

sym_lessp [SymAtom n1, SymAtom n2]
    | n1 < n2    = Just (SymAtom "T")
    | otherwise   = Just (SymAtom "F")
sym_lessp _ = Nothing

explode [SymAtom str] = Just (List (map explode1 str))
explode _ = Nothing

explode1 c
    | isDigit c  = NumAtom (digitToInt c)
    | otherwise  = SymAtom [c]

implode [List (SymAtom (c:chars): more)]
    | isAlpha c  = implodeAlpha (c:chars) more
    | otherwise  = implodeSpecial (c:chars) more
implode _ = Nothing

implodeAlpha str [] = Just (SymAtom str)
implodeAlpha s@(_:_) (SymAtom "-" : (SymAtom (c:chars)): more) 
    | isAlpha c  = implodeAlpha (s ++ ('-':c:chars)) more
    | otherwise  = Nothing
implodeAlpha str@(_:_) (SymAtom "-" : (NumAtom s): more) 
    | s > 0     = implodeAlpha (str ++ ('-':(show s))) more
    | otherwise = Nothing
implodeAlpha str ((SymAtom (c:chars)): more) 
    | isAlpha c  = implodeAlpha (str ++ (c:chars)) more
    | otherwise  = Nothing
implodeAlpha str ((NumAtom s): more) = implodeAlpha (str ++ (show s)) more

implodeSpecial str ((SymAtom (c:chars)): more) 
    | isAlpha c  = Nothing
    | otherwise  = implodeSpecial (str ++ (c:chars)) more
implodeSpecial _ _ = Nothing

divide [NumAtom n1, NumAtom 0] = Nothing
divide [NumAtom n1, NumAtom n2] = 
    Just (NumAtom (truncate (fromIntegral n1 / (fromIntegral n2))))
divide _ = Nothing

rem [NumAtom n1, NumAtom 0] = Nothing
rem [NumAtom n1, NumAtom n2] = 
    Just (NumAtom (n1 - (n2 * (truncate (fromIntegral n1 / (fromIntegral n2))))))
rem _ = Nothing

-- A custom instance for converting SExpressions to Lisp list syntax

instance Show SExpression where
    show (NumAtom n) = (show n)
    show (SymAtom s) = s
    show (List []) = "()"
    show (List (a:s)) = "(" ++ show a ++ (concatMap ((' ':) . show) s) ++ ")"

The Interpreter

module SmLispInterpret where

import SExpression as SE
import SmLispParse
import qualified Data.Map.Strict as Env
import Control.Applicative
import System.IO (stdout,stderr,hPutStr,hPutStrLn)

data FnEntry = UserDef [Identifier] SmLispExpr | 
               BuiltIn ([SE.SExpression] -> Maybe SE.SExpression)

type FnEnv = Env.Map [Char] FnEntry

builtinFnEnv = 
    Env.fromList [("first", BuiltIn SE.first), ("rest", BuiltIn SE.rest), 
                  ("endp", BuiltIn SE.endp), ("cons", BuiltIn SE.cons), 
                  ("eq", BuiltIn SE.eq),
                  ("numberp", BuiltIn SE.numberp), ("symbolp", BuiltIn SE.symbolp), 
                  ("listp", BuiltIn SE.listp), ("eqp", BuiltIn SE.eqp),
                  ("lessp", BuiltIn SE.lessp), ("greaterp", BuiltIn SE.greaterp), 
                  ("plus", BuiltIn SE.plus), ("minus", BuiltIn SE.minus), 
                  ("times", BuiltIn SE.times), ("divide", BuiltIn SE.divide), 
                  ("rem", BuiltIn SE.rem), ("sym-lessp", BuiltIn SE.sym_lessp), 
                  ("explode", BuiltIn SE.explode), ("implode", BuiltIn SE.implode)]

type ValEnv = ([(String, SE.SExpression)], [(String, SE.SExpression)])

builtinValEnv = ([], 
                 [("T", SymAtom "T"), ("F", SymAtom "F"), ("otherwise", SymAtom "T")])

reset_to_global_frame (local, global) = ([], global)

applyValEnv (local, global) name =
    case lookup name local of
        Just a -> Just a
        Nothing -> lookup name global

applyFnEnv fnEnv name = Env.lookup name fnEnv


sl_eval (SExpr a) _ _ = Just a
sl_eval (Variable n) _ valEnv = applyValEnv valEnv n
sl_eval (FnCall n args) fnEnv valEnv = 
    sl_apply n (sequence (map (\e -> sl_eval e fnEnv valEnv) args)) 
               fnEnv (reset_to_global_frame valEnv)
sl_eval (CondExpr clauses) fnEnv valEnv = sl_evcond clauses fnEnv valEnv
sl_eval (LetExpr defs e) fnEnv valEnv = 
    sl_eval e fnEnv (add_associations defs fnEnv valEnv)

add_associations [] fnEnv valEnv = valEnv
add_associations ((Binding n e):more_defs) fnEnv (local, global) = 
    case sl_eval e fnEnv (local, global) of
        Just x -> add_associations more_defs fnEnv ((n, x):local, global)
        Nothing -> add_associations more_defs fnEnv (local, global)

sl_evcond [] _ _ = Nothing
sl_evcond (Clause p e : more_clauses) fnEnv valEnv =
    case sl_eval p fnEnv valEnv of
        Just (SymAtom "T") -> sl_eval e fnEnv valEnv
        Just (SymAtom "F") -> sl_evcond more_clauses fnEnv valEnv
        Nothing -> Nothing

sl_apply n Nothing fnEnv valEnv = Nothing
sl_apply n (Just args) fnEnv (local, global) =
    case applyFnEnv fnEnv n of
        Just (BuiltIn primitive) -> primitive args
        Just (UserDef parameters expr) -> 
            sl_eval expr fnEnv (foldr (:) local (zip parameters args), global)
        _-> Nothing

setup_envs_then_eval fnEnv valEnv [] expr = sl_eval expr fnEnv valEnv

setup_envs_then_eval fnEnv valEnv@(local, global) (ConstantDef n e : more_defs) expr =
    case sl_eval e fnEnv valEnv of
        Just sexpr -> 
            setup_envs_then_eval fnEnv (local, (n, sexpr): global) more_defs expr
        Nothing ->  -- on Error, skip and leave n undefined
            setup_envs_then_eval fnEnv valEnv more_defs expr 

setup_envs_then_eval fnEnv valEnv (FunctionDef name args e : more_defs) expr =
    setup_envs_then_eval (Env.insert name (UserDef args e) fnEnv) valEnv more_defs expr


interpret defs expr = setup_envs_then_eval builtinFnEnv builtinValEnv defs expr

smlisp def_text expr_text =
    case (parseProgramText def_text, parseExpressionText expr_text) of
        (Just defs, Just expr) -> interpret defs expr
        _ -> Nothing
Updated Wed March 28 2018, 14:25 by cameron.