minosys script を作ろう (9)
Minosys script における実行エンジン
参考にした本によれば、実行エンジンの実装には2種類あるそうです。
- 作者: まつもとゆきひろ
- 出版社/メーカー: 日経BP社
- 発売日: 2016/12/22
- メディア: 単行本
- この商品を含むブログ (5件) を見る
どちらも一長一短がありますが、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; };