コンパイラーの可視化

コンパイラーは文字の範囲を分類し、トークンへ階層を与え、木をたどって実行可能なプログラムを書きます。三課はソースから AST、AST からバイトコード、スタック VM での実行です。後の二課は 1 + 2 * 3 を共有し、最初は複数桁を示す 12 + 3 * 4 を使います。

最後の VM は CPU やハイパーバイザーではなく、言語の仮想機械です。操作コードを取り、スタックを変えて繰り返します。ソースの + は木の形になり、ADD は二回 pop して一回 push します。演算子をオペランドより先に出力すると、実行でアンダーフローが起きます。

エラーの担当も異なります。12 + @ 4 は完全なトークン列にならず、12 + * 4 は字句解析後に primary() で失敗します。先頭 ADD は読めるバイトコードでも、実行時にオペランドが足りません。

レッスンの比較

レッスン問い変化失敗境界
字句解析と構文解析優先順位はどこから来るか文字範囲を平らなトークン列にし、文法の呼び出しで階層化@ は字句、オペランド位置の * は構文の失敗
AST → コード1 + 2 * 3 が先に MUL を書く理由後順走査、Fold で定数を先に畳む名前は親の畳み込みを止めても、別部分木は止めない
仮想機械命令順が必要な理由命令でスタックを変え、熱いループをコンパイル演算子先行でアンダーフロー、冷たいループでは JIT なし

解析器の出力とコード生成の入力は同じ演算子階層です。コード生成右側の IR は書かれるプログラム、VM 右側の Stack は実行で使うデータです。この二つを分けます。

推奨する学習順序

字句解析と構文解析で 12 + 3 * 4 を使い、1 と 2 が同じ字句に残る様子を見ます。expr が + を待つ間に term が * を処理します。括弧の例では解析器を変えず、Add が内側、Mul が根になります。字句と構文のエラーを一つずつ試し、トークン化と妥当性を分けます。

AST → コードは Naive で 1 + 2 * 3 を処理します。+ が待つ間に MUL が出ます。同じ木で Fold にすると五命令が LOAD_CONST 7 になります。x + 2 * 3 では乗算は畳めても、名前が未確定なので加算は畳めません。

仮想機械は同じ式をインタープリターで実行します。三回 push し、MUL で 2 * 3 → 6、ADD で 1 + 6 を処理します。これが後順の理由です。直後に ADD first を試すと Underflow が発生します。

スタックの規則を理解してから、任意の JIT タブへ進みます。カウントダウンは二巡を解釈して熱度に達し、コンパイル後は命令の逐次取得表示が止まります。冷たいループは閾値に届かず、コンパイルされません。

注目する点

  • ソース順、トークン順、木の階層、命令順を分けます。+ が先に書かれても、* が入れ子になり、MUL を先に実行します。
  • 有効なトークンでも位置が無効な場合があります。12 + * 4 の STAR は認識できても primary() を開始できません。
  • 頂上の値だけでなく高さを見ます。既定の最大 3 は、最初の演算前に待つ三つの葉です。
  • 空スタック自体は失敗ではありません。初期化は空でよく、足りないオペランドを要求したときに失敗します。
  • JIT 後も残る計算は実行します。ネイティブコードの一拍は逐次 fetch の省略で、O(1) の仕事や計算の飛び越しではありません。

コンパイラーの可視化一覧

式やエンジンを変え、Python のコードとトレースを見ます。字句の段階では構文解析器のコードを光らせず、文字からトークンへの証拠をキャンバスに置いています。