ErlangによるBrainf*ckインタプリタは既にあるので、
「yeccを使って構文木っぽいものを作った後にそれを1関数で解釈・実行」
という自分で言ってて訳の分からないテーマに挑戦してみた。
開始前の問題は、
という事くらい。
まぁ何とか動くので曝しとくけど、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