ブログに書いていたものを引っ越してきました。元の記事公開日は 2020-09-08 です。
20年ぶりくらいに Perl のコードを書きました。やっつけなので汚いです。ライフゲームのコンパイルが通ったのでヨシ、という程度の雑なものです。
https://github.com/sonota88/vm2gol-v2-perl
Rubyで素朴な自作言語のコンパイラを作った
(自作言語の概要などについてはこちらを参照してください)
ベースになっているバージョン: tag:49 のあたり
(追記 2021-09-11: ステップ60の修正まで適用しました)
作り方はここに全部書いています(Ruby 版のものですが): vm2gol v2 製作メモ
ただ、Ruby 版よりは主に C言語版 を見ながら書き写していました。クラスを使わなかったのもあり、C言語版に近いところも多いです。
for my $x (@$xs) { ... }
の @
がなくて動かないとか、たぶん初心者あるあるlib/Val.pm
)。my $foo = ...
をいっぱい書かないといけなくてさすがに面倒だったので途中で次のような Emacs Lisp を書いて Ctrl + Alt + M
で入力できるようにしたり。yasnippet でも良かったかも。
(add-hook
'perl-mode-hook
'(lambda ()
(local-set-key (kbd "C-M-m")
(lambda ()
(interactive)
(insert "my $ = ;")
(backward-char 4)))))
Dart版で call
、Java版で set
を不要にした流れで、今回は call_set
キーワードを不要にしてみました。同じ要領でできますね、ということが分かりました。
(追記 2021-04-09: この修正はいったん revert しましたが、一応 trial ブランチに残してあります。)
sub parse_stmt {
my $t = peek(0);
if (Token::is($t, "sym", "}")) {
return -1;
}
if (Token::str_eq($t, "func" )) { return parse_func(); }
elsif (Token::str_eq($t, "var" )) { return parse_var(); }
elsif (Token::str_eq($t, "set" )) { return parse_set(); }
elsif (Token::str_eq($t, "call" )) { return parse_call(); }
# elsif (Token::str_eq($t, "call_set")) { return parse_call_set(); }
elsif (Token::str_eq($t, "return" )) { return parse_return(); }
elsif (Token::str_eq($t, "while" )) { return parse_while(); }
elsif (Token::str_eq($t, "case" )) { return parse_case(); }
elsif (Token::str_eq($t, "_cmt" )) { return parse_vm_comment(); }
else {
if (Token::kind_eq($t, "ident")) {
return parse_call_set();
} else {
p_e("parse_stmt", $t);
die "not_yet_impl";
}
}
}
(追記 2021-08-30: その後の修正により、下記の部分はなくなりました)
それと、今回はコード生成処理のアレ、何度も似たようなのが出てきて鬱陶しかった部分をためしにサブルーチンに抽出して共通化してみました。うーん。どうでしょう。どうしようかな。とりあえず名前が微妙。
名前が微妙な上に上手い抽象でもない気がする……となるとこれはメソッド抽出すべきではないパターンのように思えます。いい解決法が見つかるまで本家の Ruby版には取り込めなさそう。とはいえコーディングの手間・退屈さは減らせるので、移植版では気軽に使っていこうかなと。
sub to_asm_str {
my $fn_arg_names = shift;
my $lvar_names = shift;
my $val = shift;
if (Utils::is_arr($val)) {
return undef;
} elsif (Val::kind_eq($val, "int")) {
return $val->{"val"};
} elsif (Val::kind_eq($val, "str")) {
my $str = $val->{"val"};
if (0 <= str_arr_index($fn_arg_names, $str)) {
return to_fn_arg_ref($fn_arg_names, $str);
} elsif (0 <= str_arr_index($lvar_names, $str)) {
return to_lvar_ref($lvar_names, $str);
} else {
return undef;
}
} else {
return undef;
}
}
呼び出し箇所は6箇所。それなりに使いまわせているようではあります。
vgcg.pl:132: $push_arg = to_asm_str($fn_arg_names, $lvar_names, $val);
vgcg.pl:240: $push_arg = to_asm_str($fn_arg_names, $lvar_names, $fn_arg);
vgcg.pl:304: $src_val = to_asm_str($fn_arg_names, $lvar_names, $expr);
vgcg.pl:316: my $vram_ref = to_asm_str($fn_arg_names, $lvar_names, sval($vram_arg));
vgcg.pl:342: my $vram_ref = to_asm_str($fn_arg_names, $lvar_names, sval($vram_arg));
vgcg.pl:375: my $vram_ref = to_asm_str([], $lvar_names, sval($vram_arg));
記事 | リポジトリ | 日付 |
---|---|---|
Haskell | github | 2021-06-28 |
OCaml | github | 2021-06-26 |
Pascal | github | 2021-05-22 |
Julia | github | 2021-05-03 |
Rust | github | 2021-04-07 |
Crystal | github | 2021-03-27 |
Pric(セルフホスト) | github | 2021-02-21 |
Kotlin | github | 2021-01-14 |
Zig | github | 2021-01-07 |
LibreOffice Basic | github | 2020-12-14 |
Go | github | 2020-09-25 |
PHP | github | 2020-09-18 |
C♭ | github | 2020-09-13 |
C | github | 2020-09-06 |
Java | github | 2020-08-30 |
Dart | github | 2020-08-22 |
Python | github | 2020-08-19 |
TypeScript (Deno) | github | 2020-08-15 |
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント