1 // A Small Compiler for a Simple Functional Language |
1 // A Small Compiler for a Simple Functional Language |
2 // (it does not include a parser and lexer) |
2 // (includes a parser and lexer) |
3 // |
3 // |
4 // call with |
4 // call with |
5 // |
5 // |
|
6 // amm fun.sc test |
|
7 // |
|
8 // or |
|
9 // |
6 // amm fun.sc main defs.fun |
10 // amm fun.sc main defs.fun |
7 // |
|
8 // amm fun.sc main fact.fun |
11 // amm fun.sc main fact.fun |
9 // |
12 // |
10 // or |
13 // or |
11 // amm fun.sc test |
14 // |
12 // |
15 // amm fun.sc run defs.fun |
13 // the latter will print out the JVM instructions for two |
16 // amm fun.sc run fact.fun |
|
17 // |
|
18 // the first prints out the JVM instructions for two |
14 // factorial functions |
19 // factorial functions |
|
20 // |
|
21 // the next compile/run fun files |
|
22 // |
15 |
23 |
16 import $file.fun_tokens, fun_tokens._ |
24 import $file.fun_tokens, fun_tokens._ |
17 import $file.fun_parser, fun_parser._ |
25 import $file.fun_parser, fun_parser._ |
18 |
26 |
19 |
27 |
74 } |
82 } |
75 |
83 |
76 // variable / index environments |
84 // variable / index environments |
77 type Env = Map[String, Int] |
85 type Env = Map[String, Int] |
78 |
86 |
|
87 def compile_op(op: String) = op match { |
|
88 case "+" => i"iadd" |
|
89 case "-" => i"isub" |
|
90 case "*" => i"imul" |
|
91 case "/" => i"idiv" |
|
92 case "%" => i"irem" |
|
93 } |
|
94 |
|
95 |
79 // compile expressions |
96 // compile expressions |
80 def compile_exp(a: Exp, env : Env) : String = a match { |
97 def compile_exp(a: Exp, env: Env) : String = a match { |
81 case Num(i) => i"ldc $i" |
98 case Num(i) => i"ldc $i" |
82 case Var(s) => i"iload ${env(s)}" |
99 case Var(s) => i"iload ${env(s)}" |
83 case Aop("+", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"iadd" |
100 case Aop(op, a1, a2) => |
84 case Aop("-", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"isub" |
101 compile_exp(a1, env) ++ compile_exp(a2, env) ++ compile_op(op) |
85 case Aop("*", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"imul" |
|
86 case Aop("/", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"idiv" |
|
87 case Aop("%", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"irem" |
|
88 case If(b, a1, a2) => { |
102 case If(b, a1, a2) => { |
89 val if_else = Fresh("If_else") |
103 val if_else = Fresh("If_else") |
90 val if_end = Fresh("If_end") |
104 val if_end = Fresh("If_end") |
91 compile_bexp(b, env, if_else) ++ |
105 compile_bexp(b, env, if_else) ++ |
92 compile_exp(a1, env) ++ |
106 compile_exp(a1, env) ++ |
135 i"ireturn" ++ |
149 i"ireturn" ++ |
136 m".end method\n" |
150 m".end method\n" |
137 } |
151 } |
138 case Main(a) => { |
152 case Main(a) => { |
139 m".method public static main([Ljava/lang/String;)V" ++ |
153 m".method public static main([Ljava/lang/String;)V" ++ |
140 m".limit locals 200" ++ |
154 m".limit locals 1" ++ |
141 m".limit stack 200" ++ |
155 m".limit stack ${1 + max_stack_exp(a)}" ++ |
142 compile_exp(a, Map()) ++ |
156 compile_exp(a, Map()) ++ |
143 i"invokestatic XXX/XXX/write(I)V" ++ |
157 i"invokestatic XXX/XXX/write(I)V" ++ |
144 i"return" ++ |
158 i"return" ++ |
145 m".end method\n" |
159 m".end method\n" |
146 } |
160 } |
151 val instructions = prog.map(compile_decl).mkString |
165 val instructions = prog.map(compile_decl).mkString |
152 (library + instructions).replaceAllLiterally("XXX", class_name) |
166 (library + instructions).replaceAllLiterally("XXX", class_name) |
153 } |
167 } |
154 |
168 |
155 |
169 |
|
170 import ammonite.ops._ |
|
171 |
|
172 def compile_to_file(prog: List[Decl], class_name: String) : Unit = |
|
173 write.over(pwd / s"$class_name.j", compile(prog, class_name)) |
|
174 |
|
175 def compile_and_run(prog: List[Decl], class_name: String) : Unit = { |
|
176 println(s"Start of compilation") |
|
177 compile_to_file(prog, class_name) |
|
178 println(s"generated $class_name.j file") |
|
179 os.proc("java", "-jar", "jasmin.jar", s"$class_name.j").call() |
|
180 println(s"generated $class_name.class file") |
|
181 println(s"Run program") |
|
182 os.proc("java", s"${class_name}/${class_name}").call(stdout = os.Inherit) |
|
183 println(s"done.") |
|
184 } |
156 |
185 |
157 |
186 |
158 // An example program (two versions of factorial) |
187 // An example program (two versions of factorial) |
159 // |
188 // |
160 // def fact(n) = |
189 // def fact(n) = |
179 Var("acc"), |
208 Var("acc"), |
180 Call("facT",List(Aop("-",Var("n"),Num(1)), |
209 Call("facT",List(Aop("-",Var("n"),Num(1)), |
181 Aop("*",Var("n"),Var("acc")))))), |
210 Aop("*",Var("n"),Var("acc")))))), |
182 |
211 |
183 Main(Sequence(Write(Call("fact",List(Num(10)))), |
212 Main(Sequence(Write(Call("fact",List(Num(10)))), |
184 Write(Call("facT",List(Num(10), Num(1))))))) |
213 Write(Call("facT",List(Num(10), Num(1))))))) |
185 |
214 |
186 // prints out the JVM instructions |
215 // prints out the JVM instructions for the factorial example |
187 @main |
216 @main |
188 def test() = |
217 def test() = |
189 println(compile(test_prog, "fact")) |
218 println(compile(test_prog, "fact")) |
190 |
219 |
191 |
220 |
195 val class_name = fname.stripSuffix("." ++ path.ext) |
224 val class_name = fname.stripSuffix("." ++ path.ext) |
196 val tks = tokenise(os.read(path)) |
225 val tks = tokenise(os.read(path)) |
197 val ast = parse_tks(tks) |
226 val ast = parse_tks(tks) |
198 println(compile(ast, class_name)) |
227 println(compile(ast, class_name)) |
199 } |
228 } |
|
229 |
|
230 @main |
|
231 def run(fname: String) = { |
|
232 val path = os.pwd / fname |
|
233 val class_name = fname.stripSuffix("." ++ path.ext) |
|
234 val tks = tokenise(os.read(path)) |
|
235 val ast = parse_tks(tks) |
|
236 compile_and_run(ast, class_name) |
|
237 } |