素朴な自作言語のコンパイラをKotlinに移植した


移植一覧に戻る


やっつけなので汚いです。ライフゲームコンパイルが通ったのでヨシ、というレベルのものです。

github.com

移植元

memo88.hatenablog.com

ベースになっているバージョン: tag:51 のあたり

メモ


今回の実験は配列変数の宣言。 trial ブランチで試してみました。

確認用のコードです。今回の実験ではとりあえずこれだけコンパイルできれば OK。

// declare_array.vg.txt

func main() {
  var xs[22];
}

AST がこんな感じ。お試しなので適当。

$ ( \
>   cat declare_array.vg.txt \
>   | ./run.sh tokenize \
>   | ./run.sh parse \
> ) 2>/dev/null
[
  "top_stmts",
  [
    "func",
    "main",
    [

    ],
    [
      [
        "var_array",
        "xs",
        22
      ]
    ]
  ]
]

アセンブリコードがこんな感じ。

$ ( \
>   cat declare_array.vg.txt \
>   | ./run.sh tokenize \
>   | ./run.sh parse \
>   | ./run.sh codegen \
> ) 2>/dev/null
  call main
  exit

label main
  push bp
  cp sp bp

  # 関数の処理本体
  sub_sp 22 # ... 配列のサイズ分の領域をスタック上に確保する

  cp bp sp
  pop bp
  ret

これだけだったら少し修正するだけで済みました。

次のようなコードはまだ正しく動くようにコンパイルできません。

func main() {
  var xs[22];
  var a = 11;
}

配列がスタック上で占有している幅を考慮しないといけないのに、それをまだやってないからです。 これをやろうとするとたぶんあちこち修正しないといけなくなるので、それはまた今度ということで。

vm2gol v2 (51) 機械語コードのフォーマットを固定長風に変更



これまで機械語コードのフォーマットはこのような YAML ファイルにしていました。 (以下便宜的に「可変長風」と呼びます)

---
- call
- 1029
- exit
- label
- vram_set
- push
- bp
...

これを変更して、1行あたり1命令となるようにして、行ごとに JSON.parse でパースできるようにしました。 (以下便宜的に「固定長風」と呼びます)

["call", 1029]
["exit"]
["label", "vram_set"]
["push", "bp"]
...

どの命令でもメモリ上で占める幅が 1 になったため Vm.num_args_for も不要になってすっきりしました。

また、YAML への依存がなくなり JSON への依存に一本化できました。 Ruby でやる分には何でもいいのですが、他の言語に移植するときに若干めんどくさかったので。これでちょっと簡単になるはず。

その他の変更

メモ

元の YAML のように 1命令が複数の行(可変)となるようにしていたのは、機械語といってもバイナリではなくテキストにした上にさらに固定長風にしてしまうとさすがに簡略化しすぎで勉強にならないのではないか、みたいなことを考えていたからでした。たしか。

しかし、今になってみると別にこだわるところではなかったように思えます。こだわる必要がないのであれば簡単な方がよい、というわけで変えてしまうことにしました。

あとは 1行1命令にするとアセンブリコードとの違いがさらに小さくなってアセンブラが仕事してない感じになってしまうけどこれでいいの? ということが気になっていたのですが、そこも気にしないことにしました。


全体で 50行弱減って今このくらいの行数。

   14 common.rb
   58 vgasm.rb
  555 vgcg.rb
  509 vgparser.rb
  513 vgvm.rb
 1649 合計


素朴な自作言語のコンパイラをZigに移植した


移植一覧に戻る


やっつけなので汚いです。ライフゲームコンパイルが通ったのでヨシ、というレベルのものです。

Zig言語(ziglang)を触り始めて1週間くらいの人が雑に書いたものですので、「お手本にできそうな Zig のコード」「かっこいい Zig のコード」を求めている人は参考にならないと思います……。

Zig のバージョンは 0.6.0。 (追記 2021-03-17: 0.7.1 に上げました)

github.com

目次

移植元

memo88.hatenablog.com

ベースになっているバージョン: tag:50 のあたり

(追記 2021-03-17: ステップ56 の修正まで適用しました)

メモ

  • C と Go の中間、GC のない Go みたいな印象
  • sentinel-terminated な配列もなるほど便利そうと思って使ってみたが、通常の配列とは別の型になって煩雑なのと、サイズ固定な配列の出番が実はほとんどなかったので、後から普通の配列+スライスで書き直していった
  • 例外の扱いも Go とは違ったアプローチでおもしろい。最初は試しに使っていたが、お遊びプログラムなのでまじめにやる必要ないかと思い直し、これも後から使わないように(Java でいえば検査例外をその場で RuntimeException に包んで投げ直す感じに)書き変えた。単に煩雑さを避けるのを優先した形なのでほんとはちゃんとハンドリングすべきと思います。
  • 型まわりの理解がまだいまいち(配列や const が絡むあたりとか)
  • あと細かい話がいろいろあった気がするけど、作った後このエントリを書くまでしばらく他のことをしていたら忘れてしまった……思い出したら追記します
    • その間に Zig のバージョンが 0.6 から 0.8 に上がっていた
  • 今回はあんまり余裕なかったので実験的な要素はなし

