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

![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) 20番目の言語はなでしこ3です。


ライフゲームのコンパイルが通ればヨシ、という程度の雑なものです。



# できたもの

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


# 移植元

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


なでしこ3版のベースになっているバージョンは [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)（コンパイラ部分のみ）
- [セルフホスト版](https://qiita.com/sonota88/items/1e683276541cf1b87b76)
- 製作過程を知りたい場合は[製作メモ](https://memo88.hatenablog.com/entry/2019/05/04/234516)を見てください

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




# メモ

なでしこ3版のサイズ（行数）はこれくらい。1,000行切ってますね。

```
$ wc -l *.nako3 lib/*.nako3
  365 codegen.nako3
  110 lexer.nako3
  422 parser.nako3
   49 lib/utils.nako3
  946 合計
```



- そんなに苦労しなかった
    - 書き味は普通にスクリプト言語っぽい感じ
    - 標準でJSON読み書きできる、ネストした配列が作れる
    - `[1, "a", []]` のように JavaScript っぽくリテラルが書ける
- ファイル分割できない？
    - [ナデシコ命令](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E3%83%8A%E3%83%87%E3%82%B7%E3%82%B3&show)を使って分割したファイルの取込み（というか読み込んだコードの評価）ができないか軽く試したのですが、期待した動きにできなかったので、諦めて適当なスクリプト（`preproc.rb`）で前処理することに

----

`引数＋助詞、引数＋助詞、 ... 関数。` のように書くのだ、ということをまず最初に知ると基本的な読み書きができるようになります。

- [なでしこさん マニュアル - 文法/基本](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E5%9F%BA%E6%9C%AC&show)

> 命令文の基本的な文形 *
> そして、なでしこのプログラムの構造は、以下のような形式になっています。
> `引数＋助詞、引数＋助詞、 ... 関数。`
> 『「こんにちは」と表示』という命令文で言えば、「こんにちは」が引数で、「表示」が関数となります。

「読んで」が `読` と `んで` に別れる、みたいなのもあって最初はよく分からなかった気がするので、助詞一覧にも目を通しておくとよいです。

- [なでしこさん マニュアル - 文法/字句解析](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E5%AD%97%E5%8F%A5%E8%A7%A3%E6%9E%90&show)

予約語や助詞はハイライトがあるといいなと思って、 Emacs の generic-mode で色を付けて書いていました。最初は助詞一覧が頭に入ってないので、エディタのサポートがあると入門がスムーズになるように思います。Qiita のコードブロックでもサポートされるといいですね。

<!--
文字列やコメントが一部変ですが、これあるだけで、何もないよりはだいぶいい感じでした。

TODO 画像
-->

![0044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/f73841fa-ac65-add9-98f5-ac5492581138.png)

- 参考: [Emacs でオレオレ言語用モードを簡単作成！ (ヘルプもあるよ！) - Qiita](https://qiita.com/tm_tn/items/3b40b5ab5e72750ffd7f)



<!--
 ----

[なでしこさん マニュアル - 文法/それ](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E3%81%9D%E3%82%8C&show)

なでしこ初心者でまだ慣れていないので「それ」は多用はしていませんが、
連文であることが分かりやすくていいかなと思って下記のような使い方はしていました。

 ```
〜を〜して、それを○○に代入。
〜を〜して、それを戻す。
 ```
-->


----

<!--
最初は慣れてなくて試行錯誤が少しありました。その例。
-->

最初は慣れてなくて試行錯誤が少しありました、という例をメモ。


```
# 「変数宣言パース」はトークン列をパースして変数宣言文オブジェクトを返す自作関数
# 「文リスト」は配列
変数宣言パースして文リストに配列追加
```

`変数宣言パース` 関数の戻り値を `文リスト` に追加したいのですが、これは文法エラーになります。

マニュアルの [「配列追加」命令](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E9%85%8D%E5%88%97%E8%BF%BD%E5%8A%A0&show)  の説明に書かれているように、`AにBを` または `Aへ` の形で引数を渡さなければいけない（関数定義で定められた通りに助詞を添えなければいけない）からです。

ふーむ、なるほど、ということで次のように一時変数を補って2行（2文？）に分けると `AにBを配列追加` の形になり、動くようになります。

```
変数宣言パースして文に代入
文リストに文を配列追加
```

1行で書けないでしょうか？

```
# () はなくてもOK
文リストに（変数宣言パース）を配列追加
```

1行で書けるようになりました。でもなんか微妙ですね……:thinking:

こういう場合は、「それ」を使うと `AにBを配列追加` の形に持ち込むことができます。


```
変数宣言パースして、文リストにそれを配列追加

# 「Aに」「Bを」の順番は入れ替えてもよい
変数宣言パースして、それを文リストに配列追加
```

自然になりました。なるほど、こう書けばいいのかー。

- [なでしこさん マニュアル - 文法/それ](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E3%81%9D%E3%82%8C&show)


----

なでしこ3には敬語があります。

- [なでしこさん マニュアル - 文法/敬語](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E6%95%AC%E8%AA%9E&show)

> なでしこv3.1.14以降、なでしこで礼節をわきまえたプログラムを書くことができるようになりました。

<!--
コンパイラにも礼節があった方がいいだろうということで、次のように書いています。
使ってみました。コンパイラにも礼節があった方がいいですよね。
-->

使ってみました。なでしこ3では礼節のあるコンパイラを書くことができます。

```
# parser.nako3
パースして抽象構文木に代入してください。
抽象構文木をJSONエンコードして、それを表示してください。お願いします。
```

```
# codegen.nako3
抽象構文木からコード生成してください。お願いします。
```







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

<!-- cat cmd -->
https://qiita.com/sonota88/items/d43c9618498062863e58

<!-- 素朴な自作言語のコンパイラをLibreOffice Basicに移植した - Qiita -->
https://qiita.com/sonota88/items/31c127c42a9722892aa8