%%% BEGIN bf.erl %%% %%% %%% bf - pedantic Brainf*ck interpreter in Erlang %%% Copyright (c)2002 Cat's Eye Technologies. 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 Cat's Eye Technologies 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 THE REGENTS OR CONTRIBUTORS 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. %% @doc Cat's Eye Technologies' Erlang Brainf*ck Interpreter. %% %%

An implementation of the world's most beautifulest langugage %% in the world's second most beautifulest language.

%%

This program demonstrates:

%% %%

Variable names used in this program:

%% %% %% @end -module(bf). -vsn('2002.1208'). -copyright('Copyright (c)2002 Cat`s Eye Technologies. All rights reserved.'). -export([interpret/1, interpret/2, test/1]). %% @spec interpret(B::[instruction()], StorageSize::integer()) -> %% {integer(), tuple()} %% instruction() = integer() %% @doc The main user interface to the Brainf*ck interpreter. %% The user generally passes a list, which is parsed into a tuple. %% In this tuple, each Brainf*ck instruction represented by %% an atom, except for [ and ], %% which are represented by a nested tuple to make interpretation simpler. %% The return value is a tuple containing the data pointer and %% another tuple representing the state of the Brainf*ck tape %% after all is said and done. interpret(B, N) when tuple(B) -> interpret(1, B, 1, erlang:make_tuple(N, 0)); interpret(B, N) when list(B) -> interpret(parse(B), N). %% @spec interpret(B::[instruction()]) -> {integer(), tuple()} %% @equiv interpret(Program, 512) interpret(B) -> interpret(B, 512). % default memsize, use interpret/2 to specify %% @spec interpret(I::integer(), B::[instruction()], D::integer(), M::tuple) -> {integer(), tuple()} %% @doc The internal driver which implements the execution loop. %% When the I pointer is at the end of the program, processing is finished. %% But more usually, I will be travelling forward through B. interpret(I, B, D, M) when I > size(B) -> {D, M}; interpret(I, B, D, M) -> {D2, M2} = execute(element(I, B), D, M), interpret(I + 1, B, D2, M2). %% @spec execute(instruction(), D::integer(), M::tuple) -> {integer(), tuple()} %% @doc Executes specific, individual instructions. Erlang doesn't have an %% updatable store like Brainf*ck (unless you count the process dictionary %% (which you probably shouldn't,)) so instead we approach the problem by %% continually deriving new stores from old stores, returning the new %% store to the caller each time this function is called. execute($>, D, M) -> {D + 1, M}; execute($<, D, M) -> {D - 1, M}; execute($+, D, M) -> {D, setelement(D, M, element(D, M) + 1)}; execute($-, D, M) -> {D, setelement(D, M, element(D, M) - 1)}; %%

I/O is fairly crude, and could stand to be improved.

execute($., D, M) -> io:put_chars([element(D, M)]), {D, M}; execute($,, D, M) -> {D, setelement(D, M, hd(io:get_chars('bf> ', 1)))}; %%

The 'while' loop. A tuple represents a [...] structure; if %% the data pointer points to a non-zero, the nested Brainf*ck %% subprogram is executed, and the check is repeated.

execute(B, D, M) when tuple(B), element(D, M) == 0 -> {D, M}; execute(B, D, M) when tuple(B) -> {D2, M2} = interpret(1, B, D, M), execute(B, D2, M2); %%

Finally, comments and other line noise are ignored.

execute(_, D, M) -> {D, M}. %% @spec parse([instruction()]) -> tuple() %% @doc Takes a string (list of ASCII values) and butchers %% it into a nested tuple data structure, suitable for interpretation. %% Writing this function elegantly %% was much trickier than writing the imperative processing engine above. parse(L) -> parse({}, L). % default is to add to fresh tuple parse(U, []) -> U; parse(U, [$]|T]) -> {U, T}; parse(U, [$[|T]) -> {V, L} = parse({}, T), parse(erlang:append_element(U, V), L); parse(U, [H |T]) -> parse(erlang:append_element(U, H), T). %% @spec test(Test::integer()) -> {integer(), tuple()} %% @doc Test functions, numbered from 1 upwards. They implement two of the %% programs from the original Brainf*ck archive (hello and atoi). test(1) -> test(hello); test(hello) -> interpret(" >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[ <++++>-]<+.[-]++++++++++." ); test(2) -> test(atoi); test(atoi) -> interpret(" ==== ==== ==== cont digi num ==== ==== ==== + [ - cont=0 >, ======SUB10====== ---------- [ not 10 <+> cont=1 =====SUB38====== ---------- ---------- ---------- -------- > =====MUL10======= [>+>+<<-]>>[<<+>>-]< dup >>>+++++++++ [ <<< [>+>+<<-]>>[<<+>>-]< dup [<<+>>-] >>- ] <<<[-]< ======RMOVE1====== < [>+<-] ] < ] >>[<<+>>-]<< #" ). %%% END of bf.erl %%%