Initial commit.
This commit is contained in:
143
brainfuck.erl
Normal file
143
brainfuck.erl
Normal file
@@ -0,0 +1,143 @@
|
||||
% A Brainfuck Interpreter in Erlang
|
||||
%
|
||||
% Copyright (c) 2015, Jens Luedicke <jens.luedicke@gmail.com>
|
||||
% All rights reserved.
|
||||
%
|
||||
% Redistribution and use in source and binary forms, with or without
|
||||
% modification, are permitted provided that the following conditions are met:
|
||||
% * Redistributions of source code must retain the above copyright
|
||||
% notice, this list of conditions and the following disclaimer.
|
||||
% * Redistributions in binary form must reproduce the above copyright
|
||||
% notice, this list of conditions and the following disclaimer in the
|
||||
% documentation and/or other materials provided with the distribution.
|
||||
% * Neither the name of the <organization> nor the
|
||||
% names of its contributors may be used to endorse or promote products
|
||||
% derived from this software without specific prior written permission.
|
||||
%
|
||||
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
% ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
% DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-module(brainfuck).
|
||||
-export([run/1]).
|
||||
|
||||
parse(Data) ->
|
||||
case Data of
|
||||
"+" -> opplus;
|
||||
"-" -> opminus;
|
||||
">" -> opnext;
|
||||
"<" -> opprev;
|
||||
"," -> opin;
|
||||
"." -> opout;
|
||||
"[" -> oploopbegin;
|
||||
"]" -> oploopend;
|
||||
_ -> opignore
|
||||
end.
|
||||
|
||||
findloop([], LoopCodes, _) ->
|
||||
LoopCodes;
|
||||
|
||||
findloop([Op|OpCodes], LoopCodes, LoopDepth) ->
|
||||
% io:format("findloop ~B ~n", [LoopDepth]),
|
||||
if
|
||||
(Op == oploopbegin) ->
|
||||
findloop(OpCodes, lists:flatten([LoopCodes] ++ [Op]), LoopDepth + 1);
|
||||
(Op == oploopend) and (LoopDepth > 1) ->
|
||||
findloop(OpCodes, lists:flatten([LoopCodes] ++ [Op]), LoopDepth - 1);
|
||||
(Op /= oploopbegin) and (Op /= oploopend) ->
|
||||
findloop(OpCodes, lists:flatten([LoopCodes] ++ [Op]), LoopDepth);
|
||||
(LoopDepth == 0) ->
|
||||
LoopCodes;
|
||||
true ->
|
||||
LoopCodes
|
||||
end.
|
||||
|
||||
evalloop(OpCodes, Tape, Pos) ->
|
||||
Value = maps:get(Pos, Tape, 0),
|
||||
% io:format("evalloop ~B ~B ~n", [Pos, Value]),
|
||||
|
||||
if
|
||||
Value /= 0 ->
|
||||
{NewTape, NewPos} = eval(OpCodes, Tape, Pos),
|
||||
evalloop(OpCodes, NewTape, NewPos);
|
||||
true ->
|
||||
% io:format("evalloop ~B ~B is 0. Stop.~n", [Pos, Value]),
|
||||
{Tape, Pos}
|
||||
end.
|
||||
|
||||
eval([], Tape, Pos) -> {Tape, Pos};
|
||||
|
||||
eval([Op|OpCodes], Tape, Pos) ->
|
||||
case Op of
|
||||
opplus ->
|
||||
Value = maps:get(Pos, Tape, 0) + 1,
|
||||
NewTape = maps:put(Pos, Value, Tape),
|
||||
% io:format("eval: opplus ~B ~B ~n", [Pos, Value]),
|
||||
eval(OpCodes, NewTape, Pos);
|
||||
opminus ->
|
||||
Value = maps:get(Pos, Tape, 0) - 1,
|
||||
NewTape = maps:put(Pos, Value, Tape),
|
||||
% io:format("eval: opminus ~B ~B ~n", [Pos, Value]),
|
||||
eval(OpCodes, NewTape, Pos);
|
||||
opnext ->
|
||||
% io:format("eval: opnext ~B ~B ~n", [Pos, Pos + 1]),
|
||||
eval(OpCodes, Tape, Pos + 1);
|
||||
opprev ->
|
||||
% io:format("eval: opprev ~B ~B ~n", [Pos, Pos - 1]),
|
||||
eval(OpCodes, Tape, Pos - 1);
|
||||
opin ->
|
||||
Data = io:get_chars("", 1),
|
||||
Char = lists:nth(1, Data),
|
||||
io:format("eval: opin ~B ~B ~n", [Pos, Char]),
|
||||
NewTape = maps:put(Pos, Char, Tape),
|
||||
eval(OpCodes, NewTape, Pos);
|
||||
opout ->
|
||||
Value = maps:get(Pos, Tape, 0),
|
||||
% io:format("eval: opout ~B ~B ~n", [Pos, Value]),
|
||||
io:format("~c", [Value]),
|
||||
eval(OpCodes, Tape, Pos);
|
||||
oploopbegin ->
|
||||
% io:format("eval: oploopbegin ~B ~n", [Pos]),
|
||||
LoopCodes = findloop(OpCodes, [], 1),
|
||||
{NewTape, NewPos} = evalloop(LoopCodes, Tape, Pos),
|
||||
eval(OpCodes -- LoopCodes, NewTape, NewPos);
|
||||
oploopend ->
|
||||
% io:format("eval: oploopend ~B ~n", [Pos]),
|
||||
eval(OpCodes, Tape, Pos);
|
||||
_ ->
|
||||
eval(OpCodes, Tape, Pos)
|
||||
end.
|
||||
|
||||
read_file(IoDevice, OpCodes) ->
|
||||
case file:read(IoDevice, 1) of
|
||||
{ok, Data} ->
|
||||
OpCode = parse(Data),
|
||||
read_file(IoDevice, lists:flatten([OpCodes] ++ [OpCode]));
|
||||
eof ->
|
||||
{ok, OpCodes};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
run(File) ->
|
||||
case file:open(File, read) of
|
||||
{ok, IoDevice} ->
|
||||
case read_file(IoDevice, []) of
|
||||
{ok, OpCodes} ->
|
||||
Tape = maps:new(),
|
||||
Pos = 0,
|
||||
eval(OpCodes, Tape, Pos);
|
||||
{error, Reason} ->
|
||||
io:format("Unable to read file ~s: ~s ~n", [File, Reason])
|
||||
end,
|
||||
file:close(IoDevice);
|
||||
{error, Reason} ->
|
||||
io:format("Unable to open file ~s: ~s ~n", [File, Reason])
|
||||
end.
|
||||
Reference in New Issue
Block a user