Browse Source

Solve the problem badly with too many debug messages

Getty Ritter 4 years ago
parent
commit
093270c8a2
1 changed files with 140 additions and 35 deletions
  1. 140 35
      07/main.pony

+ 140 - 35
07/main.pony

@@ -85,6 +85,21 @@ type Opcode is I64
 // a Mode is true if immediate, false if positional
 type Modes is (Bool, Bool, Bool)
 
+
+class val OutputRequest
+  let _o: I64
+  new val create(_o': I64) => _o = _o'
+  fun output(): I64 => _o
+  fun string(): String =>
+    "OutputRequest(" + _o.string() + ")"
+primitive InputRequest
+  fun string(): String => "InputRequest"
+primitive Halted
+  fun string(): String => "Halted"
+
+type ProgramResult is (OutputRequest | InputRequest | Halted)
+
+
 class Program
   """
   All the logic we need for running programs
@@ -94,26 +109,23 @@ class Program
   var storage: Array[I64]
   // the current instruction we're running
   var pc: USize = 0
+  //
+  var input: Array[I64] = Array[I64]()
 
   // a list of errors that may or may not have happened. (right now,
   // we'll only ever have one, but it's worth planning ahead, eh?)
   var errors: Array[Error val] = Array[Error val]()
 
-  // the queue of input values from the user
-  var input: Array[I64]
-  // the queue of lines we intend to output
-  var output: Array[I64] = Array[I64]()
   // a queue of debug messages
   var debug: Array[String] = Array[String]()
 
-  new create(s: Array[I64], i: Array[I64] = Array[I64]()) =>
+  new create(s: Array[I64]) =>
     """
     Create a Program from a verbatim storage and input array
     """
     storage = s
-    input = i
 
-  new from_file(file: File, i: Array[I64] = Array[I64]()) =>
+  new from_file(file: File) =>
     """
     Create a Program by walking input from a file. This just skips any
     errors if they happen (e.g. if there are non-numbers), which
@@ -127,8 +139,9 @@ class Program
         end
       end
     end
-    input = i
 
+  fun ref send_input(i: I64) =>
+    input.push(i)
 
   fun print(env: Env) =>
     """
@@ -224,13 +237,15 @@ class Program
     (opcode, (mode1 == 1, mode2 == 1, mode3 == 1))
 
 
-  fun ref do_binop(modes: Modes, op: {(I64, I64): I64}) ? =>
+  fun ref do_binop(modes: Modes, op: {(I64, I64): I64}, op_name: String) ? =>
     """
     Get the values of the first two operands by the provided modes, run
     the function `op` on them, and store the result of that at the
     provided location
     """
     let data = get_op_data(modes)?
+    dbg("    writing " + data.val1().string() + op_name + data.val2().string()
+      + " = " + op(data.val1(), data.val2()).string() + " to *" + data.tgt().string())
     write_storage(data.tgt(), op(data.val1(), data.val2()))?
     pc = pc + 4
 
@@ -258,19 +273,45 @@ class Program
       errors.push(NotEnoughInput(pc))
       error
     end
-    write_storage(read_storage(pc+1)?.usize(), i)?
+    let addr = read_storage(pc+1)?.usize()
+    dbg("    writing " + i.string() + " to *" + addr.string())
+    write_storage(addr.usize(), i)?
     pc = pc + 2
 
-  fun ref do_output(modes: Modes) ? =>
+  fun ref do_output(modes: Modes): OutputRequest ? =>
     """
     Write the value indicated according to the mode to the output queue.
     """
     (let m, _, _) = modes
-    output.push(get_value(pc+1, m)?.value())
+    let r = get_value(pc+1, m)?.value()
     pc = pc + 2
+    OutputRequest(r)
+
+  fun ref at_pc(): USize => pc
+
+  fun ref fmt_op(opcode: I64, modes: Modes): String =>
+    (let a, let b, let c) = modes
+    let op1 = try read_storage(pc+1)? else -99 end
+    let op2 = try read_storage(pc+2)? else -99 end
+    let op3 = try read_storage(pc+3)? else -99 end
+    let arg1 = if not a then "*" else "" end + op1.string()
+    let arg2 = if not b then "*" else "" end + op2.string()
+    let arg3 = if not c then "*" else "" end + op3.string()
+    match opcode
+      | 1 => "add " + arg1 + " " + arg2 + " -> " + arg3
+      | 2 => "sub " + arg1 + " " + arg2 + " -> " + arg3
+      | 3 => "input " + arg1
+      | 4 => "output " + arg1
+      | 5 => "jnz " + arg1 + " to " + arg2
+      | 6 => "jez " + arg1 + " to " + arg2
+      | 7 => "lt " + arg1 + " " + arg2 + " -> " + arg3
+      | 8 => "eq " + arg1 + " " + arg2 + " -> " + arg3
+      | 99 => "halt"
+    else
+      "unknown op(" + opcode.string() + ")"
+    end
 
-
-  fun ref run_loop() ? =>
+  fun ref run_loop(): ProgramResult ? =>
     """
     This continues running the VM until it encounters a halt instruction
     or it encounters some other error
@@ -278,33 +319,43 @@ class Program
 
     while true do
       (let opcode, let modes) = parse_op(read_storage(pc)?)
-      dbg("running op " + Format.int[I64](opcode))
+      dbg("  " + pc.string() + " -> " + fmt_op(opcode, modes))
       match opcode
-        | 1 => do_binop(modes, {(x, y) => x + y})?
-        | 2 => do_binop(modes, {(x, y) => x * y})?
-        | 3 => do_input(modes)?
-        | 4 => do_output(modes)?
+        | 1 => do_binop(modes, {(x, y) => x + y}, "+")?
+        | 2 => do_binop(modes, {(x, y) => x * y}, "*")?
+        | 3 => try do_input(modes)? else return InputRequest end
+        | 4 => return do_output(modes)?
         | 5 => do_jump(modes, {(x) => x != 0})?
         | 6 => do_jump(modes, {(x) => x == 0})?
-        | 7 => do_binop(modes, {(x, y) => if x < y then 1 else 0 end})?
-        | 8 => do_binop(modes, {(x, y) => if x == y then 1 else 0 end})?
-        | 99 => return None
+        | 7 => do_binop(modes, {(x, y) => if x < y then 1 else 0 end}, "<")?
+        | 8 => do_binop(modes, {(x, y) => if x == y then 1 else 0 end}, "==")?
+        | 99 => return Halted
         | let x: I64 =>
           errors.push(UnknownOperand(x))
           error
       end
     end
+    Halted
 
-  fun ref run(env: Env): Array[I64] =>
+  fun ref run_with(env: Env, i: Array[I64]): Array[I64] =>
     """
     This runs the VM and prints output: if an error occurs, it prints
     the error message(s) and any debug messages that happened during
     program execution, and then it prints everything printed via an
     output instruction.
     """
+    input.append(i)
 
+    var output = Array[I64]()
+    var is_done = false
     try
-      run_loop()?
+      repeat
+        match run_loop()?
+          | Halted => is_done = true
+          | InputRequest => env.err.print("Not enough input for program"); is_done = true
+          | let o: OutputRequest => output.push(o.output())
+        end
+      until is_done end
     else
       for err in errors.values() do
         env.out.print(err.message())
@@ -318,12 +369,24 @@ class Program
     end
     output
 
-  fun ref clone(): Program =>
-    Program(storage, input)
+  fun ref run_step(env: Env): ProgramResult =>
+    try
+      env.out.print("restarting from " + pc.string())
+      let r = run_loop()?
+      for msg in debug.values() do
+        env.out.print(msg)
+      end
+      debug.clear()
+      r
+    else
+      for err in errors.values() do
+        env.out.print(err.message())
+      end
+      Halted
+    end
 
-  fun ref set_input(i: Array[I64]): Program =>
-    input = i
-    this
+  fun ref clone(): Program =>
+    Program(storage.clone())
 
 actor Main
   new create(env: Env) =>
@@ -334,12 +397,12 @@ actor Main
       with
         file = OpenFile(FilePath(env.root as AmbientAuth, target_file, caps)?) as File
       do
-        let program = Program.from_file(file, [0;1])
+        let program = Program.from_file(file)
         var max: (I64 | None) = None
         var choice = Array[I64]()
-        for p in permutations([0;1;2;3;4]).values() do
-          env.out.print("\ntesting")
-          let res = test_phases(env, program, p)
+        for p in permutations([5;6;7;8;9]).values() do
+          env.out.print(".")
+          let res = test_phases_pt2(env, program, p)
           if try (res as I64) > (max as I64) else true end then
             max = res
             choice = p
@@ -358,10 +421,10 @@ actor Main
       env.exitcode(99)
     end
 
-  fun test_phases(env: Env, program: Program, phases: Array[I64]): (I64 | None) =>
+  fun test_phases_pt1(env: Env, program: Program, phases: Array[I64]): (I64 | None) =>
     var input: I64 = 0
     for i in phases.values() do
-      let output = program.clone().set_input([i; input]).run(env)
+      let output = program.clone().run_with(env, [i;input])
       input = try output(0)? else
         env.out.print("Not enough output returned")
         return None
@@ -370,6 +433,48 @@ actor Main
     end
     input
 
+  fun test_phases_pt2(env: Env, program: Program, phases: Array[I64]): (I64 | None) =>
+    var input: (I64 | None) = 0
+    var programs = Array[(I64, Program)]()
+    var n: I64 = 0
+    for i in phases.values() do
+      let p = program.clone()
+      env.out.print("first step on " + n.string() + " produces " + p.run_step(env).string())
+      env.out.print("  (pc = " + p.at_pc().string() + ")")
+      env.out.print("sending input " + i.string())
+      p.send_input(i)
+      env.out.print("second step on " + n.string() + " produces " + p.run_step(env).string())
+      env.out.print("  (pc = " + p.at_pc().string() + ")")
+      programs.push((n, p))
+      n = n + 1
+    end
+
+    var programs_left = phases.size()
+    while programs_left > 0 do
+      for (n', p) in programs.values() do
+        env.out.print("running program " + n'.string() + " at " + p.at_pc().string() + " with " + input.string() + ": ")
+        try
+          p.send_input((input = None) as I64)
+        else
+          env.out.print("used input more than once!")
+          return None
+        end
+        var running = true
+        repeat
+          let r = p.run_step(env)
+          match r
+            | Halted => programs_left = programs_left - 1; running = false
+            | InputRequest => running = false
+            | let o: OutputRequest =>
+            input = o.output()
+            env.out.print("produced " + input.string() + ", ")
+          end
+        until not running end
+        env.out.print("")
+      end
+    end
+    input
+
   fun permutations(array: Array[I64]): Array[Array[I64]] =>
     if array.size() == 0 then
       [[]]