-
Notifications
You must be signed in to change notification settings - Fork 0
/
Compiler.hs
150 lines (137 loc) · 5.84 KB
/
Compiler.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
module Compiler where
import Data.Maybe (fromMaybe)
import Data.List (intercalate)
import Data.Char (ord)
import ProgramStructure hiding (showsLines)
import Tokens
toString :: [String] -> String
toString = intercalate "\n"
class Compile c where
compile :: c -> String
instance Compile Class where
compile = toString . map compile . clsSubs
instance Compile SubDec where
compile sub =
let func = subClass sub ++ "." ++ subName sub
nVars = varCount $ subBody sub
header = case subKind sub of
"constructor" ->
toString [ ""
, "push constant " ++ show (clsFields sub)
, "call Memory.alloc 1"
, "pop pointer 0" ]
"method" -> "\npush argument 0\n\
\pop pointer 0"
"function" -> ""
_ -> error "Error: Invalid subroutine kind"
in toString [ "function " ++ func ++ " " ++ show nVars
++ header
, compile $ subBody sub ]
instance Compile SubBody where
compile = compile . subStatements
instance Compile Statements where
compile (Statements stms) = toString $ map compile stms
instance Compile Statement where
compile (LetArr arr push index value) = toString [ push ++ " // " ++ arr
, compile index
, "add"
, compile value
, "pop temp 0"
, "pop pointer 1"
, "push temp 0"
, "pop that 0"
]
compile (LetVar var pop value) = compile value ++ "\n"
++ pop ++ " // " ++ var
compile (If cond thenDo mElseDo labelID) =
let true = "IF_TRUE" ++ labelID
false = "IF_FALSE" ++ labelID
end = "IF_END" ++ labelID
in toString [ compile cond
, "if-goto " ++ true
, "goto " ++ false
, "label " ++ true
, compile thenDo
, "goto " ++ end
, "label " ++ false
, fromMaybe "" $ fmap compile mElseDo
, "label " ++ end
]
compile (While cond loop labelID) =
let while = "WHILE_EXP" ++ labelID
end = "WHILE_END" ++ labelID
in toString [ "label " ++ while
, compile cond
, "not"
, "if-goto " ++ end
, compile loop
, "goto " ++ while
, "label " ++ end
]
compile (Do subCall) = compile subCall ++ "\n\
\pop temp 0"
compile (Return mValue) = case mValue of
Nothing -> "push constant 0\n\
\return"
Just value -> compile value ++ "\n\
\return"
instance Compile SubCall where
compile (SubCall _ mPush _ cls sub (ExpressionList exprs)) =
let thisForMethod = case mPush of
Nothing -> ""
Just this -> this ++ "\n"
compiledExprs = toString (map compile exprs)
args = case compiledExprs of
"" -> ""
arguments -> arguments ++ "\n"
func = cls ++ "." ++ sub
nArgs = length exprs + (fromEnum . not . null $ thisForMethod)
in thisForMethod
++ args
++ "call " ++ func ++ " " ++ show nArgs
instance Compile Expression where
compile (Expression term1 mFollow) = case mFollow of
Nothing -> compile term1
Just (op, term2) -> toString [ compile term1
, compile term2
, compile op ]
instance Compile Term where
compile (Arr arr push index) = toString [ push ++ " // " ++ arr
, compile index
, "add"
, "pop pointer 1"
, "push that 0" ]
compile (Var var push) = push ++ " // " ++ var
compile (Call subCall) = compile subCall
compile (Parens expr) = compile expr
compile (Const token) = compile token
compile (Unary (SYM '-') term) = compile term ++ "\n\
\neg"
compile (Unary (SYM '~') term) = compile term ++ "\n\
\not"
compile (Unary _ _) = error "Invalid unary op"
instance Compile Token where
compile (KW "this") = "push pointer 0"
compile (KW "null") = "push constant 0"
compile (KW "true") = "push constant 0\n\
\not"
compile (KW "false") = "push constant 0"
compile (IC num) = "push constant " ++ show num
compile (SYM '+') = "add"
compile (SYM '-') = "sub"
compile (SYM '*') = "call Math.multiply 2"
compile (SYM '/') = "call Math.divide 2"
compile (SYM '=') = "eq"
compile (SYM '>') = "gt"
compile (SYM '<') = "lt"
compile (SYM '&') = "and"
compile (SYM '|') = "or"
compile (SYM '~') = "not"
compile (SC string) =
toString [ "push constant " ++ show (length string)
, "call String.new 1"
, toString $ map (\c -> "push constant "
++ shows (ord c) " // " ++ shows c "\n\
\call String.appendChar 2") string
]
compile _ = error "Error: Invalid token"