読者です 読者をやめる 読者になる 読者になる

徒然なる日々を送るソフトウェアデベロッパーの記録(2)

技術上思ったことや感じたことを気ままに記録していくブログです。さくらから移設しました。

minosys script を作ろう (9)

Minosys script における実行エンジン


参考にした本によれば、実行エンジンの実装には2種類あるそうです。

どちらも一長一短がありますが、minosys script では
解析木の木構造のまま実装することにします。

理由は以下の通りです。

  • 実行効率的の高い中間コードを考察するのに手間がかかる
  • minosys script は1ページでせいぜい 30 行くらいしか記述されない

ただし、多くのアクセスを集めるようなページの場合、中間コード
コンパイルした方が CPU の負荷(およびトータルのメモリ
アクセス回数)は減ります。

スタックマシンを採用

また、minosys script では、パラメータの評価にスタックマシンを
使用します。これも世の中の実装にはレジスタマシン派とスタックマシン
派があるようなのですが、スタックマシンは構造が単純で実装
しやすい利点があります。欠点としてはメモリアクセス回数が
多くなりやすく、パフォーマンスが落ちがちです。

3つのスタックを使用

以上を踏まえて、minosys script では3つのスタックを用意します。

  • 関数呼び出し時の引数スタック
  • 評価式の評価およびその戻り値を格納するパラメータスタック
  • 関数呼び出しや複文の実行を制御するコールスタック

各スタックは関数呼び出し時にそのスタック位置を記録するマークを付けて
置きます。
minosys script ではスタックの解放は呼び出し側の責任で行います。
マークをつけておけば関数がスタックを使いっぱなしにしても
元のスタックサイズに戻すことが容易です。

普通の言語実装では引数スタックとパラメータスタックは一緒にする
ことが多いと思いますが、minosys script では仮引数の解析を
解析木作成時に行わないため、実行時に解析するために別になっています。

グローバル変数の取り扱い

global 宣言した変数はどの関数・メンバー関数でも使用できるように
なりますが、そのための入れ物としてグローバル変数一覧を持つように
します。

そのため、minosys script の変数探索は若干複雑なものとなっています。
具体的には以下の順序で変数を探索します。

  • ローカル変数定義を複文の内側から外側に向かってチェックする
  • 引数リストをチェックする
  • グローバル変数をチェックする

この操作は Var *searchVar(const string &) 関数に記述しておきます。

パッケージ一覧

minosys script ではパッケージは一度しか読み込まないため、再帰問題
は発生しません。これは別の言葉でいうと、パッケージがパッケージ名
で管理されることを意味します。
パッケージには minosys script パッケージとバイナリパッケージがある
ため、両者の基本クラス PackageBase を作成します。

以上を踏まえて実行エンジンのメンバー変数を以下のように定義します。

class Engine {
 public:
  unordered_map<string, Ptr<PackageBase> > packages;
  unordered_map<string, Ptr<Var> > globalvars;
  vector<unordered_map<string, Ptr<Var> > > vars;
  vector<int> varmark;
  vector<Ptr<Var> > paramstack;
  vector<int> topmark;
  vector<Content *> callstack;
  vector<int> callmark;
};

次回は実行エンジンの残りの機能であるアーカイブインスタンス表現
について説明します。