solutions5/bfc.scala
changeset 233 38ea26f227af
parent 232 0855c4478f27
child 234 c51305a2217f
equal deleted inserted replaced
232:0855c4478f27 233:38ea26f227af
    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 //}