Skip to content

Instantly share code, notes, and snippets.

@funrep
Created May 3, 2013 18:36
Show Gist options
  • Save funrep/5512527 to your computer and use it in GitHub Desktop.
Save funrep/5512527 to your computer and use it in GitHub Desktop.
module Main where
import Control.Applicative ((<$>))
import Control.Monad.Error
import Text.ParserCombinators.Parsec
-- Types
data Expr
= Sym String
| List [Expr]
| Num Int
| Str String
| Bool Bool
| Func Env [String] Expr
| Prim ([Expr] -> Expr)
instance Show Expr where
show exp =
case exp of
Sym x -> x
List xs -> "(" ++ (unwords $ map show xs) ++ ")"
Num x -> show x
Str x -> "\"" ++ x ++ "\""
Bool x -> show x
Func _ _ _ -> "a function"
Prim _ -> "a primitive"
data Err
= NotInScope String
| TypeMismatch String
| NumArgs Integer
| Parser ParseError
| Default String
deriving (Show)
instance Error Err
type ErrorOr = Either Err
-- Environment
type Env = [(String, Expr)]
nullEnv = [] :: Env
updateEnv env [] [] = env
updateEnv env (x:xs) (y:ys) = (x, y) : updateEnv env xs ys
-- Evaluation
eval :: Env -> Expr -> ErrorOr Expr
eval env (Sym x) = do
let result = lookup x env
case result of
Just _ -> return $ Sym x
Nothing -> throwError $ NotInScope x
eval env (List [Sym "fn", List params, List form]) =
return $ Func env (map (\(Sym x) -> x) params) (List form)
eval env (List [Sym "if", pred, consq, alt]) = do
result <- eval env pred
case result of
Bool True -> eval env consq
Bool False -> eval env alt
eval env (List [Sym "def", Sym var, form]) = do
val <- eval env form
eval ((var, val) : env) val
eval env (List [Sym "quote", xs]) = return xs
eval env (List (op:args)) = do
func <- eval env op
args <- mapM (eval env) args
apply func args
eval env x = return x
apply :: Expr -> [Expr] -> ErrorOr Expr
apply (Func env params form) args =
eval (updateEnv env params args) form >>= return
apply _ _ = return $ Str "hello"
-- Primitives
prims = [
-- Arithmetic functions
("+", arith (+)),
("-", arith (-)),
("/", arith div),
("*", arith (*)),
("%", arith mod),
("<", numBool (<)),
(">", numBool (>)),
-- Univarsal equality function
("=", equal),
-- Boolean functions
("&", bool (&&)),
("|", bool (||)),
-- List functions
("cons", cons),
("car", car),
("cdr", cdr)]
arith :: (Int -> Int -> Int) -> [Expr] -> ErrorOr Expr
arith f [] = throwError $ NumArgs 2
arith f x@[_] = throwError $ NumArgs 2
arith f params = Num . foldl1 f . (\(Num x) -> x) <$> params
numBool :: (Int -> Int -> Bool) -> [Expr] -> ErrorOr Expr
numBool f [Num x, Num y] = return . Bool $ f x y
numBool f [_,_] = throwError $ TypeMismatch "num"
numBool f _ = throwError $ NumArgs 2
{- TODO: Make so it works on any amount of elements
(= 2 3 4 5)
Should be possible for example -}
equal :: [Expr] -> ErrorOr Expr
equal [Bool x, Bool y] = return . Bool $ x == y
equal [Num x, Num y] = return . Bool $ x == y
equal [Str x, Str y] = return . Bool $ x == y
equal [Sym x, Sym y] = return . Bool $ x == y
equal [List x, List y] =
return . Bool $ eqLength x y && (and $ map eqvPair $ zip x y)
where
eqLength z q = length z == length y
eqvPair (z,q) = case equal [z q] of
Left _ -> False
Right (Bool v) -> v
bool :: (Bool -> Bool -> Bool) -> [Expr] -> ErrorOr Expr
bool f [] = throwError $ NumArgs 2
bool f x@[_] = throwError $ NumArgs 2
bool f [Bool x, Bool y] = return . Bool $ f x y
bool f [_,_] = throwError $ TypeMismatch "bool"
cons :: [Expr] -> ErrorOr Expr
cons [x, List []] = return $ List [x]
cons [x, List xs] = return . List $ [x] ++ xs
cons _ = throwError $ NumArgs 2
car :: [Expr] -> ErrorOr Expr
car [List (x:xs)] = return x
car [_] = throwError $ TypeMismatch "list"
car _ = throwError $ NumArgs 1
cdr :: [Expr] -> ErrorOr Expr
cdr [List (x:xs)] = return $ List xs
cdr [_] = throwError $ TypeMismatch "list"
cdr _ = throwError $ NumArgs 1
-- Parser
readExpr :: String -> ErrorOr Expr
readExpr x =
case parse parseExpr "lisp" x of
Left err -> throwError $ Parser err
Right y -> return y
symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
parseSym :: Parser Expr
parseSym = do
x <- letter <|> symbol
y <- many $ letter <|> digit <|> symbol
return . Sym $ [x] ++ y
parseNum :: Parser Expr
parseNum = do
x <- many1 digit
return . Num $ read x
parseStr :: Parser Expr
parseStr = do
char '"'
x <- many $ noneOf "\""
char '"'
return $ Str x
parseBool :: Parser Expr
parseBool = do
x <- oneOf "TB"
y <- many1 letter
let bool = [x] ++ y
return . Bool $ case bool of
"True" -> True
"False" -> False
parseQtd :: Parser Expr
parseQtd = do
char '\''
x <- parseExpr
return $ List [Sym "quote", x]
parseList :: Parser Expr
parseList = fmap List $ sepBy parseExpr space
parseExpr :: Parser Expr
parseExpr =
parseNum <|>
parseStr <|>
parseBool <|>
parseQtd <|>
parseSym <|>
do char '('
x <- parseList
char ')'
return x
@funrep
Copy link
Author

funrep commented May 3, 2013

Main.hs:103:48:
Couldn't match expected type [Int]' with actual typeInt'
In the expression: x
In the second argument of (.)', namely(\ (Num x) -> x)'
In the second argument of (.)', namely foldl1 f . (\ (Num x) -> x)'

Main.hs:119:60:
Couldn't match expected type t0 -> Expr' with actual typeExpr'
Expected type: [t0 -> Expr]
Actual type: [Expr]
In the first argument of zip', namelyx'
In the second argument of ($)', namelyzip x y'
Failed, modules loaded: none.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment