Browse Source

Try using union types for errors

Getty Ritter 4 years ago
parent
commit
9591605cdc
1 changed files with 96 additions and 25 deletions
  1. 96 25
      02/main.pony

+ 96 - 25
02/main.pony

@@ -2,6 +2,54 @@ use "collections"
 use "files"
 use "format"
 
+class Value
+  let _addr: I64
+  let _value: I64
+
+  new create(__addr: I64, __value: I64) =>
+    _addr = __addr
+    _value = __value
+
+  fun addr(): I64 => _addr
+  fun value(): I64 => _value
+
+class Operands
+  let _lhs: Value
+  let _rhs: Value
+  let _tgt: I64
+
+  new create(__lhs: Value, __rhs: Value, __tgt: I64) =>
+    _lhs = __lhs
+    _rhs = __rhs
+    _tgt = __tgt
+
+  fun val1(): I64 => _lhs.value()
+  fun val2(): I64 => _rhs.value()
+  fun tgt(): USize => _tgt.usize()
+
+trait Error
+  fun message(): String
+
+class OutOfBounds is Error
+  let _loc: USize
+
+  new create(loc: USize) => _loc = loc
+  fun message(): String =>
+    "Memory access out of bounds: " + Format.int[USize](_loc)
+
+class UnknownOperand is Error
+  let _op: I64
+  new create(op: I64) => _op = op
+  fun message(): String =>
+    "Unknown operand: " + Format.int[I64](_op)
+
+primitive Handler
+  fun run(env: Env, result: (None | Error)) =>
+    match result
+      | None => None
+      | let e: Error => env.err.print(e.message())
+    end
+
 class Program
   var storage: Array[I64]
   var pc: USize = 0
@@ -27,47 +75,70 @@ class Program
     end
     env.out.print("]")
 
-  fun ref get_op_data(env: Env): (I64, I64, I64, I64, I64) ? =>
-    let addr1 = storage(pc+1)?
-    let addr2 = storage(pc+2)?
-    let tgt = storage(pc+3)?
+  fun ref get_value(addr_loc: USize): (Value | Error) =>
+    let addr = try storage(addr_loc)? else return OutOfBounds(addr_loc) end
+    let value = try storage(addr.usize())? else return OutOfBounds(addr.usize()) end
+    Value(addr, value)
 
-    let val1 = storage(addr1.usize())?
-    let val2 = storage(addr2.usize())?
-    (addr1, val1, addr2, val2, tgt)
+  fun ref get_op_data(): (Operands | Error) =>
+    let lhs = match get_value(pc+1)
+    | let v: Value => v
+    | let e: Error => return e
+    end
+    let rhs = match get_value(pc+2)
+    | let v: Value => v
+    | let e: Error => return e
+    end
+    let tgt = try storage(pc+3)? else return OutOfBounds(pc+1) end
+    Operands(lhs, rhs, tgt)
 
-  fun ref do_add(env: Env) ? =>
-    (let x1, let op1, let x2, let op2, let tgt) = get_op_data(env)?
-    storage.update(tgt.usize(), op1 + op2)?
+  fun ref do_add(): (None | Error) =>
+    let data = match get_op_data()
+    | let o: Operands => o
+    | let e: Error => return e
+    end
+    try
+      storage(data.tgt())? = data.val1() + data.val2()
+    else
+      return OutOfBounds(data.tgt())
+    end
     pc = pc + 4
-    run(env)
+    None
 
-  fun ref do_mul(env: Env) ? =>
-    (let x1, let op1, let x2, let op2, let tgt) = get_op_data(env)?
-    storage.update(tgt.usize(), op1 * op2)?
+  fun ref do_mul(): (None | Error) =>
+    let data = match get_op_data()
+    | let o: Operands => o
+    | let e: Error => return e
+    end
+    try
+      storage(data.tgt())? = data.val1() * data.val2()
+    else
+      return OutOfBounds(data.tgt())
+    end
     pc = pc + 4
-    run(env)
+    None
 
-  fun ref run(env: Env) =>
+  fun ref run(): (None | Error) =>
     try
       match storage(pc)?
-        | 1 => do_add(env)?
-        | 2 => do_mul(env)?
-        | 99 => None
-        | let x: I64 => env.err.print("Unknown opcode " + Format.int[I64](x))
+        | 1 => do_add()
+        | 2 => do_mul()
+        | 99 => return None
+        | let x: I64 => return UnknownOperand(x)
       end
     else
-      env.err.print("PC " + Format.int[USize](pc) + " out of bounds")
-  end
+      return OutOfBounds(pc)
+    end
+    run()
 
   fun make_trial(a: I64, b: I64): Program ? =>
     var arr = storage.clone()
-    arr.update(1, a)?
-    arr.update(2, b)?
+    arr(1)? = a
+    arr(2)? = b
     Program(arr)
 
   fun ref run_trial(env: Env): Bool =>
-    run(env)
+    Handler.run(env, run())
     try
       storage(0)? == 19690720
     else