|
@@ -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
|
|
|
[[]]
|