トップ «前の日記(2008-07-02) 最新 次の日記(2008-07-04)» 月表示 編集

日々の流転


2008-07-03 [長年日記]

λ. A brainf*ck interpreter in Erlang

HelloWorld を書いたので、次は brainf*ck のインタプリタでも。

-module(brainfuck).
-export([brainfuck/1]).

brainfuck(Src) ->
    Prog=parse(Src), seq(Prog, {[], [0]}).

parse(X) -> {Y,_} = parse(X, []), Y.

parse([], Acc) -> {lists:reverse(Acc), []};
parse([H|T], Acc) -> 
    case H of
      $] -> {lists:reverse(Acc), T};
      $> -> parse(T, ['>'|Acc]);
      $< -> parse(T, ['<'|Acc]);
      $+ -> parse(T, ['+'|Acc]);
      $- -> parse(T, ['-'|Acc]);
      $. -> parse(T, ['.'|Acc]);
      $, -> parse(T, [','|Acc]);
      $[ -> {Prog,X}=parse(T, []), parse(X,[{Prog}|Acc]);
      _  -> parse(T, Acc)
    end.

seq(Prog, T) ->
    lists:foldl(fun (P,T1) -> step(P,T1) end, T, Prog).

step('>', {L,[X|R]}) ->
    {[X|L], case R of [_|_] -> R; _ -> [0] end};
step('<', {[X|L],R}) -> {L, [X|R]};
step('+', {L,[X|R]}) ->
    {L, [ case X of 255 -> 0; _ -> X+1 end | R ]};
step('-', {L,[X|R]}) ->
    {L, [ case X of 0 -> 255; _ -> X-1 end | R ]};
step('.', {L,[X|R]}) ->
    io:put_chars([X]), {L, [X|R]};
step(',', {L,[_|R]}) ->
    [X] = io:get_chars("", 1), {L, [X|R]};
step({W}, T) -> while(W, T).

while(_, {_,[0|_]}=T1) -> T1;
while(W, T) -> while(W, seq(W, T)).

といっても、前にPrologで書いたもの を適当に翻訳しただけ。

Erlang shell から実行してみる。

% erl
Eshell V5.6.3  (abort with ^G)
1> c(brainfuck).
{ok,brainfuck}
2> brainfuck:brainfuck("
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.").
Hello World!
{[],[10,0]}
Tags: Erlang