main.pony 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. use "collections"
  2. use "files"
  3. use "format"
  4. interface Value
  5. fun value(): I64
  6. class Position
  7. let _addr: I64
  8. let _value: I64
  9. new create(__addr: I64, __value: I64) =>
  10. _addr = __addr
  11. _value = __value
  12. fun addr(): I64 => _addr
  13. fun value(): I64 => _value
  14. class Immediate
  15. let _value: I64
  16. new create(__value: I64) =>
  17. _value = __value
  18. fun value(): I64 => _value
  19. class Operands
  20. let _lhs: Value
  21. let _rhs: Value
  22. let _tgt: I64
  23. new create(__lhs: Value, __rhs: Value, __tgt: I64) =>
  24. _lhs = __lhs
  25. _rhs = __rhs
  26. _tgt = __tgt
  27. fun val1(): I64 => _lhs.value()
  28. fun val2(): I64 => _rhs.value()
  29. fun tgt(): USize => _tgt.usize()
  30. trait Error
  31. fun message(): String
  32. class val OutOfBounds is Error
  33. let _loc: USize
  34. new val create(loc: USize) => _loc = loc
  35. fun message(): String =>
  36. "Memory access out of bounds: " + Format.int[USize](_loc)
  37. class val UnknownOperand is Error
  38. let _op: I64
  39. new val create(op: I64) => _op = op
  40. fun message(): String =>
  41. "Unknown operation: " + Format.int[I64](_op)
  42. class val NotEnoughInput is Error
  43. let _pc: USize
  44. new val create(pc: USize) => _pc = pc
  45. fun message(): String =>
  46. "Not enough input when running instruction at " + Format.int[USize](_pc)
  47. type Opcode is I64
  48. type Modes is (Bool, Bool, Bool)
  49. class Program
  50. var storage: Array[I64]
  51. var pc: USize = 0
  52. var errors: Array[Error val] = Array[Error val]()
  53. var input: Array[I64]
  54. var output: Array[I64] = Array[I64]()
  55. var debug: Array[String] = Array[String]()
  56. new create(s: Array[I64], i: Array[I64]) =>
  57. storage = s
  58. input = i
  59. new from_file(file: File, i: Array[I64]) =>
  60. storage = Array[I64](0)
  61. for line in file.lines() do
  62. for chunk in line.split(",").values() do
  63. try
  64. storage.push(chunk.i64()?)
  65. end
  66. end
  67. end
  68. input = i
  69. fun print(env: Env) =>
  70. env.out.write("[")
  71. for x in storage.values() do
  72. env.out.write(Format.int[I64](x))
  73. env.out.write(" ")
  74. end
  75. env.out.print("]")
  76. fun ref dbg(msg: String) =>
  77. debug.push(msg)
  78. fun ref read_storage(addr: USize): I64 ? =>
  79. try
  80. storage(addr)?
  81. else
  82. errors.push(OutOfBounds(addr))
  83. error
  84. end
  85. fun ref write_storage(addr: USize, value: I64) ? =>
  86. try
  87. storage(addr)? = value
  88. else
  89. errors.push(OutOfBounds(addr))
  90. error
  91. end
  92. fun ref get_immediate(addr_loc: USize): Immediate ? =>
  93. let value = read_storage(addr_loc)?
  94. Immediate(value)
  95. fun ref get_position(addr_loc: USize): Position ? =>
  96. let addr = read_storage(addr_loc)?
  97. let value = read_storage(addr.usize())?
  98. Position(addr, value)
  99. fun ref get_value(addr_loc: USize, mode: Bool): Value ? =>
  100. if mode then
  101. get_immediate(addr_loc)?
  102. else
  103. get_position(addr_loc)?
  104. end
  105. fun ref get_op_data(modes: Modes): Operands ? =>
  106. (let lm, let rm, _) = modes
  107. let lhs = get_value(pc+1, lm)?
  108. let rhs = get_value(pc+2, rm)?
  109. let tgt = read_storage(pc+3)?
  110. Operands(lhs, rhs, tgt)
  111. fun ref parse_op(n: I64): (Opcode, Modes) =>
  112. let opcode = n % 100
  113. let mode1 = (n / 100) % 10
  114. let mode2 = (n / 1000) % 10
  115. let mode3 = (n / 10000) % 10
  116. (opcode, (mode1 == 1, mode2 == 1, mode3 == 1))
  117. fun ref do_binop(modes: Modes, op: {(I64, I64): I64}) ? =>
  118. let data = get_op_data(modes)?
  119. write_storage(data.tgt(), op(data.val1(), data.val2()))?
  120. pc = pc + 4
  121. fun ref do_jump(modes: Modes, jump_cond: {(I64): Bool}) ? =>
  122. (let cond, let tgt, _) = modes
  123. if jump_cond(get_value(pc+1, cond)?.value()) then
  124. pc = get_value(pc+2, tgt)?.value().usize()
  125. else
  126. pc = pc + 3
  127. end
  128. fun ref do_input(modes: Modes) ? =>
  129. let i = try
  130. input.shift()?
  131. else
  132. errors.push(NotEnoughInput(pc))
  133. error
  134. end
  135. write_storage(read_storage(pc+1)?.usize(), i)?
  136. pc = pc + 2
  137. fun ref do_output(modes: Modes) ? =>
  138. (let m, _, _) = modes
  139. output.push(get_value(pc+1, m)?.value())
  140. pc = pc + 2
  141. fun ref run() ? =>
  142. while true do
  143. (let opcode, let modes) = parse_op(read_storage(pc)?)
  144. dbg("running op " + Format.int[I64](opcode))
  145. match opcode
  146. | 1 => do_binop(modes, {(x, y) => x + y})?
  147. | 2 => do_binop(modes, {(x, y) => x * y})?
  148. | 3 => do_input(modes)?
  149. | 4 => do_output(modes)?
  150. | 5 => do_jump(modes, {(x) => x != 0})?
  151. | 6 => do_jump(modes, {(x) => x == 0})?
  152. | 7 => do_binop(modes, {(x, y) => if x < y then 1 else 0 end})?
  153. | 8 => do_binop(modes, {(x, y) => if x == y then 1 else 0 end})?
  154. | 99 => return None
  155. | let x: I64 =>
  156. errors.push(UnknownOperand(x))
  157. error
  158. end
  159. end
  160. fun ref run_trial(env: Env) =>
  161. try
  162. run()?
  163. else
  164. for err in errors.values() do
  165. env.out.print(err.message())
  166. end
  167. for n in debug.values() do
  168. env.out.print("[debug] " + n)
  169. end
  170. end
  171. for n in output.values() do
  172. env.out.print("> " + Format.int[I64](n))
  173. end
  174. actor Main
  175. new create(env: Env) =>
  176. let caps = recover val FileCaps.>set(FileRead).>set(FileStat) end
  177. try
  178. with
  179. file = OpenFile(FilePath(env.root as AmbientAuth, "input.txt", caps)?) as File
  180. do
  181. let program = Program.from_file(file, [5])
  182. program.run_trial(env)
  183. program.print(env)
  184. end
  185. else
  186. // if something failed, then print an error message of some kind and exit
  187. env.err.print("Couldn't read expected file `input.txt'")
  188. env.exitcode(99)
  189. end