git @ Cat's Eye Technologies Sbeezg / master src / sbeezg.erl
master

Tree @master (Download .tar.gz)

sbeezg.erl @masterraw · history · blame

-module(sbeezg).
-vsn('2002.0317').
-author('cpressey@gmail.com').
-copyright('This work is in the public domain; see UNLICENSE for more info').

-export([run/1, test/1]).

%%% BEGIN sbeezg.erl %%%

%%% A simple interpreter for the Sbeezg Programming Language.

%% Interpreter ---------------------------------------------------------
%% Call sbeezg:run("sbeezg-program") to interpret a Sbeezg program.
%% Empties the process dictionary as a side effect.

run(String) when list(String) ->
  {ok, Toks, N} = erl_scan:string(String),
  % io:fwrite("scan: ~s -> ~w~n", [String, Toks]),
  erase(), % clear process dictionary for parser
  case sbeezg_parser:parse(Toks ++ [{'$end',999}]) of
    {ok, Prog} -> 
      % io:fwrite("parse: ~s -> ~w~n", [String, Prog]),
      erase(), % destroy the garbage left in the process dictionary
      run(Prog);
    {error, {Line, Module, Message}}=Q ->
      io:fwrite("Error: ~s~n", [Message]),
      Q
  end;
run({assign, L, F, A}=C) ->
  % io:fwrite("~w~n", [C]),
  L0 = get_name(L),
  F0 = run(F),
  A0 = run(A),
  assign(L0, '$placeholder'),
  R0 = execute(F0, A0),
  assign(L0, R0);
run({assign, L, R}=C) ->
  % io:fwrite("~w~n", [C]),
  L0 = get_name(L),
  R0 = run(R),
  assign(L0, R0);
run({vlist, H, T}=C) ->
  % io:fwrite("~w~n", [C]),
  H0 = run(H),
  T0 = run(T),
  [H0 | T0];
run({lambda, N, A, R}=C) ->
  % io:fwrite("~w~n", [C]),
  {func, N, A, R};
run({alist, H, T}=C) ->
  % io:fwrite("~w~n", [C]),
  H0 = run(H),
  T0 = run(T),
  case T0 of
    [] -> H0;
    _  -> T0
  end;
run({lit,{atom,Line,Atom}}) -> Atom;
run({lit,{integer,Line,Int}}) -> Int;
run({name, {atom,Line,Atom}}) ->
  case get(Atom) of
    undefined ->
      io:fwrite("~w undefined!~n", [Atom]),
      Atom;
    N ->
      N
  end;
run(nil) ->
  [];
run(Q) ->
  io:fwrite("UNKNOWN ~w~n", [Q]),
  unknown.

get_name({nlist, H, T}=C) ->
  % io:fwrite("~w~n", [C]),
  H0 = get_name(H),
  T0 = get_name(T),
  [H0 | T0];
get_name({name, {atom,Line,Atom}}) -> Atom;
get_name(nil) -> [].

%% Utility -------------------------------------------------------------

assign(Name, Value) ->
  % io:fwrite("Assigning ~w to ~w~n", [Value, Name]),
  case get(Name) of
    NonExist when NonExist == undefined; NonExist == '$placeholder' ->
      put(Name, Value);
    Exist ->
      io:fwrite("ERROR: Multiple Assignment to ~w=~w (~w)~n",
        [Name, Exist, Value])
  end,
  Value.
  
execute(print, [H | T]) ->
  io:fwrite("~w", [H]),
  execute(print, T);
execute(print, []) -> ok;
execute(is, [A, B, T, F]) ->
  % io:fwrite("~w", [C]),
  case A of
    B -> T;
    _ -> F
  end;
execute(pred, [X]) ->
  % io:fwrite("pred ~w~n", [X]),
  X - 1;
execute(succ, [X]) -> X + 1;
execute(Func, Args) when atom(Func) ->
  io:fwrite("Unknown built-in '~w(~w)'~n", [Func, Args]),
  unknown;
execute({func, P, B, R}, A) ->
  P0 = fresh(P),
  B0 = fresh(B),
  P1 = get_name(P0),
  bind(P1, A),
  run(B0),
  run(R).

fresh({assign, L, F, A}=C) -> {assign, fresh(L), fresh(F), fresh(A)};
fresh({assign, L, R}=C)    -> {assign, fresh(L), fresh(R)};
fresh({vlist, H, T}=C)     -> {vlist, fresh(H), fresh(T)};
fresh({lambda, X, A}=C)    -> {lambda, fresh(X), fresh(A)};
fresh({nlist, H, T}=C)     -> {nlist, fresh(H), fresh(T)};
fresh({alist, H, T}=C)     -> {alist, fresh(H), fresh(T)};
fresh({lit,X}=C)           -> C;
fresh({name,{atom,Line,Atom}}=C)  ->
  case get(Atom) of
    undefined -> C;
    _ ->
      fresh({name, {atom, Line, list_to_atom([$_ | atom_to_list(Atom)])}})
  end;
fresh(X)                   -> X.

bind([],[]) -> ok;
bind([HN|TN],[HV|TV]) ->
  % io:fwrite("bind ~w to ~w~n", [HV,HN]),
  assign(HN,HV),
  bind(TN,TV).

%% User interface ------------------------------------------------------

test([N]) -> test(list_to_integer(N));
test(N) ->
  R = run(prg(N)),
  E = erase(),
  {R, E}.

prg(1) -> "a={b|p=*print;c=p(b)|b};d=a(*foo);e=a(*bar);f=a(*baz)";
prg(2) -> "a={b|p=*print;c=p(b)|b};d={e,f|g=e(f)|g};h=d(a,*hello)";
prg(3) -> "p=*print;i=*is;r=i(*a,*b,*troo,*fall);q=p(r)";
prg(4) -> "f={a,b|p=*print;g=p(a);k=b(a,b)|a};l=f(*hello,f)";
prg(5) -> "f={a,b|i=*is;s=*pred;p=*print;g=p(*beer);h=s(a);"
          "ln={x,m|z=x|x};lg={y,n|q=n(y,n)|y};j=i(h,0,ln,lg);"
	  "k=j(h,b)|a};l=f(99,f)";
prg(6) -> "f=*a;f=*b";  % intentionally erroneous
prg(_) -> unknown.

%%% END of sbeezg.erl %%%