-
Notifications
You must be signed in to change notification settings - Fork 259
Expand file tree
/
Copy pathbf.ex
More file actions
99 lines (82 loc) · 2.12 KB
/
bf.ex
File metadata and controls
99 lines (82 loc) · 2.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
defmodule BF.Tape do
alias BF.Tape, as: Tape
defstruct data: :array.new(default: 0), pos: 0
def current(tape) do
:array.get(tape.pos, tape.data)
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}
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}
end
end
defmodule BF do
alias BF.Tape, as: Tape
def parse(source) do
{result, []} = parse(String.split(source, ""), [])
result
end
defp parse([], acc) do
{Enum.reverse(acc), []}
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)
end
end
def run([], tape) do
tape
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))
end
end
end
end
defmodule Benchmark do
def notify(msg) do
with {:ok, socket} <- :gen_tcp.connect('localhost', 9001, []) do
:gen_tcp.send(socket, msg)
:gen_tcp.close(socket)
end
end
def run do
[filename] = System.argv()
text = File.read!(filename)
notify("Elixir\t#{System.pid()}")
BF.parse(text)
|> BF.run(%BF.Tape{})
notify("stop")
end
end
Benchmark.run()