diff --git a/2015/lib/2015/7.ex b/2015/lib/2015/7.ex
index a166bc7..6598a08 100644
--- a/2015/lib/2015/7.ex
+++ b/2015/lib/2015/7.ex
@@ -2,13 +2,157 @@ import AOC
 
 aoc 2015, 7 do
   import NimbleParsec
+  use Bitwise
 
-  def parse_inst(line) do
+  defparsec(
+    :wire_label,
+    ascii_string([?a..?z], min: 1)
+    |> unwrap_and_tag(:wire)
+  )
+
+  defparsec(
+    :connects_to,
+    ignore(string(" -> "))
+  )
+
+  defparsec(
+    :signal,
+    integer(min: 1)
+    |> unwrap_and_tag(:signal)
+  )
+
+  defparsec(
+    :input,
+    choice([
+      parsec(:signal),
+      parsec(:wire_label)
+    ])
+  )
+
+  defparsec(
+    :and,
+    parsec(:input)
+    |> ignore(string(" AND "))
+    |> parsec(:input)
+    |> tag(:and)
+  )
+
+  defparsec(
+    :or,
+    parsec(:input)
+    |> ignore(string(" OR "))
+    |> parsec(:input)
+    |> tag(:or)
+  )
+
+  defparsec(
+    :lshift,
+    parsec(:wire_label)
+    |> ignore(string(" LSHIFT "))
+    |> integer(min: 1)
+    |> tag(:lshift)
+  )
+
+  defparsec(
+    :rshift,
+    parsec(:wire_label)
+    |> ignore(string(" RSHIFT "))
+    |> integer(min: 1)
+    |> tag(:rshift)
+  )
+
+  defparsec(
+    :not,
+    ignore(string("NOT "))
+    |> parsec(:wire_label)
+    |> tag(:not)
+  )
+
+  defparsec(
+    :instruction_parsec,
+    choice([
+      parsec(:and),
+      parsec(:or),
+      parsec(:lshift),
+      parsec(:rshift),
+      parsec(:not),
+      parsec(:input)
+    ])
+    |> parsec(:connects_to)
+    |> parsec(:wire_label)
+  )
+
+  def unwrap_parsec_result({:ok, [{tag, attrs}, {:wire, assign}], _, _, _, _}) do
+    attrs = List.wrap(attrs)
+    {assign, List.to_tuple([tag | attrs])}
   end
 
+  def parse_instruction(str),
+    do:
+      str
+      |> instruction_parsec()
+      |> unwrap_parsec_result()
+
+  def circuit(), do: input_stream() |> Stream.map(&parse_instruction/1) |> Enum.into(%{})
+
   def p1 do
+    :ets.new(:memo, [:set, :named_table])
+
+    circuit()
+    |> trace("a")
   end
 
   def p2 do
+    :ets.new(:memo, [:set, :named_table])
+
+    circuit()
+    |> Map.put("b", 956)
+    |> trace("a")
   end
+
+  def trace(circuit, wire) when is_binary(wire) do
+    case :ets.lookup(:memo, wire) do
+      [{^wire, value}] ->
+        value
+
+      _ ->
+        value = trace(circuit, Map.get(circuit, wire))
+        :ets.insert(:memo, {wire, value})
+        value
+    end
+  end
+
+  def trace(_circuit, signal) when is_integer(signal) do
+    signal
+  end
+
+  def trace(circuit, {:wire, wire}) do
+    trace(circuit, wire)
+  end
+
+  def trace(circuit, {:signal, signal}) do
+    trace(circuit, signal)
+  end
+
+  def trace(circuit, {:or, lhs, rhs}) do
+    trace(circuit, lhs) ||| trace(circuit, rhs)
+  end
+
+  def trace(circuit, {:and, lhs, rhs}) do
+    trace(circuit, lhs) &&& trace(circuit, rhs)
+  end
+
+  def trace(circuit, {:lshift, lhs, rhs}) do
+    trace(circuit, lhs) <<< trace(circuit, rhs)
+  end
+
+  def trace(circuit, {:rshift, lhs, rhs}) do
+    trace(circuit, lhs) >>> trace(circuit, rhs)
+  end
+
+  def trace(circuit, {:not, arg}) do
+    ~~~trace(circuit, arg)
+  end
+
+  def trace(_circuit, other), do: other
 end