-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEvaluator.hs
152 lines (138 loc) · 5.36 KB
/
Evaluator.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
module Evaluator where
import Control.Monad.Except
import Data.HashMap.Strict qualified as Map
import Data.HashSet (HashSet)
import Data.HashSet qualified as Set
import Data.IORef
import Environment (emptyEnv, initEnv)
import Macro (applyMacro)
import Parser (pExpr, parseProgram)
import Syntax
import Text.Parsec (parse)
parseEvalProgram :: Environment -> String -> IOThrowError [Expr]
parseEvalProgram env input = do
case parse parseProgram "" input of
Left _ -> throwError $ Default "Parse Error."
Right rst -> do
mapM (eval env) rst
-- |
-- Evaluate the Expr until the datum-like expr. It handle the following cases: \
-- - define-syntax: it will try to resigter it to the env.\
-- - macro call: it will expanded and evaluate it.\
-- - primitive function, lambda: it also evaluate them.
eval :: Environment -> Expr -> IOThrowError Expr
eval env expr = case expr of
Symbol s -> do
envMap <- liftIO $ readIORef env
case Map.lookup s envMap of
Nothing -> throwError $ Default $ "Unbounded Symbol: " ++ s
Just val -> return val
List [Symbol "define-syntax", Symbol macroName, content] -> do
closure <- evalSyntaxClosure' env macroName content
define env macroName (Macro closure)
List [Symbol "define", Symbol name, content] -> do
expr' <- eval env content
liftIO $ modifyIORef env (Map.insert name expr')
return $ Symbol name
List [Symbol "lambda", List params, body] -> do
let paramNames = map (\(Symbol s) -> s) params
return $ Lambda paramNames body env
List (Symbol fnName : args) -> do
fntype <- liftIO $ Map.lookup fnName <$> readIORef env
case fntype of
Just primfn@(Primitive pfn) -> do
args' <- mapM (eval env) args
pfn args'
Just lam@(Lambda params body env) -> do
evaluatedArgs <- mapM (eval env) args
applyLambda env lam evaluatedArgs
Just (Macro closure) -> do
expr <- applyMacro env closure $ List (Symbol fnName : args)
eval env expr
_ -> throwError $ Default $ "Unknown symbol: " ++ fnName
Quote expr -> return expr
_ -> return expr
applyLambda :: Environment -> Expr -> [Expr] -> IOThrowError Expr
applyLambda env lam args = case lam of
Lambda params body closure -> do
if length params /= length args
then throwError $ Default "Invalid parameters number."
else do
env' <- liftIO $ extendEnv params args env
body' <- eval env' body
eval env' body'
_ -> do
throwError $ Default "Invalid Lambda Expression."
evalSyntaxClosure' :: Environment -> String -> Expr -> IOThrowError SyntaxClosure
evalSyntaxClosure' env macroName expr = case expr of
List (Symbol "syntax-rules" : List literals : expr') -> do
litNames <- mapM readSymbolName literals
let litNameSet = Set.fromList (macroName : litNames)
pairs <- mapM unpackExprList expr'
rules <- evalRules litNameSet pairs
let closure = SyntaxClosure rules env
return closure
where
unpackExprList :: Expr -> IOThrowError (Expr, Expr)
unpackExprList expr = case expr of
List [lhs, rhs] -> return (lhs, rhs)
_ -> throwError $ Default "Error when read rules."
readSymbolName :: Expr -> IOThrowError String
readSymbolName expr = case expr of
Symbol name -> return name
_ -> throwError $ Default "Literals should contain only Symbols."
evalRules :: HashSet String -> [(Expr, Expr)] -> IOThrowError [Rule]
evalRules lits = mapM (\(p, t) -> evalRule' lits (List [p, t]))
evalRule' :: HashSet String -> Expr -> IOThrowError Rule
evalRule' litSet expr = case expr of
List [patExpr, temExpr] -> do
pat <- evalPattern' litSet patExpr
tem <- evalTemplate' (bvSet pat) temExpr
return (pat, tem)
_ -> throwError $ Default "Unknown syntax rule."
evalPattern' :: HashSet String -> Expr -> IOThrowError Pattern
evalPattern' litSet expr = case expr of
Symbol name ->
if Set.member name litSet
then return $ PSymbol name
else return $ PVariable name
Number n -> return $ PSymbol (show n)
Bool b -> return $ PSymbol (if b then "#t" else "#f")
List [p, Symbol "..."] -> do
pat <- evalPattern' litSet p
return $ PRepeat pat
List pats -> do
pats' <- mapM (evalPattern' litSet) pats
return $ PList pats'
_ -> throwError $ Default "Unknown pattern define."
evalTemplate' :: HashSet String -> Expr -> IOThrowError Template
evalTemplate' pvars expr = case expr of
Symbol name ->
if Set.member name pvars
then return $ TVariable name
else return $ TSymbol name
List [t, Symbol "..."] -> do
tem <- evalTemplate' pvars t
return $ TRepeat tem
List temps -> do
temps' <- mapM (evalTemplate' pvars) temps
return $ TList temps'
applyPrimFn :: Expr -> [Expr] -> IOThrowError Expr
applyPrimFn expr args = case expr of
(Primitive fn) -> fn args
_ -> throwError $ Default "Not a primitive function."
extendEnv :: [String] -> [Expr] -> Environment -> IO Environment
extendEnv names exprs oldenv = do
oldMap <- liftIO $ readIORef oldenv
let newBindings = Map.fromList (zip names exprs)
liftIO $ newIORef (Map.union newBindings oldMap)
define :: Environment -> String -> Expr -> IOThrowError Expr
define env name expr = do
liftIO $ modifyIORef env (Map.insert name expr)
return $ Symbol name
bvSet :: Pattern -> HashSet String
bvSet pat = case pat of
PSymbol _ -> Set.empty
PVariable var -> Set.singleton var
PList lst -> Set.unions $ map bvSet lst
PRepeat p -> bvSet p