-
Notifications
You must be signed in to change notification settings - Fork 39
/
Main.hs
120 lines (100 loc) · 3.81 KB
/
Main.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
import qualified Language.ECMAScript3.Parser as Parser
import Language.ECMAScript3.Syntax
import Control.Monad
import Control.Applicative
import Data.Map as Map (Map, insert, lookup, union, toList, empty)
import Debug.Trace
import Value
--
-- Evaluate functions
--
evalExpr :: StateT -> Expression -> StateTransformer Value
evalExpr env (VarRef (Id id)) = stateLookup env id
evalExpr env (IntLit int) = return $ Int int
evalExpr env (BoolLit bool) = return $ Bool bool
evalExpr env (InfixExpr op expr1 expr2) = do
v1 <- evalExpr env expr1
v2 <- evalExpr env expr2
infixOp env op v1 v2
evalExpr env (AssignExpr OpAssign (LVar var) expr) = do
stateLookup env var -- crashes if the variable doesn't exist
e <- evalExpr env expr
setVar var e
evalStmt :: StateT -> Statement -> StateTransformer Value
evalStmt env EmptyStmt = return Nil
evalStmt env (VarDeclStmt []) = return Nil
evalStmt env (VarDeclStmt (decl:ds)) =
varDecl env decl >> evalStmt env (VarDeclStmt ds)
evalStmt env (ExprStmt expr) = evalExpr env expr
-- Do not touch this one :)
evaluate :: StateT -> [Statement] -> StateTransformer Value
evaluate env [] = return Nil
evaluate env stmts = foldl1 (>>) $ map (evalStmt env) stmts
--
-- Operators
--
infixOp :: StateT -> InfixOp -> Value -> Value -> StateTransformer Value
infixOp env OpAdd (Int v1) (Int v2) = return $ Int $ v1 + v2
infixOp env OpSub (Int v1) (Int v2) = return $ Int $ v1 - v2
infixOp env OpMul (Int v1) (Int v2) = return $ Int $ v1 * v2
infixOp env OpDiv (Int v1) (Int v2) = return $ Int $ div v1 v2
infixOp env OpMod (Int v1) (Int v2) = return $ Int $ mod v1 v2
infixOp env OpLT (Int v1) (Int v2) = return $ Bool $ v1 < v2
infixOp env OpLEq (Int v1) (Int v2) = return $ Bool $ v1 <= v2
infixOp env OpGT (Int v1) (Int v2) = return $ Bool $ v1 > v2
infixOp env OpGEq (Int v1) (Int v2) = return $ Bool $ v1 >= v2
infixOp env OpEq (Int v1) (Int v2) = return $ Bool $ v1 == v2
infixOp env OpEq (Bool v1) (Bool v2) = return $ Bool $ v1 == v2
infixOp env OpNEq (Bool v1) (Bool v2) = return $ Bool $ v1 /= v2
infixOp env OpLAnd (Bool v1) (Bool v2) = return $ Bool $ v1 && v2
infixOp env OpLOr (Bool v1) (Bool v2) = return $ Bool $ v1 || v2
--
-- Environment and auxiliary functions
--
environment :: Map String Value
environment = Map.empty
stateLookup :: StateT -> String -> StateTransformer Value
stateLookup env var = ST $ \s ->
-- this way the error won't be skipped by lazy evaluation
case Map.lookup var (union s env) of
Nothing -> error $ "Variable " ++ show var ++ " not defiend."
Just val -> (val, s)
varDecl :: StateT -> VarDecl -> StateTransformer Value
varDecl env (VarDecl (Id id) maybeExpr) = do
case maybeExpr of
Nothing -> setVar id Nil
(Just expr) -> do
val <- evalExpr env expr
setVar id val
setVar :: String -> Value -> StateTransformer Value
setVar var val = ST $ \s -> (val, insert var val s)
--
-- Types and boilerplate
--
type StateT = Map String Value
data StateTransformer t = ST (StateT -> (t, StateT))
instance Monad StateTransformer where
return x = ST $ \s -> (x, s)
(>>=) (ST m) f = ST $ \s ->
let (v, newS) = m s
(ST resF) = f v
in resF newS
instance Functor StateTransformer where
fmap = liftM
instance Applicative StateTransformer where
pure = return
(<*>) = ap
--
-- Main and results functions
--
showResult :: (Value, StateT) -> String
showResult (val, defs) =
show val ++ "\n" ++ show (toList $ union defs environment) ++ "\n"
getResult :: StateTransformer Value -> (Value, StateT)
getResult (ST f) = f Map.empty
main :: IO ()
main = do
js <- Parser.parseFromFile "Main.js"
let statements = unJavaScript js
putStrLn $ "AST: " ++ (show $ statements) ++ "\n"
putStr $ showResult $ getResult $ evaluate environment statements