Guest User

Untitled

a guest
May 13th, 2016
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.63 KB | None | 0 0
  1. {-# LANGUAGE ExistentialQuantification #-}
  2.  
  3. module Main where
  4. import System.Environment
  5. import Text.ParserCombinators.Parsec hiding (spaces)
  6. import Control.Monad
  7. import Control.Monad.Error
  8. import System.IO
  9. import Data.IORef
  10.  
  11. type Env = IORef [(String, IORef LispVal)]
  12.  
  13. type IOThrowsError = ErrorT LispError IO
  14.  
  15. makeFunc varargs env params body = return $ Func (map showVal params) varargs body env
  16. makeNormalFunc = makeFunc Nothing
  17. makeVarArgs = makeFunc . Just . showVal
  18.  
  19. nullEnv :: IO Env
  20. nullEnv = newIORef []
  21.  
  22. liftThrows :: ThrowsError a -> IOThrowsError a
  23. liftThrows (Left err) = throwError err
  24. liftThrows (Right val) = return val
  25.  
  26. runIOThrows :: IOThrowsError String -> IO String
  27. runIOThrows action = runErrorT (trapError action) >>= return . extractValue
  28.  
  29. isBound :: Env -> String -> IO Bool
  30. isBound envRef var = readIORef envRef >>= return . maybe False (const True) . lookup var
  31.  
  32. getVar :: Env -> String -> IOThrowsError LispVal
  33. getVar envRef var = do env <- liftIO $ readIORef envRef
  34. maybe (throwError $ UnboundVar "Getting an unbound variable" var)
  35. (liftIO . readIORef)
  36. (lookup var env)
  37.  
  38. setVar :: Env -> String -> LispVal -> IOThrowsError LispVal
  39. setVar envRef var value = do env <- liftIO $ readIORef envRef
  40. maybe (throwError $ UnboundVar "Setting an unbound variable" var)
  41. (liftIO . (flip writeIORef value))
  42. (lookup var env)
  43. return value
  44.  
  45. defineVar :: Env -> String -> LispVal -> IOThrowsError LispVal
  46. defineVar envRef var value = do
  47. alreadyDefined <- liftIO $ isBound envRef var
  48. if alreadyDefined
  49. then setVar envRef var value >> return value
  50. else liftIO $ do
  51. valueRef <- newIORef value
  52. env <- readIORef envRef
  53. writeIORef envRef ((var, valueRef) : env)
  54. return value
  55.  
  56. bindVars :: Env -> [(String, LispVal)] -> IO Env
  57. bindVars envRef bindings = readIORef envRef >>= extendEnv bindings >>= newIORef
  58. where extendEnv bindings env = liftM (++ env) (mapM addBinding bindings)
  59. addBinding (var, value) = do ref <- newIORef value
  60. return (var, ref)
  61.  
  62. data LispVal = Atom String
  63. | List [LispVal]
  64. | DottedList [LispVal] LispVal
  65. | Number Integer
  66. | String String
  67. | Bool Bool
  68. | PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
  69. | Func { params :: [String], vararg :: (Maybe String),
  70. body :: [LispVal], closure :: Env }
  71.  
  72. data LispError = NumArgs Integer [LispVal]
  73. | TypeMismatch String LispVal
  74. | Parser ParseError
  75. | BadSpecialForm String LispVal
  76. | NotFunction String String
  77. | UnboundVar String String
  78. | Default String
  79.  
  80. data Unpacker = forall a. Eq a => AnyUnpacker (LispVal -> ThrowsError a)
  81.  
  82. showError :: LispError -> String
  83. showError (UnboundVar message varname) = message ++ ": " ++ varname
  84. showError (BadSpecialForm message form) = message ++ ": " ++ show form
  85. showError (NotFunction message func) = message ++ ": " ++ show func
  86. showError (NumArgs expected found) = "Expected " ++ show expected
  87. ++ " args; found values " ++ unwordsList found
  88. showError (TypeMismatch expected found) = "Invalid type: expected " ++ expected
  89. ++ ", found " ++ show found
  90. showError (Parser parseErr) = "Parse error at " ++ show parseErr
  91.  
  92. instance Show LispError where show = showError
  93. instance Error LispError where
  94. noMsg = Default "An error has occurred"
  95. strMsg = Default
  96.  
  97. type ThrowsError = Either LispError
  98.  
  99. trapError action = catchError action (return . show)
  100.  
  101. extractValue :: ThrowsError a -> a
  102. extractValue (Right val) = val
  103.  
  104. symbol :: Parser Char
  105. symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
  106.  
  107. spaces :: Parser ()
  108. spaces = skipMany1 space
  109.  
  110. parseString :: Parser LispVal
  111. parseString = do
  112. _ <- char '"'
  113. x <- many (noneOf "\"")
  114. _ <- char '"'
  115. return $ String x
  116.  
  117. parseAtom :: Parser LispVal
  118. parseAtom = do
  119. first <- letter <|> symbol
  120. rest <- many (letter <|> digit <|> symbol)
  121. let atom = first:rest
  122. return $ case atom of
  123. "#t" -> Bool True
  124. "#f" -> Bool False
  125. _ -> Atom atom
  126.  
  127. parseNumber :: Parser LispVal
  128. parseNumber = do
  129. dig <- many1 digit
  130. let n = read dig :: Integer
  131. return $ Number n
  132.  
  133. parseList :: Parser LispVal
  134. parseList = liftM List $ sepBy parseExpr spaces
  135.  
  136. parseDottedList :: Parser LispVal
  137. parseDottedList = do
  138. h <- endBy parseExpr spaces
  139. t <- char '.' >> spaces >> parseExpr
  140. return $ DottedList h t
  141.  
  142. parseQuoted :: Parser LispVal
  143. parseQuoted = do
  144. _ <- char '\''
  145. x <- parseExpr
  146. return $ List [Atom "quote", x]
  147.  
  148. parseExpr :: Parser LispVal
  149. parseExpr = parseAtom
  150. <|> parseString
  151. <|> parseNumber
  152. <|> parseQuoted
  153. <|> do _ <- char '('
  154. x <- try parseList <|> parseDottedList
  155. _ <- char ')'
  156. return x
  157.  
  158. unwordsList :: [LispVal] -> String
  159. unwordsList = unwords . map showVal
  160.  
  161. showVal :: LispVal -> String
  162. showVal (String contents) = "\"" ++ contents ++ "\""
  163. showVal (Atom name) = name
  164. showVal (Number contents) = show contents
  165. showVal (Bool True) = "#t"
  166. showVal (Bool False) = "#f"
  167. showVal (List contents) = "(" ++ unwordsList contents ++ ")"
  168. showVal (DottedList head tail) = "(" ++ unwordsList head ++ " . " ++ showVal tail ++ ")"
  169. showVal (PrimitiveFunc _) = "<primitive>"
  170. showVal (Func {params = args, vararg = varargs, body = body, closure = env}) =
  171. "(lambda (" ++ unwords (map show args) ++
  172. (case varargs of
  173. Nothing -> ""
  174. Just arg -> " . " ++ arg) ++ ") ...)"
  175.  
  176. instance Show LispVal where show = showVal
  177.  
  178. readExpr :: String -> ThrowsError LispVal
  179. readExpr input = case parse parseExpr "lisp" input of
  180. Left err -> throwError $ Parser err
  181. Right val -> return val
  182.  
  183. eval :: Env -> LispVal -> IOThrowsError LispVal
  184. eval env val@(String _) = return val
  185. eval env val@(Number _) = return val
  186. eval env val@(Bool _) = return val
  187. eval env (Atom id) = getVar env id
  188. eval env (List [Atom "quote", val]) = return val
  189. eval env (List [Atom "if", pred, conseq, alt]) =
  190. do result <- eval env pred
  191. case result of
  192. Bool False -> eval env alt
  193. otherwise -> eval env conseq
  194. eval env (List [Atom "set!", Atom var, form]) =
  195. eval env form >>= setVar env var
  196. eval env (List [Atom "define", Atom var, form]) =
  197. eval env form >>= defineVar env var
  198. eval env (List (function : args)) = do
  199. func <- eval env function
  200. argVals <- mapM (eval env) args
  201. apply func argVals
  202. eval env badForm = throwError $ BadSpecialForm "Unrecognized special form" badForm
  203. eval env (List (Atom "define" : List (Atom var : params) : body)) =
  204. makeNormalFunc env params body >>= defineVar env var
  205. eval env (List (Atom "define" : DottedList (Atom var : params) varargs : body)) =
  206. makeVarArgs varargs env params body >>= defineVar env var
  207. eval env (List (Atom "lambda" : List params : body)) =
  208. makeNormalFunc env params body
  209. eval env (List (Atom "lambda" : DottedList params varargs : body)) =
  210. makeVarArgs varargs env params body
  211. eval env (List (Atom "lambda" : varargs@(Atom _) : body)) =
  212. makeVarArgs varargs env [] body
  213.  
  214. apply :: LispVal -> [LispVal] -> IOThrowsError LispVal
  215. apply (PrimitiveFunc func) args = liftThrows $ func args
  216. apply (Func params varargs body closure) args =
  217. if num params /= num args && varargs == Nothing
  218. then throwError $ NumArgs (num params) args
  219. else (liftIO $ bindVars closure $ zip params args) >>= bindVarArgs varargs >>= evalBody
  220. where remainingArgs = drop (length params) args
  221. num = toInteger . length
  222. evalBody env = liftM last $ mapM (eval env) body
  223. bindVarArgs arg env = case arg of
  224. Just argName -> liftIO $ bindVars env [(argName, List $ remainingArgs)]
  225. Nothing -> return env
  226.  
  227. primitives :: [(String, [LispVal] -> ThrowsError LispVal)]
  228. primitives = [("car", car),
  229. ("cdr", cdr),
  230. ("cons", cons),
  231. ("eq?", eqv),
  232. ("eqv?", eqv),
  233. ("equal?", equal),
  234. ("=", numBoolBinop (==)),
  235. ("<", numBoolBinop (<)),
  236. (">", numBoolBinop (>)),
  237. ("/=", numBoolBinop (/=)),
  238. (">=", numBoolBinop (>=)),
  239. ("<=", numBoolBinop (<=)),
  240. ("&&", boolBoolBinop (&&)),
  241. ("||", boolBoolBinop (||)),
  242. ("string=?", strBoolBinop (==)),
  243. ("string<?", strBoolBinop (<)),
  244. ("string>?", strBoolBinop (>)),
  245. ("string<=?", strBoolBinop (<=)),
  246. ("string>=?", strBoolBinop (>=)),
  247. ("+", numericBinop (+)),
  248. ("-", numericBinop (-)),
  249. ("*", numericBinop (*)),
  250. ("/", numericBinop div),
  251. ("mod", numericBinop mod),
  252. ("quotient", numericBinop quot),
  253. ("remainder", numericBinop rem)]
  254.  
  255. primitiveBindings :: IO Env
  256. primitiveBindings = nullEnv >>= (flip bindVars $ map makePrimitiveFunc primitives)
  257. where makePrimitiveFunc (var, func) = (var, PrimitiveFunc func)
  258.  
  259. unpackStr :: LispVal -> ThrowsError String
  260. unpackStr (String s) = return s
  261. unpackStr (Number s) = return $ show s
  262. unpackStr (Bool s) = return $ show s
  263. unpackStr notString = throwError $ TypeMismatch "string" notString
  264.  
  265. unpackBool :: LispVal -> ThrowsError Bool
  266. unpackBool (Bool b) = return b
  267. unpackBool notBool = throwError $ TypeMismatch "boolean" notBool
  268.  
  269. numericBinop :: (Integer -> Integer -> Integer) -> [LispVal] -> ThrowsError LispVal
  270. numericBinop op [] = throwError $ NumArgs 2 []
  271. numericBinop op singleVal@[_] = throwError $ NumArgs 2 singleVal
  272. numericBinop op params = mapM unpackNum params >>= return . Number . foldl1 op
  273.  
  274. boolBinop :: (LispVal -> ThrowsError a) -> (a -> a -> Bool) -> [LispVal] -> ThrowsError LispVal
  275. boolBinop unpacker op args = if length args /= 2
  276. then throwError $ NumArgs 2 args
  277. else do left <- unpacker $ args !! 0
  278. right <- unpacker $ args !! 1
  279. return $ Bool $ left `op` right
  280.  
  281. numBoolBinop = boolBinop unpackNum
  282. strBoolBinop = boolBinop unpackStr
  283. boolBoolBinop = boolBinop unpackBool
  284.  
  285. unpackNum :: LispVal -> ThrowsError Integer
  286. unpackNum (Number n) = return n
  287. unpackNum (String n) = let parsed = reads n in
  288. if null parsed
  289. then throwError $ TypeMismatch "number" $ String n
  290. else return $ fst $ parsed !! 0
  291. unpackNum (List [n]) = unpackNum n
  292. unpackNum notNum = throwError $ TypeMismatch "number" notNum
  293.  
  294. car :: [LispVal] -> ThrowsError LispVal
  295. car [List (x : xs)] = return x
  296. car [DottedList (x : xs) _] = return x
  297. car [badArg] = throwError $ TypeMismatch "pair" badArg
  298. car badArgList = throwError $ NumArgs 1 badArgList
  299.  
  300. cdr :: [LispVal] -> ThrowsError LispVal
  301. cdr [List (x : xs)] = return $ List xs
  302. cdr [DottedList [_] x] = return x
  303. cdr [DottedList (_ : xs) x] = return $ DottedList xs x
  304. cdr [badArg] = throwError $ TypeMismatch "pair" badArg
  305. cdr badArgList = throwError $ NumArgs 1 badArgList
  306.  
  307. cons :: [LispVal] -> ThrowsError LispVal
  308. cons [x1, List []] = return $ List [x1]
  309.  
  310. cons [x, List xs] = return $ List $ x : xs
  311. cons [x, DottedList xs xlast] = return $ DottedList (x : xs) xlast
  312. cons [x1, x2] = return $ DottedList [x1] x2
  313. cons badArgList = throwError $ NumArgs 2 badArgList
  314.  
  315. eqv :: [LispVal] -> ThrowsError LispVal
  316. eqv [(Bool arg1), (Bool arg2)] = return $ Bool $ arg1 == arg2
  317. eqv [(Number arg1), (Number arg2)] = return $ Bool $ arg1 == arg2
  318. eqv [(String arg1), (String arg2)] = return $ Bool $ arg1 == arg2
  319. eqv [(Atom arg1), (Atom arg2)] = return $ Bool $ arg1 == arg2
  320. eqv [(DottedList xs x), (DottedList ys y)] = eqv [List $ xs ++ [x], List $ ys ++ [y]]
  321. eqv [(List arg1), (List arg2)] = return $ Bool $ (length arg1 == length arg2) &&
  322. (all eqvPair $ zip arg1 arg2)
  323. where eqvPair (x1, x2) = case eqv [x1, x2] of
  324. Left err -> False
  325. Right (Bool val) -> val
  326. eqv [_, _] = return $ Bool False
  327. eqv badArgList = throwError $ NumArgs 2 badArgList
  328.  
  329. unpackEquals :: LispVal -> LispVal -> Unpacker -> ThrowsError Bool
  330. unpackEquals arg1 arg2 (AnyUnpacker unpacker) =
  331. do unpacked1 <- unpacker arg1
  332. unpacked2 <- unpacker arg2
  333. return $ unpacked1 == unpacked2
  334. `catchError` (const $ return False)
  335.  
  336. equal :: [LispVal] -> ThrowsError LispVal
  337. equal [arg1, arg2] = do
  338. primitiveEquals <- liftM or $ mapM (unpackEquals arg1 arg2)
  339. [AnyUnpacker unpackNum, AnyUnpacker unpackStr, AnyUnpacker unpackBool]
  340. eqvEquals <- eqv [arg1, arg2]
  341. return $ Bool $ (primitiveEquals || let (Bool x) = eqvEquals in x)
  342. equal badArgList = throwError $ NumArgs 2 badArgList
  343.  
  344. flushStr :: String -> IO ()
  345. flushStr str = putStr str >> hFlush stdout
  346.  
  347. readPrompt :: String -> IO String
  348. readPrompt prompt = flushStr prompt >> getLine
  349.  
  350. evalAndPrint :: Env -> String -> IO ()
  351. evalAndPrint env expr = evalString env expr >>= putStrLn
  352.  
  353. evalString :: Env -> String -> IO String
  354. evalString env expr = runIOThrows $ liftM show $ (liftThrows $ readExpr expr) >>= eval env
  355.  
  356. until_ :: Monad m => (a -> Bool) -> m a -> (a -> m ()) -> m ()
  357. until_ pred prompt action = do
  358. result <- prompt
  359. if pred result
  360. then return ()
  361. else action result >> until_ pred prompt action
  362.  
  363. runOne :: String -> IO ()
  364. runOne expr = primitiveBindings >>= flip evalAndPrint expr
  365.  
  366. runRepl :: IO ()
  367. runRepl = primitiveBindings >>= until_ (== "quit") (readPrompt "Lisp>>> ") . evalAndPrint
  368.  
  369. main :: IO ()
  370. main = do args <- getArgs
  371. case length args of
  372. 0 -> runRepl
  373. 1 -> runOne $ args !! 0
  374. otherwise -> putStrLn "Program takes only 0 or 1 argument"
Add Comment
Please, Sign In to add comment