Last active
January 17, 2017 10:37
-
-
Save parrot-studio/8efdb3f79c516d587ff44bbd1298723e to your computer and use it in GitHub Desktop.
BrainF**k interpreter by Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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