|
1 import scala.language.implicitConversions |
|
2 import scala.language.reflectiveCalls |
|
3 import scala.util._ |
|
4 import scala.annotation.tailrec |
|
5 import scala.sys.process._ |
|
6 |
|
7 abstract class Rexp |
|
8 case object NULL extends Rexp |
|
9 case object EMPTY extends Rexp |
|
10 case class CHAR(c: Char) extends Rexp |
|
11 case class ALT(r1: Rexp, r2: Rexp) extends Rexp |
|
12 case class RANGE(cs: List[Char]) extends Rexp |
|
13 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp |
|
14 case class PLUS(r: Rexp) extends Rexp |
|
15 case class STAR(r: Rexp) extends Rexp |
|
16 case class NTIMES(r: Rexp, n: Int) extends Rexp |
|
17 case class NUPTOM(r: Rexp, n: Int, m: Int) extends Rexp |
|
18 |
|
19 object RANGE { |
|
20 def apply(s: String) : RANGE = RANGE(s.toList) |
|
21 } |
|
22 def NMTIMES(r: Rexp, n: Int, m: Int) = { |
|
23 if(m < n) throw new IllegalArgumentException("the number m cannot be smaller than n.") |
|
24 else NUPTOM(r, n, m - n) |
|
25 } |
|
26 |
|
27 case class NOT(r: Rexp) extends Rexp |
|
28 case class OPT(r: Rexp) extends Rexp |
|
29 |
|
30 // some convenience for typing in regular expressions |
|
31 def charlist2rexp(s : List[Char]) : Rexp = s match { |
|
32 case Nil => EMPTY |
|
33 case c::Nil => CHAR(c) |
|
34 case c::s => SEQ(CHAR(c), charlist2rexp(s)) |
|
35 } |
|
36 implicit def string2rexp(s : String) : Rexp = charlist2rexp(s.toList) |
|
37 |
|
38 implicit def RexpOps (r: Rexp) = new { |
|
39 def | (s: Rexp) = ALT(r, s) |
|
40 def % = STAR(r) |
|
41 def ~ (s: Rexp) = SEQ(r, s) |
|
42 } |
|
43 |
|
44 implicit def stringOps (s: String) = new { |
|
45 def | (r: Rexp) = ALT(s, r) |
|
46 def | (r: String) = ALT(s, r) |
|
47 def % = STAR(s) |
|
48 def ~ (r: Rexp) = SEQ(s, r) |
|
49 def ~ (r: String) = SEQ(s, r) |
|
50 } |
|
51 |
|
52 |
|
53 // nullable function: tests whether the regular |
|
54 // expression can recognise the empty string |
|
55 def nullable (r: Rexp) : Boolean = r match { |
|
56 case NULL => false |
|
57 case EMPTY => true |
|
58 case CHAR(_) => false |
|
59 case ALT(r1, r2) => nullable(r1) || nullable(r2) |
|
60 case SEQ(r1, r2) => nullable(r1) && nullable(r2) |
|
61 case STAR(_) => true |
|
62 case PLUS(r) => nullable(r) |
|
63 case NTIMES(r, i) => if (i == 0) true else nullable(r) |
|
64 case NUPTOM(r, i, j) => if (i == 0) true else nullable(r) |
|
65 case RANGE(_) => false |
|
66 case NOT(r) => !(nullable(r)) |
|
67 case OPT(_) => true |
|
68 } |
|
69 |
|
70 // derivative of a regular expression w.r.t. a character |
|
71 def der (c: Char, r: Rexp) : Rexp = r match { |
|
72 case NULL => NULL |
|
73 case EMPTY => NULL |
|
74 case CHAR(d) => if (c == d) EMPTY else NULL |
|
75 case ALT(r1, r2) => ALT(der(c, r1), der(c, r2)) |
|
76 case SEQ(r1, r2) => |
|
77 if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2)) |
|
78 else SEQ(der(c, r1), r2) |
|
79 case STAR(r) => SEQ(der(c, r), STAR(r)) |
|
80 case PLUS(r) => SEQ(der(c, r), STAR(r)) |
|
81 case NTIMES(r, i) => |
|
82 if (i == 0) NULL else der(c, SEQ(r, NTIMES(r, i - 1))) |
|
83 case NUPTOM(r, i, j) => |
|
84 if (i == 0 && j == 0) NULL else |
|
85 if (i == 0) ALT(der(c, NTIMES(r, j)), der(c, NUPTOM(r, 0, j - 1))) |
|
86 else der(c, SEQ(r, NUPTOM(r, i - 1, j))) |
|
87 case RANGE(cs) => if (cs contains c) EMPTY else NULL |
|
88 case NOT(r) => NOT(der (c, r)) |
|
89 case OPT(r) => der(c, r) |
|
90 } |
|
91 |
|
92 def zeroable (r: Rexp) : Boolean = r match { |
|
93 case NULL => true |
|
94 case EMPTY => false |
|
95 case CHAR(_) => false |
|
96 case ALT(r1, r2) => zeroable(r1) && zeroable(r2) |
|
97 case SEQ(r1, r2) => zeroable(r1) || zeroable(r2) |
|
98 case STAR(_) => false |
|
99 case PLUS(r) => zeroable(r) |
|
100 case NTIMES(r, i) => if (i == 0) false else zeroable(r) |
|
101 case NUPTOM(r, i, j) => if (i == 0) false else zeroable(r) |
|
102 case RANGE(_) => false |
|
103 case NOT(r) => !(zeroable(r)) // bug: incorrect definition for NOT |
|
104 case OPT(_) => false |
|
105 } |
|
106 |
|
107 // derivative w.r.t. a string (iterates der) |
|
108 def ders (s: List[Char], r: Rexp) : Rexp = s match { |
|
109 case Nil => r |
|
110 case c::s => ders(s, der(c, r)) |
|
111 } |
|
112 |
|
113 |
|
114 // regular expressions for the While language |
|
115 val SYM = RANGE("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_") |
|
116 val DIGIT = RANGE("0123456789") |
|
117 val ID = SYM ~ (SYM | DIGIT).% |
|
118 val NUM = PLUS(DIGIT) |
|
119 val KEYWORD : Rexp = "skip" | "while" | "do" | "if" | "then" | "else" | "read" | "write" | "true" | "false" |
|
120 val SEMI: Rexp = ";" |
|
121 val OP: Rexp = ":=" | "==" | "-" | "+" | "*" | "!=" | "<" | ">" | "%" | "/" |
|
122 val WHITESPACE = PLUS(" " | "\n" | "\t") |
|
123 val RPAREN: Rexp = ")" |
|
124 val LPAREN: Rexp = "(" |
|
125 val BEGIN: Rexp = "{" |
|
126 val END: Rexp = "}" |
|
127 val ALL = SYM | DIGIT | " " | ":" | ";" | "\"" | "=" |
|
128 val ALL2 = ALL | "\n" |
|
129 val COMMENT2 = ("/*" ~ NOT(ALL.% ~ "*/" ~ ALL.%) ~ "*/") |
|
130 val COMMENT = ("/*" ~ ALL2.% ~ "*/") | ("//" ~ ALL.% ~ "\n") |
|
131 val STRING = "\"" ~ ALL.% ~ "\"" |
|
132 |
|
133 |
|
134 // token for While languag |
|
135 abstract class Token |
|
136 case object T_WHITESPACE extends Token |
|
137 case object T_SEMI extends Token |
|
138 case object T_LPAREN extends Token |
|
139 case object T_RPAREN extends Token |
|
140 case object T_BEGIN extends Token |
|
141 case object T_END extends Token |
|
142 case object T_COMMENT extends Token |
|
143 case class T_ID(s: String) extends Token |
|
144 case class T_OP(s: String) extends Token |
|
145 case class T_NUM(s: String) extends Token |
|
146 case class T_KWD(s: String) extends Token |
|
147 case class T_STRING(s: String) extends Token |
|
148 case class T_ERR(s: String) extends Token // special error token |
|
149 |
|
150 |
|
151 type TokenFun = String => Token |
|
152 type LexRules = List[(Rexp, TokenFun)] |
|
153 val While_lexing_rules: LexRules = |
|
154 List((KEYWORD, (s) => T_KWD(s)), |
|
155 (ID, (s) => T_ID(s)), |
|
156 (COMMENT, (s) => T_COMMENT), |
|
157 (OP, (s) => T_OP(s)), |
|
158 (NUM, (s) => T_NUM(s)), |
|
159 (SEMI, (s) => T_SEMI), |
|
160 (LPAREN, (s) => T_LPAREN), |
|
161 (RPAREN, (s) => T_RPAREN), |
|
162 (BEGIN, (s) => T_BEGIN), |
|
163 (END, (s) => T_END), |
|
164 (STRING, (s) => T_STRING(s.drop(1).dropRight(1))), |
|
165 (WHITESPACE, (s) => T_WHITESPACE)) |
|
166 |
|
167 |
|
168 // calculates derivatives until all of them are zeroable |
|
169 @tailrec |
|
170 def munch(s: List[Char], |
|
171 pos: Int, |
|
172 rs: LexRules, |
|
173 last: Option[(Int, TokenFun)]): Option[(Int, TokenFun)] = { |
|
174 rs match { |
|
175 case Nil => last |
|
176 case rs if (s.length <= pos) => last |
|
177 case rs => { |
|
178 val ders = rs.map({case (r, tf) => (der(s(pos), r), tf)}) |
|
179 val rs_nzero = ders.filterNot({case (r, _) => zeroable(r)}) |
|
180 val rs_nulls = ders.filter({case (r, _) => nullable(r)}) |
|
181 val new_last = if (rs_nulls != Nil) Some((pos, rs_nulls.head._2)) else last |
|
182 munch(s, 1 + pos, rs_nzero, new_last) |
|
183 } |
|
184 }} |
|
185 |
|
186 // iterates the munching function and returns a Token list |
|
187 def tokenize(s: String, rs: LexRules) : List[Token] = munch(s.toList, 0, rs, None) match { |
|
188 case None if (s == "") => Nil |
|
189 case None => List(T_ERR(s"Lexing error: $s")) |
|
190 case Some((n, tf)) => { |
|
191 val (head, tail) = s.splitAt(n + 1) |
|
192 tf(head)::tokenize(tail, rs) |
|
193 } |
|
194 } |
|
195 |
|
196 def tokenizer(s:String) : List[Token] = |
|
197 tokenize(s, While_lexing_rules).filter { |
|
198 case T_ERR(s) => { println(s); sys.exit(-1) } |
|
199 case T_WHITESPACE => false |
|
200 case T_COMMENT => false |
|
201 case _ => true |
|
202 } |
|
203 |
|
204 def fromFile(name: String) : String = |
|
205 io.Source.fromFile(name).mkString |
|
206 |
|
207 // tokenizer tests |
|
208 //println(tokenizer(fromFile("loops.while")).mkString("\n")) |
|
209 //println(tokenizer(fromFile("fib.while")).mkString("\n")) |
|
210 //println(tokenizer(fromFile("collatz.while")).mkString("\n")) |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 // Parser - Abstract syntax trees |
|
217 abstract class Stmt |
|
218 abstract class AExp |
|
219 abstract class BExp |
|
220 type Block = List[Stmt] |
|
221 case object Skip extends Stmt |
|
222 case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt |
|
223 case class While(b: BExp, bl: Block) extends Stmt |
|
224 case class Assign(s: String, a: AExp) extends Stmt |
|
225 case class Read(s: String) extends Stmt |
|
226 case class Write(s: String) extends Stmt |
|
227 case class WriteS(s: String) extends Stmt |
|
228 |
|
229 case class Var(s: String) extends AExp |
|
230 case class Num(i: Int) extends AExp |
|
231 case class Aop(o: String, a1: AExp, a2: AExp) extends AExp |
|
232 |
|
233 case object True extends BExp |
|
234 case object False extends BExp |
|
235 case class Bop(o: String, a1: AExp, a2: AExp) extends BExp |
|
236 |
|
237 // Parser combinators |
|
238 abstract class Parser[I <% Seq[_], T] { |
|
239 def parse(ts: I): Set[(T, I)] |
|
240 |
|
241 def parse_all(ts: I) : Set[T] = |
|
242 for ((head, tail) <- parse(ts); if (tail.isEmpty)) yield head |
|
243 |
|
244 def parse_single(ts: I) : T = parse_all(ts).toList match { |
|
245 case List(t) => t |
|
246 case _ => { println ("Parse Error") ; sys.exit(-1) } |
|
247 } |
|
248 } |
|
249 |
|
250 class SeqParser[I <% Seq[_], T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, (T, S)] { |
|
251 def parse(sb: I) = |
|
252 for ((head1, tail1) <- p.parse(sb); |
|
253 (head2, tail2) <- q.parse(tail1)) yield ((head1, head2), tail2) |
|
254 } |
|
255 |
|
256 class AltParser[I <% Seq[_], T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] { |
|
257 def parse(sb: I) = p.parse(sb) ++ q.parse(sb) |
|
258 } |
|
259 |
|
260 class FunParser[I <% Seq[_], T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] { |
|
261 def parse(sb: I) = |
|
262 for ((head, tail) <- p.parse(sb)) yield (f(head), tail) |
|
263 } |
|
264 |
|
265 |
|
266 case class TokParser(tok: Token) extends Parser[List[Token], Token] { |
|
267 def parse(ts: List[Token]) = ts match { |
|
268 case t::ts if (t == tok) => Set((t, ts)) |
|
269 case _ => Set () |
|
270 } |
|
271 } |
|
272 implicit def token2tparser(t: Token) = TokParser(t) |
|
273 |
|
274 case object NumParser extends Parser[List[Token], Int] { |
|
275 def parse(ts: List[Token]) = ts match { |
|
276 case T_NUM(s)::ts => Set((s.toInt, ts)) |
|
277 case _ => Set () |
|
278 } |
|
279 } |
|
280 |
|
281 case object IdParser extends Parser[List[Token], String] { |
|
282 def parse(ts: List[Token]) = ts match { |
|
283 case T_ID(s)::ts => Set((s, ts)) |
|
284 case _ => Set () |
|
285 } |
|
286 } |
|
287 |
|
288 case object StringParser extends Parser[List[Token], String] { |
|
289 def parse(ts: List[Token]) = ts match { |
|
290 case T_STRING(s)::ts => Set((s, ts)) |
|
291 case _ => Set () |
|
292 } |
|
293 } |
|
294 |
|
295 implicit def ParserOps[I<% Seq[_], T](p: Parser[I, T]) = new { |
|
296 def || (q : => Parser[I, T]) = new AltParser[I, T](p, q) |
|
297 def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f) |
|
298 def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q) |
|
299 } |
|
300 implicit def TokOps(t: Token) = new { |
|
301 def || (q : => Parser[List[Token], Token]) = new AltParser[List[Token], Token](t, q) |
|
302 def ==>[S] (f: => Token => S) = new FunParser[List[Token], Token, S](t, f) |
|
303 def ~[S](q : => Parser[List[Token], S]) = new SeqParser[List[Token], Token, S](t, q) |
|
304 } |
|
305 |
|
306 // arithmetic expressions |
|
307 lazy val AExp: Parser[List[Token], AExp] = |
|
308 (T ~ T_OP("+") ~ AExp) ==> { case ((x, y), z) => Aop("+", x, z): AExp } || |
|
309 (T ~ T_OP("-") ~ AExp) ==> { case ((x, y), z) => Aop("-", x, z): AExp } || T |
|
310 lazy val T: Parser[List[Token], AExp] = |
|
311 (F ~ T_OP("*") ~ T) ==> { case ((x, y), z) => Aop("*", x, z): AExp } || |
|
312 (F ~ T_OP("/") ~ T) ==> { case ((x, y), z) => Aop("*", x, z): AExp } || |
|
313 (F ~ T_OP("%") ~ T) ==> { case ((x, y), z) => Aop("*", x, z): AExp } || F |
|
314 lazy val F: Parser[List[Token], AExp] = |
|
315 (T_LPAREN ~ AExp ~ T_RPAREN) ==> { case ((x, y), z) => y: AExp }|| |
|
316 IdParser ==> { case x => Var(x): AExp } || |
|
317 NumParser ==> { case x => Num(x): AExp } |
|
318 |
|
319 // boolean expressions |
|
320 lazy val BExp: Parser[List[Token], BExp] = |
|
321 (AExp ~ T_OP("==") ~ AExp) ==> { case ((x, y), z) => Bop("==", x, z): BExp } || |
|
322 (AExp ~ T_OP("!=") ~ AExp) ==> { case ((x, y), z) => Bop("!=", x, z): BExp } || |
|
323 (AExp ~ T_OP("<") ~ AExp) ==> { case ((x, y), z) => Bop("<", x, z): BExp } || |
|
324 (AExp ~ T_OP(">") ~ AExp) ==> { case ((x, y), z) => Bop("<", z, x): BExp } || |
|
325 (T_KWD("true") ==> ((_) => True)) || |
|
326 (T_KWD("false") ==> ((_) => False: BExp)) |
|
327 |
|
328 lazy val Stmt: Parser[List[Token], Stmt] = |
|
329 (T_KWD("skip") ==> ((_) => Skip: Stmt)) || |
|
330 (IdParser ~ T_OP(":=") ~ AExp) ==> { case ((x, y), z) => Assign(x, z): Stmt } || |
|
331 (T_KWD("if") ~ BExp ~ T_KWD("then") ~ Block ~ T_KWD("else") ~ Block) ==> |
|
332 { case (((((x,y),z),u),v),w) => If(y, u, w): Stmt } || |
|
333 (T_KWD("while") ~ BExp ~ T_KWD("do") ~ Block) ==> { case (((x, y), z), w) => While(y, w) } || |
|
334 (T_KWD("read") ~ IdParser) ==> { case (x, y) => Read(y) } || |
|
335 (T_KWD("write") ~ IdParser) ==> { case (x, y) => Write(y) } || |
|
336 (T_KWD("write") ~ StringParser) ==> { case (x, y) => WriteS(y) } |
|
337 |
|
338 lazy val Stmts: Parser[List[Token], Block] = |
|
339 (Stmt ~ T_SEMI ~ Stmts) ==> { case ((x, y), z) => x :: z : Block } || |
|
340 (Stmt ==> ((s) => List(s) : Block)) |
|
341 |
|
342 lazy val Block: Parser[List[Token], Block] = |
|
343 (T_BEGIN ~ Stmts ~ T_END) ==> { case ((x, y), z) => y : Block } || |
|
344 (Stmt ==> ((s) => List(s))) |
|
345 |
|
346 |
|
347 |
|
348 // parser examples |
|
349 val p1 = "x := 5" |
|
350 val p1_toks = tokenizer(p1) |
|
351 val p1_ast = Block.parse_all(p1_toks) |
|
352 //println(p1_toks) |
|
353 //println(p1_ast) |
|
354 |
|
355 val p2 = "{ x := 5; y := 8}" |
|
356 val p2_toks = tokenizer(p2) |
|
357 val p2_ast = Block.parse_all(p2_toks) |
|
358 //println(p2_ast) |
|
359 |
|
360 val p3 = "5 == 6" |
|
361 val p3_toks = tokenizer(p3) |
|
362 val p3_ast = BExp.parse_all(p3_toks) |
|
363 //println(p3_ast) |
|
364 |
|
365 val p4 = "true" |
|
366 val p4_toks = tokenizer(p4) |
|
367 val p4_ast = BExp.parse_all(p4_toks) |
|
368 //println(p4_ast) |
|
369 |
|
370 val p5 = "if true then skip else skip" |
|
371 val p5_toks = tokenizer(p5) |
|
372 val p5_ast = Stmt.parse_all(p5_toks) |
|
373 //println(p5_ast) |
|
374 |
|
375 val p6 = "if true then x := 5 else x := 10" |
|
376 val p6_toks = tokenizer(p6) |
|
377 val p6_ast = Stmt.parse_all(p6_toks) |
|
378 //println(p6_ast) |
|
379 |
|
380 val p7 = "if false then x := 5 else x := 10" |
|
381 val p7_toks = tokenizer(p7) |
|
382 val p7_ast = Stmt.parse_all(p7_toks) |
|
383 //println(p7_ast) |
|
384 |
|
385 val p8 = "write x; write \"foo\"; read x " |
|
386 val p8_toks = tokenizer(p8) |
|
387 val p8_ast = Stmts.parse_all(p8_toks) |
|
388 //println(p8_ast) |
|
389 |
|
390 // multiplication |
|
391 val p9 = """ x := 5; |
|
392 y := 4; |
|
393 r := 0; |
|
394 while y > 0 do { |
|
395 r := r + x; |
|
396 y := y - 1 |
|
397 } |
|
398 """ |
|
399 val p9_toks = tokenizer(p9) |
|
400 val p9_ast = Stmts.parse_all(p9_toks) |
|
401 //println(p9_ast) |
|
402 |
|
403 // fibonacci numbers |
|
404 val p10 = """ |
|
405 write "Fib: "; |
|
406 read n; |
|
407 minus1 := 0; |
|
408 minus2 := 1; |
|
409 temp := 0; |
|
410 while n > 0 do { |
|
411 temp := minus2; |
|
412 minus2 := minus1 + minus2; |
|
413 minus1 := temp; |
|
414 n := n - 1 |
|
415 }; |
|
416 write "Result:"; |
|
417 write minus2 |
|
418 """ |
|
419 val p10_toks = tokenizer(p10) |
|
420 val p10_ast = Stmts.parse_all(p10_toks) |
|
421 //println(p10_ast) |
|
422 |
|
423 |
|
424 // an interpreter for the WHILE language |
|
425 type Env = Map[String, Int] |
|
426 |
|
427 def eval_aexp(a: AExp, env : Env) : Int = a match { |
|
428 case Num(i) => i |
|
429 case Var(s) => env(s) |
|
430 case Aop("+", a1, a2) => eval_aexp(a1, env) + eval_aexp(a2, env) |
|
431 case Aop("-", a1, a2) => eval_aexp(a1, env) - eval_aexp(a2, env) |
|
432 case Aop("*", a1, a2) => eval_aexp(a1, env) * eval_aexp(a2, env) |
|
433 } |
|
434 |
|
435 def eval_bexp(b: BExp, env: Env) : Boolean = b match { |
|
436 case True => true |
|
437 case False => false |
|
438 case Bop("==", a1, a2) => eval_aexp(a1, env) == eval_aexp(a2, env) |
|
439 case Bop("!=", a1, a2) => !(eval_aexp(a1, env) == eval_aexp(a2, env)) |
|
440 case Bop("<", a1, a2) => eval_aexp(a1, env) < eval_aexp(a2, env) |
|
441 } |
|
442 |
|
443 def eval_stmt(s: Stmt, env: Env) : Env = s match { |
|
444 case Skip => env |
|
445 case Assign(x, a) => env + (x -> eval_aexp(a, env)) |
|
446 case If(b, bl1, bl2) => if (eval_bexp(b, env)) eval_bl(bl1, env) else eval_bl(bl2, env) |
|
447 case While(b, bl) => |
|
448 if (eval_bexp(b, env)) eval_stmt(While(b, bl), eval_bl(bl, env)) |
|
449 else env |
|
450 case WriteS(s: String) => { println(s); env } |
|
451 case Write(s: String) => { println(env(s)); env } |
|
452 case Read(s: String) => tokenizer(Console.readLine) match { |
|
453 case List(T_NUM(n)) => env + (s -> n.toInt) |
|
454 case _ => { println("not a number"); eval_stmt(Read(s: String), env) } |
|
455 } |
|
456 } |
|
457 |
|
458 def eval_bl(bl: Block, env: Env) : Env = bl match { |
|
459 case Nil => env |
|
460 case s::bl => eval_bl(bl, eval_stmt(s, env)) |
|
461 } |
|
462 |
|
463 def eval(bl: Block) : Env = eval_bl(bl, Map()) |
|
464 |
|
465 def interpret(s: String): Unit = |
|
466 eval(Stmts.parse_all(tokenizer(s)).head) |
|
467 |
|
468 // interpreter example |
|
469 //interpret(p10) |
|
470 |
|
471 |
|
472 |
|
473 // compiler - built-in functions |
|
474 // copied from http://www.ceng.metu.edu.tr/courses/ceng444/link/jvm-cpm.html |
|
475 // |
|
476 |
|
477 val beginning = """ |
|
478 .class public XXX.XXX |
|
479 .super java/lang/Object |
|
480 |
|
481 .method public <init>()V |
|
482 aload_0 |
|
483 invokenonvirtual java/lang/Object/<init>()V |
|
484 return |
|
485 .end method |
|
486 |
|
487 .method public static write(I)V |
|
488 .limit locals 5 |
|
489 .limit stack 5 |
|
490 iload 0 |
|
491 getstatic java/lang/System/out Ljava/io/PrintStream; |
|
492 swap |
|
493 invokevirtual java/io/PrintStream/println(I)V |
|
494 return |
|
495 .end method |
|
496 |
|
497 .method public static writes(Ljava/lang/String;)V |
|
498 .limit stack 2 |
|
499 .limit locals 2 |
|
500 getstatic java/lang/System/out Ljava/io/PrintStream; |
|
501 aload 0 |
|
502 invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V |
|
503 return |
|
504 .end method |
|
505 |
|
506 .method public static read()I |
|
507 .limit locals 10 |
|
508 .limit stack 10 |
|
509 |
|
510 ldc 0 |
|
511 istore 1 ; this will hold our final integer |
|
512 Label1: |
|
513 getstatic java/lang/System/in Ljava/io/InputStream; |
|
514 invokevirtual java/io/InputStream/read()I |
|
515 istore 2 |
|
516 iload 2 |
|
517 ldc 10 ; the newline delimiter |
|
518 isub |
|
519 ifeq Label2 |
|
520 iload 2 |
|
521 ldc 32 ; the space delimiter |
|
522 isub |
|
523 ifeq Label2 |
|
524 |
|
525 iload 2 |
|
526 ldc 48 ; we have our digit in ASCII, have to subtract it from 48 |
|
527 isub |
|
528 ldc 10 |
|
529 iload 1 |
|
530 imul |
|
531 iadd |
|
532 istore 1 |
|
533 goto Label1 |
|
534 Label2: |
|
535 ;when we come here we have our integer computed in Local Variable 1 |
|
536 iload 1 |
|
537 ireturn |
|
538 .end method |
|
539 |
|
540 .method public static main([Ljava/lang/String;)V |
|
541 .limit locals 200 |
|
542 .limit stack 200 |
|
543 |
|
544 """ |
|
545 |
|
546 val ending = """ |
|
547 |
|
548 return |
|
549 |
|
550 .end method |
|
551 """ |
|
552 |
|
553 // for generating new labels |
|
554 var counter = -1 |
|
555 |
|
556 def Fresh(x: String) = { |
|
557 counter += 1 |
|
558 x ++ "_" ++ counter.toString() |
|
559 } |
|
560 |
|
561 type Mem = Map[String, String] |
|
562 type Instrs = List[String] |
|
563 |
|
564 def compile_aexp(a: AExp, env : Mem) : Instrs = a match { |
|
565 case Num(i) => List("ldc " + i.toString + "\n") |
|
566 case Var(s) => List("iload " + env(s) + "\n") |
|
567 case Aop("+", a1, a2) => compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("iadd\n") |
|
568 case Aop("-", a1, a2) => compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("isub\n") |
|
569 case Aop("*", a1, a2) => compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("imul\n") |
|
570 } |
|
571 |
|
572 def compile_bexp(b: BExp, env : Mem, jmp: String) : Instrs = b match { |
|
573 case True => Nil |
|
574 case False => List("goto " + jmp + "\n") |
|
575 case Bop("=", a1, a2) => |
|
576 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("if_icmpne " + jmp + "\n") |
|
577 case Bop("!=", a1, a2) => |
|
578 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("if_icmpeq " + jmp + "\n") |
|
579 case Bop("<", a1, a2) => |
|
580 compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("if_icmpge " + jmp + "\n") |
|
581 } |
|
582 |
|
583 |
|
584 def compile_stmt(s: Stmt, env: Mem) : (Instrs, Mem) = s match { |
|
585 case Skip => (Nil, env) |
|
586 case Assign(x, a) => { |
|
587 val index = if (env.isDefinedAt(x)) env(x) else env.keys.size.toString |
|
588 (compile_aexp(a, env) ++ |
|
589 List("istore " + index + "\n"), env + (x -> index)) |
|
590 } |
|
591 case If(b, bl1, bl2) => { |
|
592 val if_else = Fresh("If_else") |
|
593 val if_end = Fresh("If_end") |
|
594 val (instrs1, env1) = compile_bl(bl1, env) |
|
595 val (instrs2, env2) = compile_bl(bl2, env1) |
|
596 (compile_bexp(b, env, if_else) ++ |
|
597 instrs1 ++ |
|
598 List("goto " + if_end + "\n") ++ |
|
599 List("\n" + if_else + ":\n\n") ++ |
|
600 instrs2 ++ |
|
601 List("\n" + if_end + ":\n\n"), env2) |
|
602 } |
|
603 case While(b, bl) => { |
|
604 val loop_begin = Fresh("Loop_begin") |
|
605 val loop_end = Fresh("Loop_end") |
|
606 val (instrs1, env1) = compile_bl(bl, env) |
|
607 (List("\n" + loop_begin + ":\n\n") ++ |
|
608 compile_bexp(b, env, loop_end) ++ |
|
609 instrs1 ++ |
|
610 List("goto " + loop_begin + "\n") ++ |
|
611 List("\n" + loop_end + ":\n\n"), env1) |
|
612 } |
|
613 case Write(x) => |
|
614 (List("iload " + env(x) + "\n" + |
|
615 "invokestatic XXX/XXX/write(I)V\n"), env) |
|
616 case WriteS(x) => |
|
617 (List("ldc \"" + x + "\"\n" + |
|
618 "invokestatic XXX/XXX/writes(Ljava/lang/String;)V\n"), env) |
|
619 case Read(x) => { |
|
620 val index = if (env.isDefinedAt(x)) env(x) else env.keys.size.toString |
|
621 (List("invokestatic XXX/XXX/read()I\n" + |
|
622 "istore " + index + "\n"), env + (x -> index)) |
|
623 } |
|
624 } |
|
625 |
|
626 |
|
627 def compile_bl(bl: Block, env: Mem) : (Instrs, Mem) = bl match { |
|
628 case Nil => (Nil, env) |
|
629 case s::bl => { |
|
630 val (instrs1, env1) = compile_stmt(s, env) |
|
631 val (instrs2, env2) = compile_bl(bl, env1) |
|
632 (instrs1 ++ instrs2, env2) |
|
633 } |
|
634 } |
|
635 |
|
636 def compile(class_name: String, input: String) : String = { |
|
637 val tks = tokenizer(input) |
|
638 val ast = Stmts.parse_single(tks) |
|
639 val instructions = compile_bl(ast, Map.empty)._1 |
|
640 (beginning ++ instructions.mkString ++ ending).replaceAllLiterally("XXX", class_name) |
|
641 } |
|
642 |
|
643 |
|
644 def compile_file(file_name: String) = { |
|
645 val class_name = file_name.split('.')(0) |
|
646 val output = compile(class_name, fromFile(file_name)) |
|
647 val fw = new java.io.FileWriter(class_name + ".j") |
|
648 fw.write(output) |
|
649 fw.close() |
|
650 } |
|
651 |
|
652 def time_needed[T](i: Int, code: => T) = { |
|
653 val start = System.nanoTime() |
|
654 for (j <- 1 to i) code |
|
655 val end = System.nanoTime() |
|
656 (end - start)/(i * 1.0e9) |
|
657 } |
|
658 |
|
659 |
|
660 def compile_run(file_name: String) : Unit = { |
|
661 val class_name = file_name.split('.')(0) |
|
662 compile_file(file_name) |
|
663 val test = ("java -jar jvm/jasmin-2.4/jasmin.jar " + class_name + ".j").!! |
|
664 //("java " + class_name + "/" + class_name).! |
|
665 } |
|
666 |
|
667 |
|
668 //examples |
|
669 //println(compile("test", p9)) |
|
670 //compile_run("loops.while") |
|
671 compile_run("fib.while") |
|
672 //compile_run("test.while") |