Create a gist now

Instantly share code, notes, and snippets.

BrainF**k interpreter by Elixir
defmodule BF do
@cmap %{
">" => :pinc,
"<" => :pdec,
"+" => :inc,
"-" => :dec,
"." => :out,
"," => :inp,
"[" => :jmp,
"]" => :ret
}
def execute(code) do
code
|> parse
|> eval
end
defp parse(str) do
str
|> String.replace("\n", "")
|> String.codepoints
|> List.foldl([], fn c, li -> li ++ [@cmap[c]] end)
end
defp eval(list) do
coms = list
|> Enum.with_index
|> Enum.map(fn {val, ind} -> {ind, val} end)
jump_map = parse_jump(coms)
cm = Enum.into(coms, %{})
step(cm[0], cm, jump_map, 1, 0, 0, %{}, [])
end
defp parse_jump(coms) do
sj = coms
|> Enum.filter(fn {_, c} -> c == :jmp end)
|> Enum.map(fn {i, _} -> i end)
ej = coms
|> Enum.filter(fn {_, c} -> c == :ret end)
|> Enum.map(fn {i, _} -> i end)
|> Enum.reverse
list = Enum.zip(sj, ej) ++ Enum.zip(ej, sj)
Enum.into(list, %{})
end
defp increment(pind, buf) do
Map.update(buf, pind, 1, &(&1 + 1))
end
defp decrement(pind, buf) do
Map.update(buf, pind, -1, &(&1 - 1))
end
defp output(result, buf, pind) do
result ++ [Map.get(buf, pind, 0)]
end
defp step(:pinc, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind+1, buf, result)
end
defp step(:pdec, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind-1, buf, result)
end
defp step(:inc, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind, increment(pind, buf), result)
end
defp step(:dec, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind, decrement(pind, buf), result)
end
defp step(:out, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind, buf, output(result, buf, pind))
end
defp step(:inp, coms, jump_map, ind, cind, pind, buf, result) do
step(coms[cind+1], coms, jump_map, ind+1, cind+1, pind, buf, result) # skip
end
defp step(:jmp, coms, jump_map, ind, cind, pind, buf, result) do
ci = jump_to(Map.get(buf, pind, 0), Map.get(jump_map, cind), cind)
step(coms[ci], coms, jump_map, ind+1, ci, pind, buf, result)
end
defp step(:ret, coms, jump_map, ind, cind, pind, buf, result) do
ci = return_to(Map.get(buf, pind, 0), Map.get(jump_map, cind), cind)
step(coms[ci], coms, jump_map, ind+1, ci, pind, buf, result)
end
defp step(_, _coms, _jump_map, _ind, _cind, _pind, _buf, result), do: result
defp jump_to(0, mi, _cind) when mi >= 0, do: mi
defp jump_to(bv, mi, cind) when (bv > 0 and mi >= 0), do: cind+1
defp return_to(0, mi, cind) when mi >= 0, do: cind+1
defp return_to(bv, mi, _cind) when (bv > 0 and mi >= 0), do: mi
end
hello = """
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.
+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.
"""
IO.puts BF.execute(hello)
# elixir bf.exs # => Hello, world!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment