BFコンパイラー
以下のようにonelinerで可能です。
ただしLanguage::BF 0.03が必要です。
CodeRepos経由
で、
- svn co svn.coderepos.org/share/lang/perl/Language-BF
- cd Language-BF/trunk
- perl Makefile.PL
- make install
するか、CPANにVersion 0.03が現れるのをお待ち下さい。
Dan the Brainf.cker
1 2 3 4 | perl -MLanguage::BF \
-e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \
| perl
Hello World!
|
Posted feedbacks - OCaml
スタックオーバーフローすると反対側に出ます。
see: 配列の中の一つの値を変更しようとすると全部変更されてしまう!?!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | let parse = function
| '>' -> "stackincr p;"
| '<' -> "stackdecr p;"
| '+' -> "incr code.(!p);"
| '-' -> "decr code.(!p);"
| '.' -> "print_char (char_of_int !(code.(!p)));"
| ',' -> "code.(!p) := read_char ();"
| '[' -> "while 0 <> !(code.(!p)) do"
| ']' -> "done;;"
| _ -> "" ;;
let main ?(stacksize=256) filename =
let file = open_in filename in
Printf.printf "\
let stackincr x = if %d < (incr x; !x) then x:=0;;\n\
let stackdecr x = if 0 > (decr x; !x) then x:=%d;;\n\
let read_char () = int_of_char (read_line () ).[0];;\n\
let code = Array.init %d (fun i -> ref 0);;\n\
let p = ref 0;;\n\n"
(stacksize-1) (stacksize-1) stacksize;
let rec read_loop () =
try
let next = input_line file in
String.iter (fun x -> print_endline (parse x)) next;
read_loop ();
with
End_of_file -> close_in file
in read_loop ();;
if !(Sys.interactive)=false then
match (Array.length Sys.argv) with
| n when n=2 -> main Sys.argv.(1)
| n when 2<n -> main ~stacksize:(int_of_string Sys.argv.(2)) Sys.argv.(1)
| n -> print_endline "usage: ocaml bf2ml.ml [stacksize] filename"
;;
|
トランスレーターといえばcamlp4ということで、camlp4の3.10版で書いてみました。 LexerはBF用に書いていますが、TokenはOCaml用のものを流用しています。 Parserのコンパイル: ocamlc -c -I +camlp4 -pp camlp4orf dk80.ml Parserを利用したBFファイルのトランスレート結果の確認: camlp4orf dk80.cmo -impl helloworld.bf Parserを利用したBFファイルのコンパイル: ocamlc -o dk80 -pp "camlp4 -printer Camlp4OCamlPrinter dk80.cmo -impl" -impl helloworld.bf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | open Camlp4.PreCast
module MyLexer = struct
module Loc = Loc
module Token = Token
module Error = Token.Error
let mk () loc sc =
let rec next n = match Stream.peek sc with
| None -> Some(EOI, loc)
| Some x -> let _ = Stream.junk sc in (match x with
|'>'|'<'|'+'|'-'|'.'|','|'['|']'-> Some((SYMBOL (Printf.sprintf "%c" x)), loc)
| _ -> next n)
in Stream.from next
end
module BF = MakeGram MyLexer
let expr = BF.Entry.mk "expr"
let repr = BF.Entry.mk "repr"
let term = BF.Entry.mk "term"
EXTEND BF
expr: [[
t = repr; `EOI -> <:str_item<
let p = ref 0 in
let a = Array.create 1024 0 in
let inc a p = a.(!p) := a.(!p) + 1 in
let dec a p = a.(!p) := a.(!p) - 1 in
let prt a p = print_char (char_of_int a.(!p)) in
( $t$ )
>>
]];
repr: [[
t = term -> t
| t = term; u = repr -> <:expr< $t$; $u$ >>
]];
term: [[
">" -> <:expr< incr p >>
| "<" -> <:expr< decr p >>
| "+" -> <:expr< inc a p >>
| "-" -> <:expr< dec a p >>
| "." -> <:expr< prt a p >>
| "," -> <:expr< a.(!p) := input_byte stdin >>
| "["; t = repr; "]" -> <:expr< while a.(!p) <> 0 do ( $t$ ) done >>
]];
END
let myparse ?(directive_handler = fun _ -> None) _loc cs = BF.parse expr _loc cs
let myparse2 ?(directive_handler = fun _ -> None) _loc _ = <:sig_item< >>
let _ = Camlp4.Register.register_parser myparse myparse2;
|
素朴な実装。 一応、連続したインクリメント・デクリメントは一つにまとめる程度の最適化はしてます。 サンプルコードは弾さんのページからコピペしました。 % cat hello.bf ++++++++++[>+++++++>++++++++++>+++>+<<<<-] >++.>+.+++++++..+++.>++.<<+++++++++++++++. >.+++.------.--------.>+.>. % ./bf.byte hello.bf % cat hello.ml (* runtime *) let p = ref 0 let buf_size = 256 let make_buf () = Array.make buf_size 0 let buf = ref (make_buf ()) let extend () = buf := Array.append !buf (make_buf ()) let pincr n = p := !p + n; if Array.length !buf >= !p then extend () let pdecr n = p := !p - n; if !p < 0 then failwith "invalid pointer address!" type vtype = INCR | DECR let vset v n = !buf.(!p) <- !buf.(!p) + (match v with INCR -> n | DECR -> -n) let output () = print_char (char_of_int !buf.(!p)) let input () = !buf.(!p) <- int_of_char (input_char stdin) (* end of runtime *) let () = (* begin of code *) vset INCR 10; while !buf.(!p) <> 0 do pincr 1; vset INCR 7; pincr 1; vset INCR 10; pincr 1; vset INCR 3; pincr 1; vset INCR 1; pdecr 4; vset DECR 1; done; pincr 1; vset INCR 2; output (); pincr 1; vset INCR 1; output (); vset INCR 7; output (); vset INCR 3; output (); pincr 1; vset INCR 2; output (); pdecr 2; vset INCR 15; output (); pincr 1; output (); vset INCR 3; output (); vset DECR 6; output (); vset DECR 8; output (); pincr 1; vset INCR 1; output (); pincr 1; output (); (* end of code *) ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | (* lexer.mll *)
{
exception Eof
type instruction =
| PINCR of int | PDECR of int
| VINCR of int | VDECR of int
| OUTPUT | INPUT | FJUMP | BJUMP
}
rule token = parse
| ('>' +) as s { PINCR (String.length s) }
| ('<' +) as s { PDECR (String.length s) }
| ('+' +) as s { VINCR (String.length s) }
| ('-' +) as s { VDECR (String.length s) }
| ('.' +) { OUTPUT }
| (',' +) { INPUT }
| ('[' +) { FJUMP }
| (']' +) { BJUMP }
| ['\n' '\r'] { token lexbuf }
| eof { raise Eof }
(* bf.ml *)
let runtime = "\
(* runtime *)
let p = ref 0
let buf_size = 256
let make_buf () = Array.make buf_size 0
let buf = ref (make_buf ())
let extend () = buf := Array.append !buf (make_buf ())
let pincr n = p := !p + n; if Array.length !buf >= !p then extend ()
let pdecr n = p := !p - n; if !p < 0 then failwith \"invalid pointer address!\"
type vtype = INCR | DECR
let vset v n = !buf.(!p) <- !buf.(!p) + (match v with INCR -> n | DECR -> -n)
let output () = print_char (char_of_int !buf.(!p))
let input () = !buf.(!p) <- int_of_char (input_char stdin)
(* end of runtime *)
"
let code_begin = "\
let () =
(* begin of code *)
"
let code_end = "\
(* end of code *)
()
"
open Lexer
let code_of_instruction token =
let to_s = string_of_int in
match token with
| PINCR i -> "pincr " ^ (to_s i) ^ ";\n"
| PDECR i -> "pdecr " ^ (to_s i) ^ ";\n"
| VINCR i -> "vset INCR " ^ (to_s i) ^ ";\n"
| VDECR i -> "vset DECR " ^ (to_s i) ^ ";\n"
| OUTPUT -> "output ();\n"
| INPUT -> "input ();\n"
| FJUMP -> "while !buf.(!p) <> 0 do\n"
| BJUMP -> "done;\n"
let translate str =
let buf = Buffer.create ((String.length runtime) * 2) in
let add = Buffer.add_string buf in
let () = add runtime; add code_begin in
let lexbuf = Lexing.from_string str in
try
while true do
add (code_of_instruction (Lexer.token lexbuf))
done;
failwith "unreached"
with Lexer.Eof ->
add code_end;
Buffer.contents buf
let main () =
match Sys.argv with
| [|_; fname |] -> begin
let out_file_name =
try (Filename.chop_extension fname) ^ ".ml"
with _ -> fname ^ ".ml"
in
let in_ch = open_in fname in
let buf = Buffer.create 10 in
try
while true do
Buffer.add_string buf (input_line in_ch)
done
with End_of_file ->
close_in in_ch;
let code = translate (Buffer.contents buf) in
let out_ch = open_out out_file_name in
output_string out_ch code;
close_out out_ch
end
| _ ->
print_endline ("usage: " ^ Sys.argv.(0) ^ " BF_file")
let () = if not !Sys.interactive then main ()
|
MetaOCaml 使ってみました。コード生成部が読みにくい……
出力されるコードはこんな風になります:
# compile "++++++++[>++++++++<-]>+.";;
- : ('a, state -> unit) code =
.<fun s_1 ->
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 8) in
let _ =
while ((s_1.array).(s_1.ptr) <> 0) do
let _ = s_1.ptr <- ((s_1.ptr + 1) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 8) in
let _ = s_1.ptr <- ((s_1.ptr + (-1)) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + (-1)) in ()
done in
let _ = s_1.ptr <- ((s_1.ptr + 1) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 1) in
let _ =
(output_char stdout
(((* cross-stage persistent value (as id: Char.chr) *))
(s_1.array).(s_1.ptr))) in
()>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | type inst = Inc of int | Shift of int | Input | Output | Loop of inst list
let count_while c s =
let rec loop m =
match Stream.peek s with
| Some c' when c = c' -> Stream.junk s; loop (m+1)
| _ -> m
in loop 1
let rec parse1 s =
match Stream.next s with
| '+' -> Inc (count_while '+' s)
| '-' -> Inc (- (count_while '-' s))
| '>' -> Shift (count_while '>' s)
| '<' -> Shift (- (count_while '<' s))
| '.' -> Output
| ',' -> Input
| '[' -> Loop (parseloop s [])
| _ -> parse1 s
and parse s acc =
try let i = parse1 s in parse s (i :: acc)
with Stream.Failure -> List.rev acc
and parseloop s acc =
if Stream.peek s = Some ']' then (Stream.junk s; List.rev acc)
else parseloop s (parse1 s :: acc)
type state = { mutable ptr : int; array : int array; array_size : int; }
let rec trans1 i s =
match i with
| Inc n -> .< (.~s).array.((.~s).ptr) <- (.~s).array.((.~s).ptr) + n >.
| Shift n -> .< (.~s).ptr <- ((.~s).ptr + n) mod (.~s).array_size >.
| Output -> .< output_char stdout (Char.chr (.~s).array.((.~s).ptr)) >.
| Input -> .< (.~s).array.((.~s).ptr) <- Char.code (input_char stdin) >.
| Loop is ->
.< while (.~s).array.((.~s).ptr) <> 0 do .~(trans is s) done >.
and trans insts s =
let f i code = .< let _ = .~(trans1 i s) in .~code >.
in List.fold_right f insts .<()>.
let make_state n = { ptr = 0; array = Array.create n 0; array_size = n; }
let compile str =
.<fun s -> .~(trans (parse (Stream.of_string str) []) .<s>.)>.
let execute str = (.! compile str) (make_state 100)
|





dankogai
#3886()
Rating0/2=0.00
「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。
私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。
Dan the Brainf.ucker
see: Brainfuck - Wikipedia
1 reply [ reply ]