Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2019
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.18 KB | None | 0 0
  1. {-# LANGUAGE FlexibleContexts, LambdaCase #-}
  2.  
  3. module Scheme.Eval where
  4.  
  5. import Scheme.Core
  6.  
  7. import Prelude hiding (lookup)
  8. import qualified Data.HashMap.Strict as H (HashMap, insert, lookup, empty, fromList, union)
  9. import Control.Monad.State
  10. import Control.Monad.Except
  11.  
  12. -- ### Evaluation helpers
  13.  
  14. -- Evaluates a symbol to string
  15. -- Throws an error if value is not a symbol
  16. -- Examples:
  17. -- getSym (Symbol "x") ==> "x"
  18. -- getSym (Number 1) ==> Not a symbol: x
  19. getSym :: Val -> EvalState String
  20. getSym (Symbol x) = return x
  21. getSym v = throwError $ NotASymbol v
  22.  
  23. -- `let` and `let*`
  24. getBinding :: Val -> EvalState (String, Val)
  25. getBinding (List [c, e]) = liftM2 (,) (getSym c) (eval e)
  26. getBinding v = throwError $ NotAListOfTwo v
  27.  
  28. -- Evaluates a list of two to a tuple
  29. -- Throws an error if value is not a list of two
  30. -- This is useful in special form `cond`, since each clause
  31. -- is expected to be exactly a two-element list
  32. -- Examples:
  33. -- getListOf2 (List (Number 1) (Symbol "x"))
  34. -- ==> ((Number 1), (Symbol "x"))
  35. -- getListOf2 (List (Number 1))
  36. -- ==> Not a list of two elements: (1)
  37. getListOf2 :: Val -> EvalState (Val, Val)
  38. getListOf2 (List [c, e]) = return (c, e)
  39. getListOf2 v = throwError $ NotAListOfTwo v
  40.  
  41. --- ### Keywords
  42.  
  43. -- When evaluating special forms, a list form starting with a keyword
  44. -- is expected to match the special form syntax.
  45. keywords :: [String]
  46. keywords = [ "define"
  47. , "lambda"
  48. , "cond"
  49. , "let"
  50. , "let*"
  51. , "define-macro"
  52. , "quasiquote"
  53. , "unquote"
  54. ]
  55.  
  56. -- ### The monadic evaluator
  57. -- Unlike evaluators in previous MPs, `eval` does not take any environment!
  58. -- This is because the environment is encapsulated in the `EvalState` monad.
  59. -- To access the environment, all you have to do is `get`, `modify` or `put`!
  60. eval :: Val -> EvalState Val
  61.  
  62. -- Primitive evaluates to itself
  63. eval v@(Number _) = return v
  64. eval v@(Boolean _) = return v
  65.  
  66. -- Symval evaluates to the value bound to it
  67. eval (Symbol sym) =
  68. H.lookup sym `liftM` get >>=
  69. \case Just val -> return val
  70. _ -> throwError $ UndefSymbolError sym
  71.  
  72. -- Dotted list may just be an equivalent representation of List.
  73. -- We simply try to flatten the list. If it's still dotted after
  74. -- flattening, it's an invalid expression.
  75. eval expr@(DottedList _ _) = case flattenList expr of
  76. DottedList _ _ -> throwError $ InvalidExpression expr
  77. v -> eval v
  78.  
  79. -- List evaluates as a form of the following
  80. -- 1. Special form (`define`, `let`, `let*`, `cond`, `quote`, `quasiquote`,
  81. -- `unquote`, `define-macro`, ...)
  82. -- 2. Macro expansion (Macro)
  83. -- 3. Function application (Func)
  84. -- 4. Primitive function application (PrimFunc)
  85. eval expr@(List lst) = evalList $ map flattenList lst where
  86. --- Evaluator for forms
  87. invalidSpecialForm :: String -> EvalState e
  88. invalidSpecialForm frm = throwError $ InvalidSpecialForm frm expr
  89.  
  90. evalList :: [Val] -> EvalState Val
  91.  
  92. evalList [] = throwError $ InvalidExpression expr
  93.  
  94. -- quote
  95. evalList [Symbol "quote", e] = return e
  96.  
  97. -- unquote (illegal at surface evaluation)
  98. evalList [Symbol "unquote", e] = throwError $ UnquoteNotInQuasiquote e
  99.  
  100. -- quasiquote
  101. evalList [Symbol "quasiquote", e] = evalQuasi 1 e where
  102. evalQuasi :: Int -> Val -> EvalState Val
  103. evalQuasi 0 (List [Symbol "unquote", v]) = throwError $ UnquoteNotInQuasiquote v
  104. evalQuasi 1 (List [Symbol "unquote", v]) = eval v
  105. evalQuasi n (List ee@[Symbol "quasiquote", _]) = List <$> evalQuasi (n+1) `mapM` ee
  106. evalQuasi n (List ee@[Symbol "unquote", _]) = List <$> evalQuasi (n-1) `mapM` ee
  107. evalQuasi n (List xx) = List <$> mapM (evalQuasi n) xx
  108. evalQuasi n (DottedList xx y) = DottedList <$> mapM (evalQuasi n) xx <*> evalQuasi n y
  109. evalQuasi _ v = return v
  110.  
  111. -- Why comment these out? Because `if` can be defined as macro!
  112. -- -- if-then
  113. -- evalList [Symbol "if", condE, thenE] =
  114. -- eval condE >>= \c -> if lowerBool c then eval thenE else return Void
  115. -- -- if-then-else
  116. -- evalList [Symbol "if", condE, thenE, elseE] =
  117. -- eval condE >>= \c -> eval $ if lowerBool c then thenE else elseE
  118.  
  119. -- cond
  120. evalList (Symbol "cond" : rest@(_:_)) =
  121. mapM getListOf2 rest >>= evalClauses where
  122. -- Empty rest
  123. evalClauses [] = return Void
  124. -- `else` at the end
  125. evalClauses ((Symbol "else", e) : []) = eval e
  126. -- `else` in the middle
  127. evalClauses ((Symbol "else", _) : _) = invalidSpecialForm "cond"
  128. -- Normal case
  129. evalClauses ((c, e) : cs) =
  130. eval c >>= \case Boolean False -> evalClauses cs
  131. _ -> eval e
  132.  
  133. -- let
  134. evalList [Symbol "let", List clauses, body] =
  135. do env <- get
  136. bindings <- mapM getBinding clauses
  137. modify $ H.union $ H.fromList bindings
  138. val <- eval body
  139. put env
  140. return val
  141.  
  142. -- let*
  143. evalList [Symbol "let*", List clauses, body] =
  144. do env <- get
  145. forM_ clauses $ getBinding >=> modify . uncurry H.insert
  146. val <- eval body
  147. put env
  148. return val
  149.  
  150. -- lambda
  151. evalList [Symbol "lambda", List args, body] =
  152. mapM getSym args >>= \names -> Func names body `liftM` get
  153.  
  154. -- define function
  155. evalList [Symbol "define", List (Symbol fname : args), body] =
  156. do env <- get
  157. val <- (\argVal -> Func argVal body env) <$> mapM getSym args
  158. modify $ H.insert fname val
  159. return Void
  160.  
  161. -- define variable
  162. evalList [Symbol "define", Symbol name, vexpr] =
  163. do modify . (H.insert name) =<< eval vexpr
  164. return Void
  165.  
  166. -- define-macro
  167. evalList [Symbol "define-macro", List (Symbol fname : args), body] =
  168. do val <- flip Macro body <$> mapM getSym args
  169. modify $ H.insert fname val
  170. return Void
  171.  
  172. -- invalid use of keyword
  173. evalList (Symbol sym : _) | elem sym keywords = invalidSpecialForm sym
  174.  
  175. -- application
  176. evalList (fexpr:args) = eval fexpr >>= aux where
  177. -- Macro expansion
  178. aux (Macro fmls body) | length fmls == length args =
  179. do -- Save environment
  180. env <- get
  181. -- Insert to env
  182. modify $ H.union (H.fromList (zip fmls args))
  183. -- Expansion using eval
  184. expanded <- eval body
  185. -- Restore environment
  186. put env
  187. -- Eval expanded body expressions
  188. eval expanded
  189. -- Function application
  190. aux f = mapM eval args >>= apply f
  191.  
  192. eval val = throwError $ InvalidExpression val
  193.  
  194. -- Function application
  195. apply :: Val -> [Val] -> EvalState Val
  196. -- Function
  197. apply (Func fmls body cenv) args | length fmls == length args =
  198. do -- Save environment
  199. env <- get
  200. -- Insert actual args
  201. modify $ H.union $ H.union (H.fromList (zip fmls args)) cenv
  202. -- Eval body exprs
  203. val <- eval body
  204. -- Restore environment
  205. put env
  206. -- Return result
  207. return val
  208. -- Primitive
  209. apply (PrimFunc p) args = p args
  210. -- Other values are not applicable
  211. apply f args = throwError $ CannotApply f args
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement