2009年01月29日
巷では空前のBrainf*ckブームっぽい。
で、その派生言語であるところの、「てってってー言語」というのが話題っぽかったのでPerlで実装してみました。
いろいろ突っ込みお待ちしてます。
あと、(俺|私)もこんな言語で実装したよ!とかいうのあったら教えてくれるとうれしいです。たぶん。
プログラム言語「てってってー」 - H.Hiroのチラシの裏の裏
404 Blog Not Found:javascript - てってってー言語を移植してみた
実装は小飼弾氏のJavaScript版を大いにパクり参考にしました。
ありがとうございます。
出来ること
- てってってーをコンパイルして実行
- てってってーをファイルからコンパイルして実行
- 文字コード指定してごにょごにょ
- Perlコード吐き出し
使い方
ちょお簡単です。
下記をご覧いただければ一目瞭然ですね!
#!/usr/bin/perl use strict; use warnings; use Language::Tettettee; use utf8; binmode STDOUT, ':utf8'; # とりあえずインスタンス生成して my $tee = Language::Tettettee->new; # てってってーをコンパイル $tee->compile(' {「てってってーてってっててー」と出力するプログラム} ーてってってー {「てってっ」をメモリに書き込む} てっててーてっててーてっててーてっててー {ポインタを4つ戻す} てってっーてってっーてってっーてってっー {標準出力へ4文字出力する} ーててーてっててーてってっー {「て」をメモリに書き込み、ポインタを戻してから標準出力へ出力する} ーーてってっててー {「ーてってって」をメモリに書き込む} てっててーてっててーてっててーてっててーてっててーてっててー {ポインタを6つ戻す} てってっーてってっーてってっーてってっーてってっーてってっー {標準出力へ6文字出力する} ーててーてっててーてってっー {「て」をメモリに書き込み、ポインタを戻してから標準出力へ出力する} ーーてーてっててーてってっー {「ー」をメモリに書き込み、ポインタを戻してから標準出力へ出力する} '); # 実行してみる $tee->run; # 実行結果表示をutf8 falg付きで出力 print $tee->output;
てってってーてってっててー
こんな感じですね。
ファイルからの場合はnewの代りにreadを使って
#my $tee = Language::Tettettee->new; my $tee = Language::Tettettee->read('てってってーソースファイル'); $tee->run; print $tee->output;
とすればいいです。
readでcompileまでやっちゃいます。
文字コード指定しる
通常であればutf8として頑張りますが、文字コードすればごにょごにょしてくれます。
newで指定すればあとはずっとそのエンコードとして扱います。
また、すべてのメソッドに対して毎回指定することもできます。
それから、utf8flag付き文字で返してほしくない場合は明示的に指定する必要があります。
#!/usr/bin/perl use strict; use warnings; use Language::Tettettee; #use utf8; #binmode STDOUT, ':utf8'; # 文字コード指定してやる my $tee = Language::Tettettee->new('sjis'); # コンパイル $tee->compile(' {"なのはなのなの"をメモリに書き込んで} ーなのはなのなのてー {標準入力から3文字受け取って} てってってーてってってーてってってー {ポインタ10個戻して} てっててーてっててーてっててーてっててーてっててー てっててーてっててーてっててーてっててーてっててー {10文字標準出力するお!} てってっーてってっーてってっーてってっーてってっー てってっーてってっーてってっーてってっーてってっー '); # 標準入力に文字入れる $tee->input('なの?'); # 実行してみる $tee->run; # 実行結果表示をutf8 falgなしにしたい! $tee->out_utf8_flag = 0; # sjisになって出力される! print $tee->output;
なのはなのなのなの?
sjisでかかれた「てってってー」を読み込んでコンパイルして実行します。
inputメソッドは標準入力の代わりです。
通常だとutf8 flag付きで出力されるので、明示的に「$tee->out_utf8_flag = 0;」でutf8 flagなしにしています。
モテたいがためにlvalueを使った。いまは反省している。
Perlコード出力
as_sourceメソッドでPerlの実行出来そうなコードを出力しますぜ
#!/usr/bin/perl use strict; use warnings; use Language::Tettettee; use utf8; binmode STDOUT, ':utf8'; my $tee = Language::Tettettee->new; $tee->compile(' {"なのはなのなの"をメモリに書き込んで} ーなのはなのなのてー {標準入力から3文字受け取って} てってってーてってってーてってってー {ポインタ10個戻して} てっててーてっててーてっててーてっててーてっててー てっててーてっててーてっててーてっててーてっててー {10文字標準出力するお!} てってっーてってっーてってっーてってっーてってっー てってっーてってっーてってっーてってっーてってっー '); # 標準入力に文字入れる $tee->input('なの?'); # perlコードに変換 print $tee->as_source;
do { my $input = []; push @$input, 'な'; push @$input, 'の'; push @$input, '?'; my $data = []; my $output = []; my $sp = 0; push @$data, 0 for (1..65535); for (my $j = 1; $j < length('ーなのはなのなのてー') - 2; $j++) { $data->[$sp++] = substr('ーなのはなのなのてー', $j, 1) } $data->[$sp++] = shift @{$self->{input}}; $data->[$sp++] = shift @{$self->{input}}; $data->[$sp++] = shift @{$self->{input}}; $sp--; $sp--; $sp--; $sp--; $sp--; $sp--; $sp--; $sp--; $sp--; $sp--; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; push @$output, $data->[$sp++]; $output; }
inputされた文字も復元されます。まぁ微妙だけど。
続きにコード晒しておきます。
ちょお汚いし、リファレンスとか使いまくってる理由がよくわからないけど気にしないでね!
Language::Tettettee
package Language::Tettettee; use strict; use warnings; use utf8; use Encode; use Carp; our $VERSION = 0.01; my $utf8 = find_encoding 'utf8'; sub new($;$$) { my $class = shift; my $enc = shift || $utf8; $enc = find_encoding $enc unless ref $enc; bless { input => my $input = [], output => my $output = [], code => my $code = "", enc => $enc, utf8_flag => 1, @_, }, $class; } sub read($;$$) { my $class = shift; my $tee_file = shift or croak __PACKAGE__, "->read(filename)"; my $tee = $class->new(@_); open my $fh, "<", $tee_file or croak "$tee_file $!"; my $src = do { local $/; <$fh> }; close $fh; $tee->compile($src); return $tee; } my $parser = sub { my $str = shift; $str eq 'てってー' ? return '$sp++;' . "\n" : $str eq 'てっててー' ? return '$sp--;' . "\n" : $str eq 'ててー' ? return '$data->[$sp]++;' . "\n" : $str eq 'てっー' ? return '$data->[$sp]--;' . "\n" : $str eq 'てってっー' ? return 'push @$output, $data->[$sp++];' . "\n" : $str eq 'てってってー' ? return '$data->[$sp++] = shift @{$self->{input}};' . "\n" : $str eq 'てってっててー' ? return 'whiile ($data->[$sp]) {' . "\n" : $str eq 'てってってっー' ? return '}' . "\n" : return 'for (my $j = 1; $j < length('. qq{'$str'} .') - 2; $j++) {' ."\n" . "\t" . '$data->[$sp++] = substr(' . qq{'$str'} . ', $j, 1)' . "\n" . '}' . "\n"; }; my $convert = sub { my $str = shift; $str =~ s/\s*{[^}]+}//g; $str =~ s/( ー.*?てー |ててー |てっー |てってー |てっててー |てってっー |てってってー |てってっててー |てってってっー )/$parser->($1)/egx; $str = 'my $data = [];' . "\n" . 'my $output = [];' . "\n" . 'my $sp = 0;' . "\n" . 'push @$data, 0 for (1..65535);' . "\n" . $str . '$output;' . "\n"; return $str; }; my $decoder = sub { my $str = shift; my $enc = shift; return decode $enc, $str unless Encode::is_utf8 $str; return $str; }; my $encoder = sub { my $str = shift; my $enc = shift; return encode $enc, $str if Encode::is_utf8 $str; return $str; }; sub run($;$$) { my $self = shift; my $str = shift; my $enc = shift || $self->{enc}; $str = $decoder->($str, $enc); unless ($self->{code}) { croak __PACKAGE__, "->run(code)" unless $str; $self->compile($str) } my $result = $self->{code}(); delete $self->{input}; delete $self->{code}; $self->{output} = $result; } sub compile($;$$) { my $self = shift; my $str = shift; my $enc = shift || $self->{enc}; $str = $decoder->($str, $enc); $self->{src} = $convert->($str); $self->{code} = eval 'sub {' . $self->{src} . '}'; croak "$@\n", $encoder->($self->{src}, $enc) if $@; return 1; } sub input($;$$) { my $self = shift; my $str = shift; my $enc = shift || $self->{enc}; $str = $decoder->($str, $enc); for (my $i = 0; $i < length $str; $i++) { push @{$self->{input}}, substr($str, $i, 1); } } sub output($;$) { my $self = shift; my $enc = shift || $self->{enc}; my $str = join '', @{$self->{output}}; return $encoder->($str, $enc) unless $self->{utf8_flag}; return $str; } sub as_source($;$) { my $self = shift; my $enc = shift || $self->{enc}; my $input = 'my $input = [];' . "\n" . do { my $str; for (my $i = 0; $i < @{$self->{input}}; $i++) { $str .= sprintf "push \@\$input, '%s';\n", $self->{input}[$i]; } $str; } . "\n"; my $src = $input . $self->{src}; $src =~ s/^/ /gm; $src = sprintf "do {\n%s}\n", $src; return $encoder->($src, $enc) unless $self->{utf8_flag}; return $src; } sub out_utf8_flag : lvalue { shift->{utf8_flag}; } 1;
Enjoy Esoteric Language !
トラックバック一覧
コメント一覧
いろいろ突っ込みお待ちしてます。
いろいろ突っ込みお待ちしてます。