From d4e68f6062053a608b2c7901fa1e56ff1b1135bf Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 7 Dec 2019 22:47:48 -0500 Subject: [PATCH] Add assembler macros --- .formatter.exs | 3 +- lib/intcode/assembler.ex | 129 +++++++++++++++++++++----------- test/intcode/assembler_test.exs | 46 +++++++----- test/intcode/cmp8.asm | 19 +++++ 4 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 test/intcode/cmp8.asm diff --git a/.formatter.exs b/.formatter.exs index d2cda26..eda0d42 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,4 +1,5 @@ # Used by "mix format" [ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], + locals_without_parens: [assemble_macro: 2] ] diff --git a/lib/intcode/assembler.ex b/lib/intcode/assembler.ex index 4955107..9a5cddb 100644 --- a/lib/intcode/assembler.ex +++ b/lib/intcode/assembler.ex @@ -1,5 +1,17 @@ +defmodule Assembler.Helpers do + defmacro assemble_macro(match, asm) do + quote do + def assemble_insn(unquote(match), memory) do + {macro_mem, _} = do_assemble(unquote(asm), length(memory)) + macro_mem ++ memory + end + end + end +end + defmodule Assembler do import NimbleParsec + import Assembler.Helpers label = ascii_string([?a..?z, ?A..?Z, ?_], min: 1) @@ -21,7 +33,7 @@ defmodule Assembler do |> ascii_string([{:not, ?,}], min: 1) instruction = - ignore(ascii_string([?\s], 2)) + ignore(ascii_string([?\s, ?\t], min: 0)) |> ascii_string([?a..?z], min: 1) |> repeat(param |> ignore(ascii_char([?,]))) |> optional(param) @@ -37,45 +49,14 @@ defmodule Assembler do ]) ) - @asm """ - in res - clt $res, _self + 1, cmpRes - jnz $cmpRes, lessThan - ceq $res, 8, cmpRes - jnz $cmpRes, equal - out 1001 - hlt - - lessThan: - out 999 - hlt - - equal: - out 1000 - hlt - - res: 0 - cmpRes: 0 - """ def test do - assemble(@asm) + File.read!("test/intcode/cmp8.asm") + |> assemble() + |> Day5.run_cli() end - def assemble(asm) do - {memory, labels} = - asm - |> String.trim_trailing("\n") - |> String.split("\n") - |> Enum.map(fn line -> - case parse_line(line) do - {:ok, res, _, _, _, _} -> - res - - err -> - raise "Unable to parse line: '#{line}':\n#{inspect(err)}" - end - end) - |> Enum.reduce({[], %{}}, &assemble_line/2) + def assemble(asm, offset \\ 0) do + {memory, labels} = do_assemble(asm, offset) memory |> Enum.reverse() @@ -92,23 +73,47 @@ defmodule Assembler do end) end - def assemble_line([], acc), do: acc + def do_assemble(asm, offset) do + asm + |> String.trim_trailing("\n") + |> String.split("\n") + |> Enum.map(fn line -> + case parse_line(line) do + {:ok, res, _, _, _, _} -> + res - def assemble_line([label: [name]], {memory, labels}) do + err -> + raise "Unable to parse line: '#{line}':\n#{inspect(err)}" + end + end) + |> Enum.reduce({[], %{}}, fn line, acc -> + assemble_line(line, acc, offset) + end) + end + + def assemble_line([], acc, _), do: acc + + def assemble_line([label: [name]], {memory, labels}, offset) do { memory, - Map.put(labels, name, length(memory)) + Map.put(labels, name, length(memory) + offset) } end - def assemble_line([data_label: [name, value]], {memory, labels}) do + def assemble_line([data_label: [name, value]], {memory, labels}, _) do + value = + case value do + {:neg, [val]} -> -val + val when is_integer(val) -> val + end + { [value | memory], Map.put(labels, name, length(memory)) } end - def assemble_line([insn: insn], {memory, labels}) do + def assemble_line([insn: insn], {memory, labels}, _) do { assemble_insn(insn, memory), labels @@ -159,6 +164,46 @@ defmodule Assembler do [99 | memory] end + # macros + assemble_macro ["mov", src, dest], + """ + add 0, #{src}, #{dest} + """ + + assemble_macro ["jmp", dest], + """ + jnz _self, #{dest} + """ + + assemble_macro ["sub", a, b, dest], + """ + mul #{b}, -1, #{dest} + add #{a}, $#{dest}, #{dest} + """ + + assemble_macro ["cle", a, b, dest], + """ + clt #{a}, #{b}, #{dest} + jnz $#{dest}, end + ceq #{a}, #{b}, #{dest} + + end: + """ + + assemble_macro ["cgt", a, b, dest], + """ + clt #{a}, #{b}, #{dest} + jnz $#{dest}, false + ceq #{a}, #{b}, #{dest} + jnz $#{dest}, false + mov 1, #{dest} + jmp end + + false: + mov 0, #{dest} + end: + """ + @doc """ Raises the base to to the given power. diff --git a/test/intcode/assembler_test.exs b/test/intcode/assembler_test.exs index a757d13..4929dda 100644 --- a/test/intcode/assembler_test.exs +++ b/test/intcode/assembler_test.exs @@ -8,28 +8,34 @@ defmodule AssemblerTest do end test "assembles instructions correctly" do - assert assemble(" add 1, 2, 3") == [1101, 1, 2, 3] - assert assemble(" mul 1, 2, 3") == [1102, 1, 2, 3] - assert assemble(" in 7") == [3, 7] - assert assemble(" out 7") == [104, 7] - assert assemble(" out $7") == [4, 7] - assert assemble(" jnz 1, 2") == [1105, 1, 2] - assert assemble(" jnz $1, 2") == [1005, 1, 2] - assert assemble(" jez 1, 2") == [1106, 1, 2] - assert assemble(" jez $1, 2") == [1006, 1, 2] - assert assemble(" clt 1, 2, 3") == [1107, 1, 2, 3] - assert assemble(" clt $1, $2, $3") == [7, 1, 2, 3] - assert assemble(" ceq 1, 2, 3") == [1108, 1, 2, 3] - assert assemble(" ceq $1, $2, $3") == [8, 1, 2, 3] - assert assemble(" hlt") == [99] + assert assemble("add 1, 2, 3") == [1101, 1, 2, 3] + assert assemble("mul 1, 2, 3") == [1102, 1, 2, 3] + assert assemble("in 7") == [3, 7] + assert assemble("out 7") == [104, 7] + assert assemble("out $7") == [4, 7] + assert assemble("jnz 1, 2") == [1105, 1, 2] + assert assemble("jnz $1, 2") == [1005, 1, 2] + assert assemble("jez 1, 2") == [1106, 1, 2] + assert assemble("jez $1, 2") == [1006, 1, 2] + assert assemble("clt 1, 2, 3") == [1107, 1, 2, 3] + assert assemble("clt $1, $2, $3") == [7, 1, 2, 3] + assert assemble("ceq 1, 2, 3") == [1108, 1, 2, 3] + assert assemble("ceq $1, $2, $3") == [8, 1, 2, 3] + assert assemble("hlt") == [99] + end + + test "assembles macros" do + assert assemble("mov 1, 2") == [1101, 0, 1, 2] + assert assemble("jmp 2") == [1105, 1, 2] + assert assemble("sub 5, 3, res\nres: 0") == [1102, 3, -1, 8, 101, 5, 8, 8, 0] end test "assembles simple program" do program = """ - add 1, 2, 10 - mul 2, 3, 11 - add $10, $11, 12 - hlt + add 1, 2, 10 + mul 2, 3, 11 + add $10, $11, 12 + hlt """ assert assemble(program) == [ @@ -96,8 +102,8 @@ defmodule AssemblerTest do end test "assembles parameters with expressions" do - assert assemble(" add 1, _self + 1, 3") == [1101, 1, 3, 3] - assert assemble(" add 1, $(_self + 1), 3") == [101, 1, 3, 3] + assert assemble("add 1, _self + 1, 3") == [1101, 1, 3, 3] + assert assemble("add 1, $(_self + 1), 3") == [101, 1, 3, 3] assert assemble(""" add 1, label + 4 - 2, 3 diff --git a/test/intcode/cmp8.asm b/test/intcode/cmp8.asm new file mode 100644 index 0000000..75db2ad --- /dev/null +++ b/test/intcode/cmp8.asm @@ -0,0 +1,19 @@ + in res + clt $res, 8, cmpRes + jnz $(res + 1), lessThan + ceq $res, 8, cmpRes + jnz $cmpRes, equal + out 1001 + hlt + +lessThan: + out 999 + hlt + +equal: + out 1000 + hlt + +res: 0 +cmpRes: 0 +