Tclでかんたんな自作言語のコンパイラを書いた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/9ebeab7b-e274-4b72-4c9a-1a69b3c3c2c9.png)

[かんたんな自作言語のコンパイラをいろんな言語で書いてみるシリーズ](https://memo88.hatenablog.com/entry/2020/08/30/132314) 22番目の言語は Tcl です。

Tk の方は（Ruby/Tk で）少し触れたことがあるのですが、そういえば Tcl って書いたことなかったなと思って書いてみました。



# できたもの

https://github.com/sonota88/vm2gol-v2-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 合計
```

# 動かし方の例

```sh
$ 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 ...
```






# 移植元

https://github.com/sonota88/vm2gol-v2


Tcl版のベースになっているバージョンは [tag:62](https://github.com/sonota88/vm2gol-v2/tree/62) のあたり

<!--
（追記 2021-09-18: [ステップ60](https://memo88.hatenablog.com/entry/20210911_vm2gol_v2_60_pass_stmt) の修正まで適用しました）
-->

＜自作言語処理系の説明用テンプレ＞

自分がコンパイラ実装に入門するために作った素朴なトイ言語とその処理系です。簡単に概要を書くと下記のような感じ。

<!--
入門者（＝自分）にやさしく、難しいことをせずに短期間でババッと作れることを重視してる感じの仕様・実装です。
- リポジトリ: [github.com/sonota88/vm2gol-v2](https://github.com/sonota88/vm2gol-v2)
-->

- 小規模: コンパイラ部分は 1,000 行程度
- pure Ruby / 標準ライブラリ以外のライブラリ不要
- x86風の自作VM向けにコンパイルする
- ライフゲームのために必要な機能だけ
  - 変数宣言、代入、反復、条件分岐、関数定義
  - 演算子: `+`, `*`, `==`, `!=` のみ（優先順位は `(` `)` で明示）
  - 型なし（値は符号付き整数のみ）
- 作ったときに書いた備忘記事
  - [RubyでオレオレVMとアセンブラとコード生成器を2週間で作ってライフゲームを動かした話](https://qiita.com/sonota88/items/f9cb3fc4a496b354b729)
  - [Rubyでかんたんな自作言語のコンパイラを作った](https://qiita.com/sonota88/items/2b95378b43a22109513c)
- [本体には含めていない後付けの機能など](https://memo88.hatenablog.com/entry/vm2gol_v2_additional_features)
    - 真偽値リテラル / break / if/else / 単項マイナス / Racc などを使って書いたパーサの別実装
- [Ruby 以外の言語への移植](https://memo88.hatenablog.com/entry/2020/08/30/132314)
    - コンパイラ部分のみ
    - Python, Java, TypeScript など、2021-12-19 の時点では 20言語
- [セルフホスト版](https://qiita.com/sonota88/items/1e683276541cf1b87b76)
- 製作過程を知りたい場合は[製作メモ](https://memo88.hatenablog.com/entry/2019/05/04/234516)を見てください

＜説明用テンプレおわり＞



# メモ

自分が知っている言語の中ではシェルスクリプトに一番似ている気がします。

- コマンド実行が基本
- 単に `foo` と書くと文字列扱いで、`$foo` だと変数置換される
- `[...]` はコマンド置換みたいな感じ

……といったあたりが分かってきた後では「なんだかすっきりした素直なシェルスクリプトみたいだな」と思いながら書いていました。

型の判別はめんどくさそうだったので、文字列だったら `{str fdsa}`、整数だったら `{int 123}` と明示的にタグ付けするスタイルにしました。

参考: [Determine type of a variable in Tcl - Stack Overflow](https://stackoverflow.com/questions/7428032/determine-type-of-a-variable-in-tcl)





# この記事を読んだ人は（たぶん）こちらも読んでいます

<!-- シェルスクリプト（Bashスクリプト）でかんたんな自作言語のコンパイラを書いた - Qiita -->
https://qiita.com/sonota88/items/79dd2b0c1dae776c56d9

<!-- Perlでかんたんな自作言語のコンパイラを書いた - Qiita -->
https://qiita.com/sonota88/items/1482e236054ad3edfdf5