てきとう

てきとう

とゆーわけで

ErlangによるBrainf*ckインタプリタは既にあるので、
yeccを使って構文木っぽいものを作った後にそれを1関数で解釈・実行」
という自分で言ってて訳の分からないテーマに挑戦してみた。
開始前の問題は、

  1. Erlang?何それ?
  2. yecc?yacc?何それ?
  3. 関数型言語に挫折済み。

という事くらい。
まぁ何とか動くので曝しとくけど、yeccの書き方とかErlangの書き方とか、これで合ってるのかなぁ?

  • bf.yrl: yeccに食わせるファイル。
Nonterminals source element loop.

Terminals '[' ']' '+' '-' '<' '>' ',' '.' 'comment'.

Rootsymbol source.


source -> element        : {op, '$1', nil}.
source -> element source : {op, '$1', '$2'}.
source -> loop           : '$1'.

loop -> '[' ']'               : {loop, {op, nil, nil}, nil}.
loop -> '[' source ']'        : {loop, '$2', nil}.
loop -> '[' ']' source        : {loop, {op, nil, nil}, '$3'}.
loop -> '[' source ']' source : {loop, '$2', '$4'}.

element -> '+': '$1'.
element -> '-': '$1'.
element -> '<': '$1'.
element -> '>': '$1'.
element -> ',': '$1'.
element -> '.': '$1'.
element -> 'comment': nil.
  • bf_scan.erl: erl_scanの真似。string/1だけ実装。
-module(bf_scan).
-export([string/1]).

string(Str) when is_list(Str) ->
    convert(Str,1,[]);
string(_)  ->
    throw(invalid_argument).

convert([], Line, Result)->
    {ok, Result, Line};
convert([H|T], Line, Result) ->
    case H of
	10 ->convert(T,Line+1,Result);
	_ ->
	    case lists:member(H,"+-,.<>[]") of
		true ->
		    convert(T,Line,Result++[{list_to_atom([H]),Line}]);
		false -> convert(T,Line,Result)
	    end
    end.
  • bf_exec: ifじゃなくてcaseを使う。意味は無い。
-module(bf_exec).
-export([exec/1,exec/2,exec/4]).

exec(Src) ->
    exec(Src,[],{[0],1},fun()->0 end, fun(_)->nil end, fun(_)->nil end).

exec(Src,On_Change)->
    exec(Src, [], {[0], 1}, fun() -> 0 end,
	 fun(C)-> io:format("~p",[C]) end, On_Change).

exec(Src,In,Out,On_Change) ->
    exec(Src,[],{[0],1},In,Out,On_Change).

exec(Src, Rets, Buf, Input, Output, On_Change) ->
    case Src of
	nil ->
	    case Rets of
		[] ->
		    {ok, Buf};
		[H]  ->
		    exec(H,[],Buf,Input,Output,On_Change);
		[H|T] ->
		    exec(H,T,Buf,Input,Output,On_Change)
	    end;
	_ when is_tuple(Src) ->
	    case element(1,Src) of
		op ->
		    case element(1,element(2,Src)) of
			'+' ->
			    exec(element(3,Src),
				 Rets,
				 On_Change({lists:sublist(element(1,Buf),
							  element(2,Buf)-1)
					    ++[lists:nth(element(2,Buf),
							 element(1,Buf))+1]
					    ++lists:nthtail(element(2,Buf),
							    element(1,Buf)),
					    element(2,Buf)}),
				 Input,Output,On_Change);
			'-' ->
			    exec(element(3,Src),
				 Rets,
				 On_Change({lists:sublist(element(1,Buf),
							  element(2,Buf)-1)
					    ++[lists:nth(element(2,Buf),
							 element(1,Buf))-1]
					    ++lists:nthtail(element(2,Buf),
							    element(1,Buf)),
					    element(2,Buf)}),
				 Input,Output,On_Change);
			'<' ->
			    case element(2,Buf) of
				1 ->
				    throw('unable to go to left');
				_ ->
				    exec(element(3,Src),Rets,
					 On_Change({element(1,Buf),
						    element(2,Buf)-1}),
					 Input,Output,On_Change)
			    end;
			'>' ->
			    case element(2,Buf) of
				_ when element(2,Buf)=:=length(element(1,Buf))->
				    exec(element(3,Src),Rets,
					 {element(1,Buf)++[0],element(2,Buf)+1},
					 Input,Output,On_Change);
				_ ->
				    exec(element(3,Src),Rets,
					 On_Change({element(1,Buf),
						    element(2,Buf)+1}),
					 Input,Output,On_Change)
			    end;
			',' ->
			    exec(element(3,Src),Rets,
				 {lists:sublist(element(1,Buf),
						element(2,Buf)-1)
				  ++[Input()]++
				  lists:nthtail(element(2,Buf),
						element(1,Buf)),
				  element(2,Buf)},
				 Input,Output,On_Change);
			'.' ->
			    Output(lists:nth(
				     element(2,Buf),
				     element(1,Buf))),
			    exec(element(3,Src),Rets,Buf,Input,
				 Output, On_Change);
			_   ->throw(unknown_operation)
		    end;
		loop ->
		    case lists:nth(element(2,Buf),element(1,Buf)) of
			0 ->
			    exec(element(3,Src),Rets,Buf,
				 Input,Output,On_Change);
			_ ->
			    exec(element(2,Src),[Src]++Rets,Buf,
				Input,Output,On_Change)
		    end;
		_ ->
		    throw(unknown_type)
	    end;
	_ ->
	    throw(unknown_instr)
    end.


使い方:

Source=bf_scan:string(">++++++++[<++++++++++++>-]<++++++.+++++++++..").
yecc:yecc("bf.yrl","bf_parser.erl"), c(bf_parser).
Tree=bf_parser:parse(Source).
bf_exec:exec(Tree).

みたいな。
うわ〜、無駄
というか頭が悪すぎる…orz