erg/doc/JA/compiler/phases/10_codegen.md
2024-05-21 21:44:52 +09:00

4.9 KiB

コード生成

badge

Ergスクリプトは、デフォルトではpycファイルに変換されて実行されます。つまり、PythonスクリプトではなくPythonバイトコードとして実行されます。 pycファイルは構文糖が剥がされ(phase 8)、依存関係の結合(phase 9)されたHIRから生成されます。 処理はPyCodeGeneratorが行います。この構造体はHIRを受け取ってCodeObjを返します。 CodeObjはPythonのCodeオブジェクトに対応し、実行する命令列や静的領域のオブジェクト、その他様々なメタデータを持ちます。CodeオブジェクトはPythonインタプリタから見るとスコープを表すオブジェクトです。トップレベルのスコープを表すCodeが、実行に必要な全ての情報を持つことになります。CodeObjdump_as_pycメソッドでバイナリ化され、pycファイルに書き出されます。

Pythonに存在しない機能

Ergランタイム

ErgはPythonインタプリタ上で動作しますが、Pythonとは様々なセマンティクスの違いがあります。 幾つかの機能はコンパイラがより低次の機能に脱糖することで実現されますが、ランタイムで実現するしかないものもあります。

組み込み型のPythonには存在しないメソッドなどがその例です。 Pythonの組み込みにはNat型は存在しませんし、times!メソッドも存在しません。 このようなメソッドは、Pythonの組み込み型をラップした新しい型を作ることで実現しています。

それらの型はに置かれています。 生成されるバイトコードはまず_erg_std_prelude.pyをimportします。このモジュールはErgランタイムの提供する型、関数をre-exportします。

Record

レコードは、Pythonのnamedtupleで実現されています。

トレイト

トレイトは、実体としてはPythonのABC(抽象基底クラス)で実現されています。 といっても、Ergのトレイトは実行時にはほとんど意味を持ちません。

match

パターンマッチは大抵、型判定と代入動作の組み合わせに還元されます。これはコンパイルの比較的早い段階で行われます。

i, [j, *k] = 1, [2, 3, 4]

_0 = 1, [2, 3]
i = _0[0]
_1 = _0[1]
j = _1[0]
k = _1[1:]

しかし、実行時まで遅延するものもあります。

x: Int or Str
match x:
    i: Int -> ...
    s: Str -> ...

このパターンマッチは実行時の判定を必要とします。この判定はin_operatorが行います。

したがって、上のコードを脱糖すると以下のようになります。網羅性検査はコンパイル時に行われます。

if in_operator(x, Int):
    ...
else:
    ...

制御構造をなす関数

for!if!などPythonの制御構造に対応する関数は、最適化状況によって実体が変わります。通常は最適化が行えて、専用のバイトコード命令に還元されます。

for! [a, b], i =>
    ...

LOAD_NAME 0(a)
LOAD_NAME 1(b)
BUILD_LIST 2
GET_ITER
FOR_ITER ...
STORE_NAME 2(i)
...

これは関数呼び出しよりも効率的です。しかし以下のように、最適化が行えない場合もあります。

f! = [for!, ...].choice!()

f! [1, 2], i =>
    ...

このような場合は実体を持つ関数として扱わなくてはなりません。関数は_erg_control.pyに定義されています。