From 52b8f7f0357194ecfe82ef063e579a3af7e1bd07 Mon Sep 17 00:00:00 2001 From: Jens Luedicke Date: Sun, 1 Feb 2015 12:30:23 +0100 Subject: [PATCH] Initial commit. --- brainfuck.erl | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ hello.bf | 2 + 2 files changed, 145 insertions(+) create mode 100644 brainfuck.erl create mode 100644 hello.bf diff --git a/brainfuck.erl b/brainfuck.erl new file mode 100644 index 0000000..5122af0 --- /dev/null +++ b/brainfuck.erl @@ -0,0 +1,143 @@ +% A Brainfuck Interpreter in Erlang +% +% Copyright (c) 2015, Jens Luedicke +% 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 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 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. diff --git a/hello.bf b/hello.bf new file mode 100644 index 0000000..0f8b7da --- /dev/null +++ b/hello.bf @@ -0,0 +1,2 @@ +>++++++++[<+++++++++>-]<.>>+>+>++>[-]+<[>[->+<<++++>]<<]>.+++++++..+++.> +>+++++++.<<<[[-]<[-]>]<+++++++++++++++.>>.+++.------.--------.>>+.>++++.