かんたんな自作言語のコンパイラをいろんな言語で書いてみるシリーズ 22番目の言語は Tcl です。
Tk の方は(Ruby/Tk で)少し触れたことがあるのですが、そういえば Tcl って書いたことなかったなと思って書いてみました。
できたもの
アドベントカレンダーの期日が過ぎてしまったのでとりあえず公開します。いろいろと雑です。3日くらいでババッと書きました。
サイズはこんな感じ。
$ wc -l *.tcl lib/*.tcl
381 codegen.tcl
129 lexer.tcl
438 parser.tcl
109 lib/json.tcl
42 lib/types.tcl
92 lib/utils.tcl
1191 合計
動かし方の例
$ echo '
func add(a, b) {
return a + b;
}
func main() {
call add(1, 2);
}
' | ./lexer.tcl | ./parser.tcl | ./codegen.tcl
# ↓アセンブリが出力される
call main
exit
label add
push bp
cp sp bp
cp [bp:2] reg_a
push reg_a
cp [bp:3] reg_a
push reg_a
pop reg_b
pop reg_a
add_ab
cp bp sp
pop bp
ret
label main
push bp
cp sp bp
cp 2 reg_a
push reg_a
cp 1 reg_a
push reg_a
_cmt call~~add
call add
add_sp 2
cp bp sp
pop bp
ret
# ... snip ...
移植元
Tcl版のベースになっているバージョンは tag:62 のあたり
<自作言語処理系の説明用テンプレ>
自分がコンパイラ実装に入門するために作った素朴なトイ言語とその処理系です。簡単に概要を書くと下記のような感じ。
- 小規模: コンパイラ部分は 1,000 行程度
- pure Ruby / 標準ライブラリ以外のライブラリ不要
- x86風の自作VM向けにコンパイルする
- ライフゲームのために必要な機能だけ
- 変数宣言、代入、反復、条件分岐、関数定義
- 演算子:
+
,*
,==
,!=
のみ(優先順位は(
)
で明示) - 型なし(値は符号付き整数のみ)
- 作ったときに書いた備忘記事
-
本体には含めていない後付けの機能など
- 真偽値リテラル / break / if/else / 単項マイナス / Racc などを使って書いたパーサの別実装
-
Ruby 以外の言語への移植
- コンパイラ部分のみ
- Python, Java, TypeScript など、2021-12-19 の時点では 20言語
- セルフホスト版
- 製作過程を知りたい場合は製作メモを見てください
<説明用テンプレおわり>
メモ
自分が知っている言語の中ではシェルスクリプトに一番似ている気がします。
- コマンド実行が基本
- 単に
foo
と書くと文字列扱いで、$foo
だと変数置換される -
[...]
はコマンド置換みたいな感じ
……といったあたりが分かってきた後では「なんだかすっきりした素直なシェルスクリプトみたいだな」と思いながら書いていました。
型の判別はめんどくさそうだったので、文字列だったら {str fdsa}
、整数だったら {int 123}
と明示的にタグ付けするスタイルにしました。
参考: Determine type of a variable in Tcl - Stack Overflow
この記事を読んだ人は(たぶん)こちらも読んでいます