Intcode assembler: add tests

This commit is contained in:
Shadowfacts 2019-12-06 11:19:39 -05:00
parent b933c30bea
commit cbbd6d8692
2 changed files with 173 additions and 3 deletions

View File

@ -67,16 +67,39 @@ defmodule Assembler do
it -> it it -> it
end) end)
IO.inspect(labels) # IO.inspect(labels)
Enum.reverse(memory) Enum.reverse(memory)
end end
def repeat(_, 0), do: [] @doc """
def repeat(n, count), do: [n | repeat(n, count - 1)] Raises the base to to the given power.
## Examples
iex> Assembler.pow(10, 3)
1000
iex> Assembler.pow(5, 4)
625
"""
def pow(base, 1), do: base def pow(base, 1), do: base
def pow(base, exp), do: base * pow(base, exp - 1) def pow(base, exp), do: base * pow(base, exp - 1)
@doc """
Parses assembly instruction parameter values and modes.
## Examples
iex> Assembler.parse_params("1 2 3", [:read, :read, :read])
{[1, 2, 3], 11100}
iex> Assembler.parse_params("1 2 3", [:read, :read, :write])
{[1, 2, 3], 1100}
iex> Assembler.parse_params("1 2 $3", [:read, :read, :write])
{[1, 2, 3], 1100}
iex> Assembler.parse_params("$1 2 3", [:read, :read, :write])
{[1, 2, 3], 1000}
iex> Assembler.parse_params("$label 2 3", [:read, :read, :write])
{[{:label, "label"}, 2, 3], 1000}
iex> Assembler.parse_params("1 label 3", [:read, :read, :write])
{[1, {:label, "label"}, 3], 1100}
"""
def parse_params(params, param_types) do def parse_params(params, param_types) do
params params
|> String.split(" ") |> String.split(" ")
@ -110,54 +133,130 @@ defmodule Assembler do
end) end)
end end
@doc """
Assembles the given instruction and adds it to the memory (in reverse order).
"""
def assemble_instruction(insn, memory \\ []) def assemble_instruction(insn, memory \\ [])
@doc """
Assemble add instruction.
## Examples
iex> Assembler.assemble_instruction("add 1 2 3")
[3, 2, 1, 1101]
"""
def assemble_instruction("add " <> params, memory) do def assemble_instruction("add " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) {[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 1 + modes | memory] [dest, b, a, 1 + modes | memory]
end end
@doc """
Assemble multiply instruction.
## Examples
iex> Assembler.assemble_instruction("mul 1 2 3")
[3, 2, 1, 1102]
"""
def assemble_instruction("mul " <> params, memory) do def assemble_instruction("mul " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) {[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 2 + modes | memory] [dest, b, a, 2 + modes | memory]
end end
@doc """
Assemble halt instruction.
## Examples
iex> Assembler.assemble_instruction("hlt")
[99]
"""
def assemble_instruction("hlt", memory) do def assemble_instruction("hlt", memory) do
[99 | memory] [99 | memory]
end end
@doc """
Assemble input instruction.
## Examples
iex> Assembler.assemble_instruction("in 7")
[7, 3]
"""
def assemble_instruction("in " <> params, memory) do def assemble_instruction("in " <> params, memory) do
{[dest], modes} = parse_params(params, [:write]) {[dest], modes} = parse_params(params, [:write])
[dest, 3 + modes | memory] [dest, 3 + modes | memory]
end end
@doc """
Assemble input instruction.
## Examples
iex> Assembler.assemble_instruction("out 7")
[7, 104]
iex> Assembler.assemble_instruction("out $7")
[7, 4]
"""
def assemble_instruction("out " <> params, memory) do def assemble_instruction("out " <> params, memory) do
{[val], modes} = parse_params(params, [:read]) {[val], modes} = parse_params(params, [:read])
[val, 4 + modes | memory] [val, 4 + modes | memory]
end end
@doc """
Assemble jump-if-non-zero instruction.
## Examples
iex> Assembler.assemble_instruction("jnz 1 2")
[2, 1, 105]
iex> Assembler.assemble_instruction("jnz $var 2")
[2, {:label, "var"}, 5]
"""
def assemble_instruction("jnz " <> params, memory) do def assemble_instruction("jnz " <> params, memory) do
{[target, dest], modes} = parse_params(params, [:read, :read]) {[target, dest], modes} = parse_params(params, [:read, :read])
[dest, target, 5 + modes | memory] [dest, target, 5 + modes | memory]
end end
@doc """
Assemble jump-if-zero instruction.
## Examples
iex> Assembler.assemble_instruction("jez 1 2")
[2, 1, 106]
iex> Assembler.assemble_instruction("jez $var 2")
[2, {:label, "var"}, 6]
"""
def assemble_instruction("jez " <> params, memory) do def assemble_instruction("jez " <> params, memory) do
{[target, dest], modes} = parse_params(params, [:read, :read]) {[target, dest], modes} = parse_params(params, [:read, :read])
[dest, target, 6 + modes | memory] [dest, target, 6 + modes | memory]
end end
@doc """
Assemble compare-less-than instruction.
## Examples
iex> Assembler.assemble_instruction("clt 1 2 3")
[3, 2, 1, 1107]
iex> Assembler.assemble_instruction("clt $1 $2 3")
[3, 2, 1, 7]
"""
def assemble_instruction("clt " <> params, memory) do def assemble_instruction("clt " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) {[a, b, dest], modes} = parse_params(params, [:read, :read, :write])
[dest, b, a, 7 + modes | memory] [dest, b, a, 7 + modes | memory]
end end
@doc """
Assemble compare-equal instruction.
## Examples
iex> Assembler.assemble_instruction("ceq 1 2 3")
[3, 2, 1, 1108]
iex> Assembler.assemble_instruction("ceq $1 $2 3")
[3, 2, 1, 8]
"""
def assemble_instruction("ceq " <> params, memory) do def assemble_instruction("ceq " <> params, memory) do
{[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) {[a, b, dest], modes} = parse_params(params, [:read, :read, :write])

View File

@ -0,0 +1,71 @@
defmodule AssemblerTest do
use ExUnit.Case
doctest Assembler
test "assembles empty program" do
assert Assembler.assemble("") == []
end
test "assembles simple program" do
program = """
add 1 2 10
mul 2 3 11
add $10 $11 12
hlt
"""
assert Assembler.assemble(program) == [
1101,
1,
2,
10,
1102,
2,
3,
11,
1,
10,
11,
12,
99
]
end
test "assembles simple program with a label" do
program = """
add 1 2 10
jnz $10 nonZero
nonZero:
hlt
"""
assert Assembler.assemble(program) == [
1101,
1,
2,
10,
1005,
10,
7,
99
]
end
test "assembles a simple program with a data label" do
program = """
add 1 2 var
out $var
var: 0
"""
assert Assembler.assemble(program) == [
1101,
1,
2,
6,
4,
6,
0
]
end
end