73 } |
73 } |
74 |
74 |
75 def run(prog: String, m: Mem = Map()) = compute(prog, 0, 0, m) |
75 def run(prog: String, m: Mem = Map()) = compute(prog, 0, 0, m) |
76 |
76 |
77 |
77 |
78 // The baseline to what we compare our "compiler"; |
78 // The baseline to what we can compare our "compiler" |
79 // it requires something like 60 seconds on my laptop |
79 // implemented below. It should require something like |
|
80 // 60 seconds for the calculation on my laptop |
80 // |
81 // |
81 //time_needed(1, run(load_bff("benchmark.bf"))) |
82 //time_needed(1, run(load_bff("benchmark.bf"))) |
82 |
83 |
|
84 |
|
85 |
|
86 // DEBUGGING INFORMATION!!! |
|
87 // |
|
88 // Compiler, even real ones, are fiedishly difficult to get |
|
89 // to prduce correct code. The point is that for example for |
|
90 // the sierpinski program, they need to still generate code |
|
91 // that displays such a triangle. If yes, then one usually |
|
92 // can take comfort that all is well. If not, then something |
|
93 // went wrong during the optimisations. |
83 |
94 |
84 |
95 |
85 |
96 |
86 // (5) Write a function jtable that precomputes the "jump |
97 // (5) Write a function jtable that precomputes the "jump |
87 // table" for a bf-program. This function takes a bf-program |
98 // table" for a bf-program. This function takes a bf-program |
88 // as an argument and Returns a Map[Int, Int]. The |
99 // as an argument and Returns a Map[Int, Int]. The |
89 // purpose of this map is to record the information |
100 // purpose of this map is to record the information |
90 // that given on the pc-position, say n, is a '[' or a ']', |
101 // that given on the position pc is a '[' or a ']', |
91 // then to which pc-position do we need to jump? |
102 // then to which pc-position do we need to jump next? |
92 // |
103 // |
93 // For example for the program |
104 // For example for the program |
94 // |
105 // |
95 // "+++++[->++++++++++<]>--<+++[->>++++++++++<<]>>++<<----------[+>.>.<+<]" |
106 // "+++++[->++++++++++<]>--<+++[->>++++++++++<<]>>++<<----------[+>.>.<+<]" |
96 // |
107 // |
100 // |
111 // |
101 // This states that for the '[' on position 5, we need to |
112 // This states that for the '[' on position 5, we need to |
102 // jump to position 20, which is just after the corresponding ']'. |
113 // jump to position 20, which is just after the corresponding ']'. |
103 // Similarly, for the ']' on position 19, we need to jump to |
114 // Similarly, for the ']' on position 19, we need to jump to |
104 // position 6, which is just after the '[' on position 5, and so |
115 // position 6, which is just after the '[' on position 5, and so |
105 // on. The idea to not calculate this information each time |
116 // on. The idea is to not calculate this information each time |
106 // we hit a bracket, but just loop uu this information in the |
117 // we hit a bracket, but just look up this information in the |
107 // jtable. |
118 // jtable. You can use the jumpLeft and jumpRight functions |
108 // |
119 // from Part 1 for calculating the jtable. |
109 // Adapt the compute and run functions from Part 1 in order to |
120 // |
110 // take advantage of the information in the jtable. |
121 // Then adapt the compute and run functions from Part 1 in order |
|
122 // to take advantage of the information stored in the jtable. |
|
123 // This means whenever jumpLeft and jumpRight was called previously, |
|
124 // you should look up the jump address in the jtable. |
111 |
125 |
112 |
126 |
113 def jtable(pg: String) : Map[Int, Int] = |
127 def jtable(pg: String) : Map[Int, Int] = |
114 (0 until pg.length).collect { pc => pg(pc) match { |
128 (0 until pg.length).collect { pc => pg(pc) match { |
115 case '[' => (pc -> jumpRight(pg, pc + 1, 0)) |
129 case '[' => (pc -> jumpRight(pg, pc + 1, 0)) |
147 compute2(pg, jtable(pg), 0, 0, m) |
161 compute2(pg, jtable(pg), 0, 0, m) |
148 |
162 |
149 //time_needed(1, run2(load_bff("benchmark.bf"))) |
163 //time_needed(1, run2(load_bff("benchmark.bf"))) |
150 |
164 |
151 |
165 |
|
166 |
152 // (6) Write a function optimise which deletes "dead code" (everything |
167 // (6) Write a function optimise which deletes "dead code" (everything |
153 // that is not a bf-command) and also replaces substrings of the form |
168 // that is not a bf-command) and also replaces substrings of the form |
154 // [-] by a new command 0. The idea is that the the loop [-] resets the |
169 // [-] by a new command 0. The idea is that the loop [-] just resets the |
155 // memory at the current location to 0. In the compute3 and run3 functions |
170 // memory at the current location to 0. In the compute3 and run3 functions |
156 // below we implement this command by writing 0 to mem(mp), then is |
171 // below you implement this command by writing the number 0 to mem(mp), |
157 // write(mem, mp, 0). |
172 // that is write(mem, mp, 0). |
158 // |
173 // |
159 // The easiest way to modify a string in this way is to use the regular |
174 // The easiest way to modify a string in this way is to use the regular |
160 // expression """[^<>+-.,\[\]]""", whcih recognises everything that is |
175 // expression """[^<>+-.,\[\]]""", which recognises everything that is |
161 // not a bf-command and replace it by the empty string. Similarly the |
176 // not a bf-command and replace it by the empty string. Similarly the |
162 // regular expression """\[-\]""" finds all occurences of [-] and |
177 // regular expression """\[-\]""" finds all occurences of [-] and |
163 // by using the Scala method .replaceAll you can repplace it with the |
178 // by using the Scala method .replaceAll you can repplace it with the |
164 // string "0" standing for the new bf-program. |
179 // string "0" standing for the new bf-command. |
165 |
180 |
166 def optimise(s: String) : String = |
181 def optimise(s: String) : String = |
167 s.replaceAll("""[^<>+-.,\[\]]""","").replaceAll("""\[-\]""", "0") |
182 s.replaceAll("""[^<>+-.,\[\]]""","").replaceAll("""\[-\]""", "0") |
|
183 |
168 |
184 |
169 def compute3(pg: String, tb: Map[Int, Int], pc: Int, mp: Int, mem: Mem) : Mem = { |
185 def compute3(pg: String, tb: Map[Int, Int], pc: Int, mp: Int, mem: Mem) : Mem = { |
170 if (0 <= pc && pc < pg.length) { |
186 if (0 <= pc && pc < pg.length) { |
171 val (new_pc, new_mp, new_mem) = pg(pc) match { |
187 val (new_pc, new_mp, new_mem) = pg(pc) match { |
172 case '0' => (pc + 1, mp, write(mem, mp, 0)) |
188 case '0' => (pc + 1, mp, write(mem, mp, 0)) |
191 val pg_opt = optimise(pg) |
207 val pg_opt = optimise(pg) |
192 compute3(pg_opt, jtable(pg_opt), 0, 0, m) |
208 compute3(pg_opt, jtable(pg_opt), 0, 0, m) |
193 } |
209 } |
194 |
210 |
195 |
211 |
196 time_needed(1, run3(load_bff("benchmark.bf"))) |
212 // testcases |
197 |
213 |
198 |
214 //optimise(load_bff("benchmark.bf")) // should have inserted 0's |
199 // (7) |
215 //optimise(load_bff("mandelbrot.bf")).length // => 11203 |
|
216 |
|
217 //time_needed(1, run3(load_bff("benchmark.bf"))) |
|
218 |
|
219 |
|
220 |
|
221 // (7) Write a function combine which replaces sequences |
|
222 // of repated increment and decrement commands by appropriate |
|
223 // two-character commands. For example for sequences of + |
|
224 // |
|
225 // orig bf-cmds | replacement |
|
226 // ------------------------------ |
|
227 // + | +A |
|
228 // ++ | +B |
|
229 // +++ | +C |
|
230 // | |
|
231 // ... | |
|
232 // | |
|
233 // +++....+++ | +Z |
|
234 // (where length = 26) |
|
235 // |
|
236 // Similar for the bf-command -, > and <. All other commands should |
|
237 // be unaffected by this change. |
|
238 // |
|
239 // Adapt the compute4 and run4 functions such that they can deal |
|
240 // appropriately with such two-character commands. |
200 |
241 |
201 def splice(cs: List[Char], acc: List[(Char, Int)]) : List[(Char, Int)] = (cs, acc) match { |
242 def splice(cs: List[Char], acc: List[(Char, Int)]) : List[(Char, Int)] = (cs, acc) match { |
202 case (Nil, acc) => acc |
243 case (Nil, acc) => acc |
203 case ('[' :: cs, acc) => splice(cs, ('[', 1) :: acc) |
244 case ('[' :: cs, acc) => splice(cs, ('[', 1) :: acc) |
204 case (']' :: cs, acc) => splice(cs, (']', 1) :: acc) |
245 case (']' :: cs, acc) => splice(cs, (']', 1) :: acc) |
213 |
254 |
214 def spl(s: String) = splice(s.toList, Nil).reverse |
255 def spl(s: String) = splice(s.toList, Nil).reverse |
215 |
256 |
216 spl(load_bff("benchmark.bf")) |
257 spl(load_bff("benchmark.bf")) |
217 |
258 |
218 def combine(cs: List[(Char, Int)]) : String = { |
259 def combine(s: String) : String = { |
219 (for ((c, n) <- cs) yield c match { |
260 (for ((c, n) <- spl(s)) yield c match { |
220 case '>' => List('>', (n + '@').toChar) |
261 case '>' => List('>', (n + '@').toChar) |
221 case '<' => List('<', (n + '@').toChar) |
262 case '<' => List('<', (n + '@').toChar) |
222 case '+' => List('+', (n + '@').toChar) |
263 case '+' => List('+', (n + '@').toChar) |
223 case '-' => List('-', (n + '@').toChar) |
264 case '-' => List('-', (n + '@').toChar) |
224 case _ => List(c) |
265 case _ => List(c) |
225 }).flatten.mkString |
266 }).flatten.mkString |
226 } |
267 } |
227 |
268 |
228 |
269 |
229 combine(spl(load_bff("benchmark.bf"))) |
270 combine(load_bff("benchmark.bf")) |
230 |
|
231 |
271 |
232 def compute4(pg: String, tb: Map[Int, Int], pc: Int, mp: Int, mem: Mem) : Mem = { |
272 def compute4(pg: String, tb: Map[Int, Int], pc: Int, mp: Int, mem: Mem) : Mem = { |
233 if (0 <= pc && pc < pg.length) { |
273 if (0 <= pc && pc < pg.length) { |
234 val (new_pc, new_mp, new_mem) = pg(pc) match { |
274 val (new_pc, new_mp, new_mem) = pg(pc) match { |
235 case '0' => (pc + 1, mp, write(mem, mp, 0)) |
275 case '0' => (pc + 1, mp, write(mem, mp, 0)) |
249 } |
289 } |
250 else mem |
290 else mem |
251 } |
291 } |
252 |
292 |
253 def run4(pg: String, m: Mem = Map()) = { |
293 def run4(pg: String, m: Mem = Map()) = { |
254 val pg_opt = combine(spl(optimise(pg))) |
294 val pg_opt = combine(optimise(pg)) |
255 compute4(pg_opt, jtable(pg_opt), 0, 0, m) |
295 compute4(pg_opt, jtable(pg_opt), 0, 0, m) |
256 } |
296 } |
257 |
297 |
258 |
298 |
|
299 combine(optimise(load_bff("benchmark.bf"))) // => """>A+B[<A+M>A-A]<A[[.....""" |
|
300 |
259 //time_needed(1, run4(load_bff("benchmark.bf"))) |
301 //time_needed(1, run4(load_bff("benchmark.bf"))) |
|
302 |
|
303 //time_needed(1, run(load_bff("sierpinski.bf"))) |
|
304 //time_needed(1, run4(load_bff("sierpinski.bf"))) |
|
305 |
260 //time_needed(1, run4(load_bff("mandelbrot.bf"))) |
306 //time_needed(1, run4(load_bff("mandelbrot.bf"))) |
261 |
307 |
262 |
308 |
263 } |
309 //} |