%%% BEGIN pibfi.erl %%% %%% %%% pibfi - Platonic Ideal Brainf*ck Interpreter %%% Copyright (c)2003 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 Platonic Ideal Brainf*ck Interpreter (pibfi). %% %%

This application implements an interpreter for the language %% Brainf*ck. It does not by default impose any limit on the maximum %% length of the tape, nor the maximum value that can entered into %% any of the cells in the tape (beyond those limits inescapably %% imposed upon it by the underlying operating system and hardware.) %% It can, however, be configured to simulate the limits imposed upon %% the language by many other implementations which sacrifice %% scaleability in order to achieve other goals (usually, to build an %% astonishingly small compiler or interpreter.)

%% %%

As such, pibfi may be in a position to one day %% develop into a universal (or at least reference) interpreter for %% the Brainf*ck language.

%% %%

For a synopsis of the command-line options that can be used %% with pibfi, see the %% pibfi_options %% documentation.

%% %%

This module contains the interface to start pibfi %% both from the command line, and programmatically. It also contains %% common functions used by other pibfi_* modules.

%% %%

Parts of this program were derived from the Erlang example %% program bf.erl.

%% %% @end -module(pibfi). -vsn('2003.0505'). -copyright('Copyright (c)2003 Cat`s Eye Technologies. All rights reserved.'). -export([run/1, startup/2, run/6]). -export([wrap/3, assert/2, assert_in_bounds/4]). -export([whisper/1, whisper/2]). -export([os_eol/0]). %% @spec run(Args::[string()]) -> halt() %% @doc Starts pibfi for the purposes of running a Brainf*ck %% program. This function is intended to be invoked %% from the command line. When using pibfi from another Erlang %% program, or from the Erlang shell, it is suggested you call %% run/6 instead. run([Filename]) -> Supervisor = pibfi_supervisor:start(), pibfi_supervisor:spawn_link(Supervisor, "startup", critical, ?MODULE, startup, [Supervisor, Filename]). startup(Supervisor, Filename) -> ParserOptions = pibfi_options:get_opts( [ {dontstrip, ""}, {optimize, 1}, {statuscmd, "#"}, {heredoc, undefined} ]), TapeOptions = pibfi_options:get_opts( [ {tapemodule, pibfi_tape_ets}, {maxcell, infinity}, {mincell, 0}, {wrapcell, false}, {maxtape, infinity}, {mintape, 0}, {wraptape, false} ]), IOOptions = pibfi_options:get_opts( [ {infile, tty}, {outfile, tty}, {maxout, infinity}, {minout, 0}, {wrapout, false}, {maxin, infinity}, {minin, 0}, {wrapin, false}, {eof, 0}, {xlatout, [{"\n", os_eol()}]}, {xlatin, [{os_eol(), "\n"}]} ]), DebugOptions = pibfi_options:get_opts( [ {statusevery, undefined} ]), {ok, {B, [Version]}} = beam_lib:version(code:which(?MODULE)), VersionString = atom_to_list(Version), whisper("pibfi: Platonic Ideal Brainf*ck Interpreter v~s", [VersionString]), whisper("Copyright (c)2003 Cat's Eye Technologies. All rights reserved."), whisper("Program file name: ~s", [Filename]), whisper("Parser options: ~s", [map_format(ParserOptions)]), whisper("Tape options: ~s", [map_format(TapeOptions)]), whisper("I/O options: ~s", [map_format(IOOptions)]), whisper("Debug options: ~s", [map_format(DebugOptions)]), whisper("--------------BEGIN PROGRAM OUTPUT--------------"), Program = case read_source_file(Filename) of {ok, FileContents} -> pibfi_supervisor:spawn_link(Supervisor, "interpreter", critical, ?MODULE, run, [Supervisor, FileContents, ParserOptions, TapeOptions, IOOptions, DebugOptions]); Else -> exit({could_not_read_file, Filename}) end. %% @spec run(Supervisor::pid(), ProgramSource::binary(), %% ParserOptions::[{atom(), term()}], %% TapeOptions::[{atom(), term()}], %% IOOptions::[{atom(), term()}], %% DebugOptions::[{atom(), term()}]) -> tape() %% program() = string() | binary() | tuple() %% @doc Runs a Brainf*ck program. run(Supervisor, ProgramSource, ParserOptions, TapeOptions, IOOptions, DebugOptions) -> {Program, HereDoc} = pibfi_parser:parse(ProgramSource, ParserOptions), CannedInput = case pibfi_options:get_option(IOOptions, infile, undefined) of heredoc -> HereDoc; _ -> "" end, IoPid = pibfi_io:start(Supervisor, IOOptions, CannedInput), Module = pibfi_options:get_option(TapeOptions, tapemodule, pibfi_tape_dict), TapePid = pibfi_tape:start(Module, Supervisor, TapeOptions), pibfi_statistics:start(Supervisor, TapePid, DebugOptions), pibfi_interpreter:interpret(Program, ParserOptions, TapePid, IoPid), pibfi_io:flush(IoPid), pibfi_io:stop(IoPid), TapePid. map_format(List) -> lists:map(fun ({Atom, Term}) when Atom == xlatin; Atom == xlatout -> io_lib:fwrite("~p=[~s] ", [Atom, map_format(Term)]); ({Atom, Term}) -> io_lib:fwrite("~p=~p ", [Atom, Term]) end, List). read_source_file("http://" ++ RestOfURL) -> case http:request_sync(get, {"http://" ++ RestOfURL, []}) of {200, Headers, Body} -> {ok, list_to_binary(Body)}; {Response, Headers, Body} -> {error, {unexpected_response_code, Response}} end; read_source_file(Filename) -> file:read_file(Filename). %% @spec wrap(Value::integer(), Max::integer(), Min::integer()) -> integer() %% @doc Implements a generic modulus function. Both the top and bottom %% modulus limits may be specified. wrap(X, Min, Max) -> case (X - Min) rem ((Max - Min) + 1) of Y when Y < 0 -> Max + 1 + Y; Y when Y >= 0 -> Y + Min end. %% @spec assert(Condition::boolean(), ErrorReason::term()) -> true %% @doc Asserts that the condition is true. If it is not, the %% process crashes with the given reason. assert(true, _) -> true; assert(false, Reason) -> exit(Reason). assert_in_bounds(Type, Min, Test, Max) -> Reason = {out_of_bounds, {{Type, Test}, {Min, Max}}}, case {Max, Min} of {infinity, infinity} -> true; {infinity, _} -> assert(Test >= Min, Reason); {_, infinity} -> assert(Test =< Max, Reason); {_, _} -> assert(Test >= Min andalso Test =< Max, Reason) end. %% @spec whisper(string()) -> ok %% @equiv whisper(string(), []) whisper(String) -> whisper(String, []). %% @spec whisper(string(), [term()]) -> ok %% @doc Displays extra information. The user can shut this off with %% -quiet. whisper(FmtString, Args) -> case pibfi_options:get_flag(quiet) of false -> io:fwrite(FmtString ++ "~n", Args); true -> ok end. %% @spec os_eol() -> string() %% @doc Returns the native end-of-line convention, if it can be determined. %% If it cannot be determined, linefeed (ASCII character 10) is assumed. os_eol() -> case os:type() of {win32, _} -> [13, 10]; _ -> [10] end. %%% END of pibfi.erl %%%