|
1 // Compiler for JVM |
|
2 |
|
3 import $file.lexer |
|
4 import lexer._ |
|
5 |
|
6 import $file.parser |
|
7 import parser._ |
|
8 |
|
9 |
|
10 val beginning = """ |
|
11 .class public XXX.XXX |
|
12 .super java/lang/Object |
|
13 |
|
14 .method public static write(I)V |
|
15 .limit locals 1 |
|
16 .limit stack 2 |
|
17 getstatic java/lang/System/out Ljava/io/PrintStream; |
|
18 iload 0 |
|
19 invokevirtual java/io/PrintStream/print(I)V |
|
20 return |
|
21 .end method |
|
22 |
|
23 .method public static writes(Ljava/lang/String;)V |
|
24 .limit stack 2 |
|
25 .limit locals 1 |
|
26 getstatic java/lang/System/out Ljava/io/PrintStream; |
|
27 aload 0 |
|
28 invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V |
|
29 return |
|
30 .end method |
|
31 |
|
32 .method public static read()I |
|
33 .limit locals 10 |
|
34 .limit stack 10 |
|
35 |
|
36 ldc 0 |
|
37 istore 1 ; this will hold our final integer |
|
38 Label1: |
|
39 getstatic java/lang/System/in Ljava/io/InputStream; |
|
40 invokevirtual java/io/InputStream/read()I |
|
41 istore 2 |
|
42 iload 2 |
|
43 ldc 10 ; the newline delimiter |
|
44 isub |
|
45 ifeq Label2 |
|
46 iload 2 |
|
47 ldc 32 ; the space delimiter |
|
48 isub |
|
49 ifeq Label2 |
|
50 |
|
51 iload 2 |
|
52 ldc 48 ; we have our digit in ASCII, have to subtract it from 48 |
|
53 isub |
|
54 ldc 10 |
|
55 iload 1 |
|
56 imul |
|
57 iadd |
|
58 istore 1 |
|
59 goto Label1 |
|
60 Label2: |
|
61 ;when we come here we have our integer computed in local variable 1 |
|
62 iload 1 |
|
63 ireturn |
|
64 .end method |
|
65 |
|
66 .method public static main([Ljava/lang/String;)V |
|
67 .limit locals 200 |
|
68 .limit stack 200 |
|
69 |
|
70 ; COMPILED CODE STARTS |
|
71 |
|
72 """ |
|
73 |
|
74 val ending = """ |
|
75 ; COMPILED CODE ENDS |
|
76 return |
|
77 |
|
78 .end method |
|
79 """ |
|
80 |
|
81 // Compiler |
|
82 |
|
83 var counter = -1 |
|
84 |
|
85 def Fresh(x: String) = { |
|
86 counter += 1 |
|
87 x ++ "_" ++ counter.toString() |
|
88 } |
|
89 |
|
90 implicit def string_interpolations(sc: StringContext) = new { |
|
91 def i(args: Any*): String = " " ++ sc.s(args:_*) ++ "\n" |
|
92 def l(args: Any*): String = sc.s(args:_*) ++ ":\n" |
|
93 } |
|
94 |
|
95 type Env = Map[String, Int] |
|
96 |
|
97 def compile_op(op: String) = op match { |
|
98 case "+" => i"iadd" |
|
99 case "-" => i"isub" |
|
100 case "*" => i"imul" |
|
101 case "/" => i"idiv" |
|
102 case "%" => i"irem" |
|
103 } |
|
104 |
|
105 def compile_aexp(a: AExp, env : Env) : String = a match { |
|
106 case Num(i) => i"ldc $i" |
|
107 case Var(s) => i"iload ${env(s)} \t\t; $s" |
|
108 case Aop(op, a1, a2) => |
|
109 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ compile_op(op) |
|
110 } |
|
111 |
|
112 def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match { |
|
113 case True => "" |
|
114 case False => i"goto $jmp" |
|
115 case And(b1, b2) => compile_bexp(b1, env, jmp) ++ compile_bexp(b2, env, jmp) |
|
116 case Or(b1, b2) => { |
|
117 val b1_false = Fresh("Or_second"); |
|
118 val or_end = Fresh("Or_end"); |
|
119 compile_bexp(b1, env, b1_false) ++ |
|
120 i"goto $or_end" ++ |
|
121 l"$b1_false" ++ |
|
122 compile_bexp(b2, env, jmp) ++ |
|
123 l"$or_end" |
|
124 } |
|
125 case Bop("==", a1, a2) => |
|
126 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpne $jmp" |
|
127 case Bop("!=", a1, a2) => |
|
128 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpeq $jmp" |
|
129 case Bop("<", a1, a2) => |
|
130 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpge $jmp" |
|
131 case Bop(">", a1, a2) => |
|
132 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmple $jmp" |
|
133 } |
|
134 |
|
135 def compile_stmt(s: Stmt, env: Env) : (String, Env) = s match { |
|
136 case Skip => ("", env) |
|
137 case Assign(x, a) => { |
|
138 val index = env.getOrElse(x, env.keys.size) |
|
139 (compile_aexp(a, env) ++ i"istore $index \t\t; $x", env + (x -> index)) |
|
140 } |
|
141 case If(b, bl1, bl2) => { |
|
142 val if_else = Fresh("If_else") |
|
143 val if_end = Fresh("If_end") |
|
144 val (instrs1, env1) = compile_block(bl1, env) |
|
145 val (instrs2, env2) = compile_block(bl2, env1) |
|
146 (compile_bexp(b, env, if_else) ++ |
|
147 instrs1 ++ |
|
148 i"goto $if_end" ++ |
|
149 l"$if_else" ++ |
|
150 instrs2 ++ |
|
151 l"$if_end", env2) |
|
152 } |
|
153 case While(b, bl) => { |
|
154 val loop_begin = Fresh("Loop_begin") |
|
155 val loop_end = Fresh("Loop_end") |
|
156 val (instrs1, env1) = compile_block(bl, env) |
|
157 (l"$loop_begin" ++ |
|
158 compile_bexp(b, env, loop_end) ++ |
|
159 instrs1 ++ |
|
160 i"goto $loop_begin" ++ |
|
161 l"$loop_end", env1) |
|
162 } |
|
163 case For(id, lower, upper, code) => { |
|
164 val (assignment_code, env1) = compile_stmt(Assign(id, lower), env) // id := lower; |
|
165 val while_equivalent = While( |
|
166 Or(Bop("<", Var(id), upper), Bop("==", Var(id), upper)), // while id <= upper do { |
|
167 code ++ // code |
|
168 List( |
|
169 Assign(id, Aop("+", Var(id), Num(1))) // id := id + 1 |
|
170 )) // }; |
|
171 |
|
172 val (while_code, env2) = compile_stmt(while_equivalent, env1) |
|
173 (assignment_code ++ while_code, env2) |
|
174 } |
|
175 case WriteId(x) => (i"iload ${env(x)} \t\t; $x" ++ |
|
176 i"invokestatic XXX/XXX/write(I)V", env) |
|
177 case WriteString(x) => (s" ldc ${x}\n" ++ |
|
178 i"invokestatic XXX/XXX/writes(Ljava/lang/String;)V", env) |
|
179 case Read(x) => { |
|
180 val index = env.getOrElse(x, env.keys.size) |
|
181 (i"invokestatic XXX/XXX/read()I" ++ |
|
182 i"istore $index \t\t; $x", env + (x -> index)) |
|
183 } |
|
184 } |
|
185 |
|
186 def compile_block(bl: Block, env: Env) : (String, Env) = bl match { |
|
187 case Nil => ("", env) |
|
188 case s::bl => { |
|
189 val (instrs1, env1) = compile_stmt(s, env) |
|
190 val (instrs2, env2) = compile_block(bl, env1) |
|
191 (instrs1 ++ instrs2, env2) |
|
192 } |
|
193 } |
|
194 |
|
195 def compile(bl: Block, class_name: String) : String = { |
|
196 val instructions = compile_block(bl, Map.empty)._1 |
|
197 (beginning ++ instructions ++ ending).replaceAllLiterally("XXX", class_name) |
|
198 } |
|
199 |
|
200 // Compiling and running |
|
201 |
|
202 import scala.util._ |
|
203 import scala.sys.process._ |
|
204 import scala.io |
|
205 |
|
206 def compile_tofile(bl: Block, class_name: String) = { |
|
207 val output = compile(bl, class_name) |
|
208 val fw = new java.io.FileWriter(class_name + ".j") |
|
209 fw.write(output) |
|
210 fw.close() |
|
211 } |
|
212 |
|
213 def compile_all(bl: Block, class_name: String) : Unit = { |
|
214 compile_tofile(bl, class_name) |
|
215 println("compiled ") |
|
216 val test = ("java -jar jasmin.jar " + class_name + ".j").!! |
|
217 println("assembled ") |
|
218 } |
|
219 |
|
220 def time_needed[T](i: Int, code: => T) = { |
|
221 val start = System.nanoTime() |
|
222 for (j <- 1 to i) code |
|
223 val end = System.nanoTime() |
|
224 (end - start)/(i * 1.0e9) |
|
225 } |
|
226 |
|
227 def compile_run(bl: Block, class_name: String) : Unit = { |
|
228 println("Start compilation") |
|
229 compile_all(bl, class_name) |
|
230 println("running") |
|
231 println("Time: " + time_needed(1, ("java " + class_name + "/" + class_name).!)) |
|
232 } |
|
233 |
|
234 // ---- Q1 |
|
235 |
|
236 // Fibonacci |
|
237 |
|
238 val fibonacciProgram = """write "Fib"; |
|
239 read n; |
|
240 minus1 := 0; |
|
241 minus2 := 1; |
|
242 while n > 0 do { |
|
243 temp := minus2; |
|
244 minus2 := minus1 + minus2; |
|
245 minus1 := temp; |
|
246 n := n - 1 |
|
247 }; |
|
248 write "Result"; |
|
249 write minus2""" |
|
250 |
|
251 //compile_all(Stmts.parse_all(tokenise(fibonacciProgram)).head, "fib") |
|
252 |
|
253 val factorialProgram = """write "Factorial"; |
|
254 read n; |
|
255 fact := 1; |
|
256 |
|
257 while n > 0 do { |
|
258 fact := n * fact; |
|
259 n := n - 1 |
|
260 }; |
|
261 |
|
262 write "Result"; |
|
263 write fact |
|
264 """ |
|
265 |
|
266 compile_all(Stmts.parse_all(tokenise(factorialProgram)).head, "factorial") |
|
267 |
|
268 // ---- Q3 |
|
269 |
|
270 /* compile_run(Stmts.parse_all(tokenise("""for i := 1 upto 10 do { |
|
271 for i := 1 upto 10 do { |
|
272 write i |
|
273 } |
|
274 }""")).head, "nestedloop") */ |
|
275 |
|
276 |
|
277 compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head, "collatz2") |