From 1aa4857097f505fa40394240d0a7dea3a03cc276 Mon Sep 17 00:00:00 2001 From: poland-can-into-space <40441297+poland-can-into-space@users.noreply.github.com> Date: Thu, 5 Sep 2019 15:48:17 +0900 Subject: [PATCH] Update bf.ex --- brainfuck2/bf.ex | 288 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 228 insertions(+), 60 deletions(-) diff --git a/brainfuck2/bf.ex b/brainfuck2/bf.ex index 94b0dde4..b3711bee 100644 --- a/brainfuck2/bf.ex +++ b/brainfuck2/bf.ex @@ -1,86 +1,254 @@ -defmodule BF.Tape do - alias BF.Tape, as: Tape - defstruct data: :array.new(default: 0), pos: 0 +defmodule BrainFuck do - def current(tape) do - :array.get(tape.pos, tape.data) + @code """ + >++[<+++++++++++++>-]<[[>+>+<<-]>[<+>-]++++++++ + [>++++++++<-]>.[-]<<>++++++++++[>++++++++++[>++ + ++++++++[>++++++++++[>++++++++++[>++++++++++[>+ + +++++++++[-]<-]<-]<-]<-]<-]<-]<-]++++++++++. + """ + + def code() do + @code + end + defmacro inc(n) do + {:inc, n} + end + defmacro move(n) do + {:move, n} + end + defmacro print do + {:print, :_} + end + def loop(rest) do + {rest, acc} = parse_main(rest) + {{:loop, acc}, rest} end - def inc(tape, x) when x == -1 or x == 1 do - new_data = :array.set(tape.pos, :array.get(tape.pos, tape.data) + x, - tape.data) - %Tape{tape | data: new_data} + def parse("+"<>rest), do: {inc(1), rest} + def parse("-"<>rest), do: {inc(-1), rest} + def parse(">"<>rest), do: {move(1), rest} + def parse("<"<>rest), do: {move(-1), rest} + def parse("."<>rest), do: {print(), rest} + def parse("["<>rest), do: loop(rest) + def parse("]"<>rest), do: {:break, rest} + def parse(""), do: {:break, ""} + def parse(rest) do + parse(String.replace_prefix(rest, String.first(rest), "")) + rescue _ -> + {:break, ""} end - def move(tape, x) when x == -1 or x == 1 do - new_pos = tape.pos + x - new_data = - if new_pos < :array.size(tape.data) do - tape.data - else - :array.set(new_pos, 0, tape.data) - end - %Tape{tape | data: new_data, pos: new_pos} + def parse_main(str, acc \\ []) do + case parse(str) do + {:break, rest} -> {rest, Enum.reverse(acc)} + {data, rest} -> + parse_main(rest, [data | acc]) + end end -end -defmodule BF do - alias BF.Tape, as: Tape + def main() do + {_, acc} = parse_main(@code) + acc + end + + def run() do + BrainFuck.Ets.new() + BrainFuck.EtsExec.main(main()) + end + + defmodule Ets do + import :ets + + def new() do + new(:state, [:named_table, :public, :set]) + end + + def new_row(pos) do + insert_new(:state, {pos, 0}) + end + + def inc(pos, n) do + update_counter(:state, pos, n) + end + + def fetch(pos) do + case lookup(:state, pos) do + [] -> nil + [{_key, v}] -> v + end + end - def parse(source) do - {result, []} = parse(String.split(source, ""), []) - result end - defp parse([], acc) do - {Enum.reverse(acc), []} + defmodule Proc do + def start_link do + {:ok, pid} = Agent.start_link(fn -> + 0 + end, []) + pid + end + + def inc(pid, n) do + Agent.update(pid, & &1 + n) + end + + def fetch(pid) do + Agent.get(pid, & &1) + end end - defp parse([x | xs], acc) do - case x do - "+" -> parse(xs, [{:inc, 1} | acc]) - "-" -> parse(xs, [{:inc, -1} | acc]) - ">" -> parse(xs, [{:move, 1} | acc]) - "<" -> parse(xs, [{:move, -1} | acc]) - "." -> parse(xs, [{:print, nil} | acc]) - "[" -> - {loop_code, new_xs} = parse(xs, []) - parse(new_xs, [{:loop, loop_code} | acc]) - "]" -> {Enum.reverse(acc), xs} - _ -> parse(xs, acc) + + defmodule ProcExec do + import BrainFuck.Proc + + def get_value(tape, pos) do + fetch(get_pid(tape, pos)) + end + def get_pid(tape, pos) do + Enum.at(tape, pos) + end + + # inst for instruction + def main(inst, tape \\ [], pos \\ 0) + def main(inst, [], pos) do + pid = start_link() + main(inst, [pid], pos) + end + def main(whole = [h | rest], tape, pos) do + {inst, tape, pos} = + case h do + {:inc, n} -> + inc(get_pid(tape, pos), n) + {rest, tape, pos} + {:move, n} -> + new_pos = pos + n + new_tape = + case Enum.at(tape, new_pos) do + nil -> + pid = start_link() + List.insert_at(tape, new_pos, pid) + _ -> + tape + end + {rest, new_tape, new_pos} + {:print, _} -> + :ok = IO.binwrite([get_value(tape, pos)]) + :file.sync(:stdout) + {rest, tape, pos} + {:loop, loop_code} -> + case get_value(tape, pos) do + 0 -> {rest, tape, pos} + _ -> + {tape, pos} = main(loop_code, tape, pos) + {whole, tape, pos} + end + end + + main(inst, tape, pos) + end + def main(_, tape, pos) do + {tape, pos} end + end - def run([], tape) do - tape + + defmodule EtsExec do + import BrainFuck.Ets + + # inst for instruction + def main(inst, pos \\ 0) + + def main(whole = [h | rest], pos) do + {inst, new_pos} = + case h do + {:inc, n} -> + inc(pos, n) + {rest, pos} + {:move, n} -> + new_row(pos + n) + {rest, pos + n} + {:print, _} -> + :ok = IO.binwrite([fetch(pos)]) + :file.sync(:stdout) + {rest, pos} + {:loop, loop_code} -> + case fetch(pos) do + 0 -> {rest, pos} + _ -> + pos = main(loop_code, pos) + {whole, pos} + end + end + + main(inst, new_pos) + end + def main(_, pos) do + pos + end + end - def run(program = [op | ops], tape) do - case op do - {:inc, x} -> run(ops, Tape.inc(tape, x)) - {:move, x} -> run(ops, Tape.move(tape, x)) - {:print, nil} -> - :ok = IO.binwrite([Tape.current(tape)]) - :file.sync(:stdout) - run(ops, tape) - {:loop, loop_code} -> - if Tape.current(tape) == 0 do - run(ops, tape) - else - run(program, run(loop_code, tape)) + defmodule FullyEtsExec do + import :ets + + def tables() do + new(:pos, [:named_table, :set]) + insert_new(:pos, {0, 0}) + new(:state, [:named_table, :set]) + end + + def new_row(pos) do + insert_new(:state, {pos, 0}) + end + + def inc(pos, n) do + update_counter(:state, pos, n) + end + + def fetch() do + [{_, pos}] = lookup(:pos, 0) + case lookup(:state, pos) do + [] -> nil + [{_key, v}] -> v + end + end + + def main(whole = [h | rest]) do + x = + case h do + {:inc, n} -> + [{_, pos}] = lookup(:pos, 0) + update_counter(:state, pos, n) + {:move, n} -> + pos = update_counter(:pos, 0, n) + insert_new(:state, {pos, 0}) + {:print, _} -> + :ok = IO.binwrite([fetch()]) + :file.sync(:stdout) + {:loop, loop_code} -> + case fetch() do + 0 -> :ok + _ -> + main(loop_code) + :whole + end end + case x do + :whole -> main(whole) + _ -> main(rest) + end + end + def main(_, pos) do + pos end end + end -defmodule Benchmark do +defmodule BF do def run do - [filename] = System.argv() - File.read!(filename) - |> BF.parse() - |> BF.run(%BF.Tape{}) + BrainFuck.run() end end - -Benchmark.run()