Oberon-07 のシンボル・ファイル

プログラミング言語 Oberon-07 は Modula 言語族に属し、 翻訳時環境の束縛対を永続化してシンボル・ファイルを作成する、 この言語族共通の機能を持ちます。 永続化の対象は、 翻訳単位である MODULE 宣言のトップ・レベルと、 そこから見える構造体フィールドです。 それらの中で EXPORT 指定をしてある識別子を抜き出して独自のバイナリ形式にエンコードします。 翻訳時環境の束縛対は識別子を意味オブジェクトに束縛しており、 意味オブジェクトには型式と初期値等の定数を含みます。 厳密に言えば違いがあるものの、 C 言語族のヘッダ・ファイルに相当するものを自動的にソース・コードから生成するような感じです。

型式は循環有向グラフになっており、 IMPORT した他の MODULE の型式にまたがっています。 Oberon-07 のエンコードでは、 他の MODULE の型式は、 型式のままエンコードし、 その後に MODULE 名と型名を並べます。 IMPORT 時に IMPORT MODULE 記述子をトップ・レベルに追加し、 その下に、 その MODULE のシンボル・ファイルから読み込んだ束縛対をデコードして格納します。 そのため、 複数の MODULE からある MODULE の名前付き型を参照していると、 同じ型式を何度も主記憶内にデコードして形成するのですが、 既に IMPORT 済みのときは、 せっかく作った型式を使わずに、 既存の型式を返します。

Oberon-07 は完全なゴミ集めをサポートしており、 構造体オブジェクトには 2 つの隠しフィールドを割り当てます。 その1つはマーク・フィールドです。 もう 1 つでタグを参照し、 タグに構造体が持つポインタ・フィールドのオフセットを並べてあります。 このタグは翻訳子のコード生成部が作成します。 タグ作成に使うために、 シンボル・ファイルに EXPORT されていなくてもポインタ型のフィールドを匿名フィールドとしてエンコードしてあります。

Oberon システムでは、 MODULE ごとにローダブル・バイナリへ翻訳し、 共用ライブラリとして利用します。 そのため、 ある MODULE を翻訳した後に、 IMPORT した MODULE を後に翻訳しなおすと、 不整合が生じます。 そのため、 シンボル・ファイルには、 それ自身のチェック・サムを計算して格納してあり、 ローダブル・バイナリにも翻訳時に IMPORT した MODULE シンボル・ファイルのチェック・サムを転記しています。 ローダはチェック・サムを調べて、 一致しないときは MODULE の不整合が生じていることを報告して、 ロードを防ぎます。

FPGA で作られた Oberon システム用の Oberon-07 翻訳子のソース・コードから読み取った、 シンボル・ファイルの構文は次のようになっています。 記述形式は Parsing Expression Grammar を参考にしています。 INTEGER は 4 オクテットの符号付きリトル・エンディアンのバイナリ整数エンコーディングです。 STRING はゼロ終端オクテット列で可変長です。 空文字列はゼロの 1 オクテットで表します。 BYTE は符号付き 1 オクテット整数です。 NUM は符号付き 32 ビット整数を、 7 ビット単位に分割して可変長にエンコードしています。 これもリトル・エンディアンです。 小さな数値は 1 オクテットに、 大きくなるにつれて必要なオクテット数が増えていきます。 記録媒体のバイト単価が高価だった Modula-2 時代の名残りなのでしょう。

    symbol_table
     <- INTEGER{0}
        INTEGER{thismod.val}
        STRING{thismod.name}
        BYTE{VersionKey==1}
        (   BYTE{obj.class==Typ}
            STRING{obj.name}
            type{obj.type}
            (   BYTE{ref!=0 && typTab[ref]:=obj.type}
            )*
            BYTE{0}
        /   BYTE{obj.class==Const}
            STRING{obj.name}
            type{obj.type==Real}
            INTEGER{obj.val}
        /   BYTE{obj.class==Const}
            STRING{obj.name}
            type{obj.type!=Real}
            NUM{obj.val}
        /   BYTE{obj.class==Var}
            type{obj.type}
            NUM{obj.val}
        /   BYTE{obj.class}
            type{obj.type}
        )*
        BYTE{0}

    type
     <- BYTE{ref<0 && typTab[-ref]}
      / BYTE{ref>0 && typTab[ref]}
        (   BYTE{typTab[ref].form==Pointer}
            type{typTab[ref].base}
        /   BYTE{typTab[ref].form==Array}
            type{typTab[ref].base}
            NUM{typTab[ref].len}
            NUM{typTab[ref].size}
        /   BYTE{typTab[ref].form==Record}
            type{typTab[ref].base}
            NUM{typTab[ref].len}
            NUM{typTab[ref].nofpar}
            NUM{typTab[ref].size}
            (   BYTE{fld.class==Fld}
                STRING{fld.name!=""}
                type{fld.type}
                NUM{fld.val}
            /   BYTE{fld.class==Fld}
                STRING{fld.name==""}
                NUM{fld.val}
            )*
            BYTE{0}
        /   BYTE{typTab[ref].form==Proc}
            type{typTab[ref].base}
            (   BYTE{par.class}
                BYTE{par.rdo}
                type{par.type}
            )*
            BYTE{0}
        /   BYTE{0}
        )
        (   STRING{mod.name!=""}
            INTEGER{mod.val}
            STRING{obj.name}
        /   STRING{""}
        )

    BYTE <- .{x && (x < 128 ? x : x - 256)}

    STRING <- (!'\0' .)* '\0'

    INT <- BYTE{x0} BYTE{x1} BYTE{x2} BYTE{x3}  {(x3<<24)|(x2<<16)|(x1<<8)|x0}

    NUM <- (BYTE{x>127})* BYTE{x<128}