Created
May 3, 2013 18:36
-
-
Save funrep/5512527 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Main.hs:103:48:
Couldn't match expected type
[Int]' with actual type
Int'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 type
Expr'Expected type: [t0 -> Expr]
Actual type: [Expr]
In the first argument of
zip', namely
x'In the second argument of
($)', namely
zip x y'Failed, modules loaded: none.