Add simple Intcode assembler

This commit is contained in:
Shadowfacts 2019-12-05 22:48:17 -05:00
parent 1b94a352b1
commit b60c9a9a63
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
2 changed files with 162 additions and 3 deletions

View File

@ -66,11 +66,12 @@ defmodule Day5 do
def run(memory, ip \\ 0)
def run(memory, ip) when ip < length(memory) do
IO.puts("IP: #{ip}")
IO.inspect(memory)
case eval(Enum.drop(memory, ip), memory) do
{memory, :halt} ->
memory
{_memory, :halt} ->
:halt
{memory, :cont, offset} ->
run(memory, ip + offset)
@ -78,6 +79,8 @@ defmodule Day5 do
{memory, :jump, ip} ->
run(memory, ip)
end
:ok
end
def run(memory, _ip), do: memory
@ -146,7 +149,8 @@ defmodule Day5 do
# output
def eval([op, param | _], memory) when opcode(op, 4) do
get_param(op, 0, param, memory) |> IO.inspect()
out = get_param(op, 0, param, memory)
IO.puts("Output: #{out}")
{memory, :cont, 2}
end

155
lib/intcode/assembler.ex Normal file
View File

@ -0,0 +1,155 @@
defmodule Assembler do
@asm """
in #30
clt 30 #8 #31
jnz 31 #lessThan
ceq 30 #8 #31
jnz 31 #equal
out #1001
hlt
lessThan:
out #999
hlt
equal:
out #1000
hlt
"""
def test do
@asm
|> IO.inspect()
|> assemble(32)
end
def assemble(asm, pad_length \\ 0) do
{memory, labels} =
asm
|> String.split("\n")
|> Enum.reject(&(String.length(&1) == 0))
|> Enum.reduce({[], %{}}, fn line, {memory, labels} ->
cond do
Regex.match?(~r/^\s+$/, line) ->
{memory, labels}
String.starts_with?(line, " ") ->
{
assemble_instruction(String.slice(line, 2..-1), memory),
labels
}
String.ends_with?(line, ":") ->
{
memory,
Map.put(labels, String.slice(line, 0..-2), length(memory))
}
true ->
IO.inspect("Ignoring line: #{line}")
end
end)
memory =
Enum.map(memory, fn
{:label, name} -> Map.fetch!(labels, name)
it -> it
end)
memory =
if pad_length > length(memory) do
repeat(0, pad_length - length(memory)) ++ memory
else
memory
end
IO.inspect(labels)
Enum.reverse(memory)
end
def repeat(_, 0), do: []
def repeat(n, count), do: [n | repeat(n, count - 1)]
def pow(base, 1), do: base
def pow(base, exp), do: base * pow(base, exp - 1)
def parse_params(params, param_types) do
params
|> String.split(" ")
|> Enum.zip(param_types)
|> Enum.with_index()
|> Enum.map_reduce(0, fn {{param, type}, index}, modes ->
val =
case Regex.run(~r/^#?(\d+)$/, param) do
[_, digits] ->
String.to_integer(digits)
_ ->
[_, name] = Regex.run(~r/^#(\w+)$/, param)
{:label, name}
end
modes =
if type == :read && String.starts_with?(param, "#") do
modes + pow(10, index + 2)
else
modes
end
{val, modes}
end)
end
def assemble_instruction(insn, memory \\ [])
def assemble_instruction("add " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 1 + modes | memory]
end
def assemble_instruction("mul " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 2 + modes | memory]
end
def assemble_instruction("hlt", memory) do
[99 | memory]
end
def assemble_instruction("in " <> params, memory) do
{[dest], modes} = parse_params(params, [:write])
[dest, 3 + modes | memory]
end
def assemble_instruction("out " <> params, memory) do
{[val], modes} = parse_params(params, [:read])
[val, 4 + modes | memory]
end
def assemble_instruction("jnz " <> params, memory) do
{[target, dest], modes} = parse_params(params, [:read, :read])
[dest, target, 5 + modes | memory]
end
def assemble_instruction("jez " <> params, memory) do
{[target, dest], modes} = parse_params(params, [:read, :read])
[dest, target, 6 + modes | memory]
end
def assemble_instruction("clt " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 7 + modes | memory]
end
def assemble_instruction("ceq " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 8 + modes | memory]
end
end