逆引き的なメモ

標準エラー出力に出力 (v0.6.0)

const std = @import("std");

pub fn main() !void {
    const x = 123;
    const file: std.fs.File = std.io.getStdErr();

    try file.outStream().print("{}\n", .{x});

    // または catch で処理する
    // file.outStream().print("{}", .{x}) catch |err| {
    //     std.debug.panic("error ({})", .{err});
    // };
}

デバッグ用なら std.debug.print が手軽。
https://github.com/ziglang/zig/blob/0.7.1/lib/std/debug.zig#L63

標準入力からの入力をすべて読む

Zig: 1バイトごとに読み書きするだけのcatコマンドを書いてみた

panic (v0.7.1)

https://github.com/ziglang/zig/blob/0.7.1/lib/std/fmt.zig#L1249

const panic = @import("std").debug.panic;

pub fn main() !void {
    panic("PANIC {} {}", .{ -123, "foo" });
}

C の sprintf 相当 (v0.7.1)

https://github.com/ziglang/zig/blob/0.7.1/lib/std/fmt.zig#L1249

const std = @import("std");

pub fn main() !void {
    const s = "foo";
    const n = -123;
    var buf: [16]u8 = undefined;

    const slice: []u8 = try std.fmt.bufPrint(
        &buf,
        "{} bar {}",
        .{ s, n }
    );

    std.debug.print("{}\n", .{ slice });
}

整数 → 文字列 などの変換もこれでできる。

文字列を整数に変換 (v0.7.1)

https://github.com/ziglang/zig/blob/0.7.1/lib/std/fmt.zig#L1072

const std = @import("std");

pub fn main() !void {
    const s = "-123";
    const n = try std.fmt.parseInt(i32, s, 10);
    std.debug.print("{}\n", .{ n });
}

Zig 0.6.0 → 0.7.1 にバージョンアップした際の変更点

(2021-03-17 に追記)

memo88の2020年ブックマークランキング

blog.sushi.money

やってみました。調べてないけど9割方セルクマ 😅

ブクマしてくださった方、ありがとうございます。

memo88の2020年ブックマークランキングベスト24(累計30ブックマーク)

# タイトル
1位 きしださんのかわいいリレーショナルデータベースをRubyで写経した - memo88
2位 リレー式論理回路シミュレータを自作して1bit CPUまで動かした - memo88
3位 Rubyで素朴な自作言語のコンパイラを作った - memo88
4位 LibreOffice Drawのodgファイルから図形の情報を抜き出して使う - memo88
5位 kairo-gokko 製作メモ - memo88
6位 Ruby/Racc: パースに失敗した位置(行、桁)を得る - memo88
7位 素朴な自作言語のコンパイラをGoに移植した - memo88
8位 kairo-gokko (35) 1bit CPU 2 - memo88
9位 kairo-gokko (34) 1bit CPU 1 - memo88
10位 素朴な自作言語のコンパイラをPHPに移植した - memo88
11位 HiveQL テスト 自動化 メモ - memo88
12位 kairo-gokko (33) Dフリップフロップ 2 - memo88
13位 DockerでDigdagサーバを動かす(クライアントの簡単な動作確認用、PostgreSQL不使用) - memo88
14位 素朴な自作言語のコンパイラをPerlに移植した - memo88
15位 素朴な自作言語のコンパイラをCに移植した - memo88
16位 失敗したタスクの情報をdigdagコマンドとシェルスクリプトで取得する - memo88
17位 Ubuntu 18.04にJupyter NotebookとIRubyをインストール(pyenv, rbenv を使用) - memo88
18位 hive.server2.enable.doAs がよく分からなかったので Apache Bigtop で調べてみた - memo88
19位 素朴な自作言語のコンパイラをJavaに移植した - memo88
20位 正規表現エンジン(ロブ・パイクのバックトラック実装)をRubyで写経した - memo88
21位 素朴な自作言語のコンパイラをTypeScript(Deno)に移植した - memo88
22位 素朴な自作言語のコンパイラをDartに移植した - memo88
23位 素朴な自作言語のコンパイラをC♭に移植した - memo88
24位 素朴な自作言語のコンパイラをPythonに移植した - memo88

generated by 年間ブックマークランキングジェネレーター