* 「10日くらいでできる!プログラミング言語自作入門」の続編#2-2 (HL-31)
-(by [[K]], 2022.06.25)
** (1) 今回の改造テーマ
-今回は、関数を定義して呼び出せるようになりたいです。
-いきなりあれこれとやると大変なので、まずは引数なし、返値なし、ローカル変数なしで考えます。
** (2-1) 関数型を追加
-before:
enum { TypInt, TypDbl };
-after:
enum { TypInt, TypDbl, TypFnc };
** (2-2) 内部命令を追加(1)
-before:
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpCgt, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpAnd, OpShr,
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, OpJle, OpJgt, OpLop, OpPrint, OpTime, OpEnd,
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, OpOpnWin, OpSetPix0, OpM64s, OpRgb8, OpWait,
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, OpF16Cos, OpInkey, OpDrwStr0, OpGprDec, OpBitBlt,
OpPrintd, OpAddDbl, OpSubDbl, OpMulDbl, OpDivDbl, OpCpyDbl, OpIntToDbl, OpDblToInt };
-after:
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpCgt, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpAnd, OpShr,
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, OpJle, OpJgt, OpLop, OpPrint, OpTime, OpEnd,
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, OpOpnWin, OpSetPix0, OpM64s, OpRgb8, OpWait,
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, OpF16Cos, OpInkey, OpDrwStr0, OpGprDec, OpBitBlt,
OpPrintd, OpAddDbl, OpSubDbl, OpMulDbl, OpDivDbl, OpCpyDbl, OpIntToDbl, OpDblToInt, OpCall, OpRet };
** (2-3) 内部命令を追加(2)
-関数exec()の直前あたりに以下を追加。
IntP *icpRet;
-case OpDblToInt: の下に以下を追加。
case OpCall: icpRet = icp + 5; icp = (IntP *) icp[1]; continue;
case OpRet: icp = icpRet; continue;
-とりあえず今は手抜き実装にしていて、リターンアドレスをスタックには積んでいません。だから関数の中から関数を呼ぶと誤作動します。先のバージョンで直します。
** (2-4) ブロック構文に関数定義を追加
-before:
enum { BlkIf = 1, BlkFor, BlkMain };
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01, ForWpc11, ForWpc02, ForWpc12 };
-after:
enum { BlkIf = 1, BlkFor, BlkMain, BlkFunc };
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01, ForWpc11, ForWpc02, ForWpc12 };
enum { FncSkp = 1 };
** (2-5) 構文を追加
-compile()関数に以下を追加。phrCmp(36, "double", pc) のあとあたり。
} else if (phrCmp(37, "void !!*0() {", pc)) {
bd += BInfSiz;
binf[bd] = BlkFunc;
binf[bd + FncSkp] = tmpLabelAlloc();
putIc(OpGoto, &var[binf[bd + FncSkp]], &var[binf[bd + FncSkp]], 0, 0);
var[tc[wpc[0]]] = icq - ic; // ラベルに対応するicqを記録しておく.
varTyp[tc[wpc[0]]] = TypFnc;
} else if (phrCmp(38, "}", pc) && binf[bd] == BlkFunc) {
var[binf[bd + FncSkp]] = icq - ic; // ラベルに対応するicqを記録しておく.
bd -= BInfSiz;
} else if (phrCmp(39, "return;", pc)) {
putIc(OpRet, 0, 0, 0, 0);
** (2-6) 関数呼び出し演算子の実装
-追加する場所は phrCmp(99, "( !!**0 )", epc) の前あたり
} else if (phrCmp(82, "!!*0()", epc) && varTyp[tc[wpc[0]]] == TypFnc) {
putIc(OpCall, &var[tc[wpc[0]]], &var[tc[wpc[0]]], 0, 0);
i = Tc0; // 本当はvoidだから0ではないのだけれど、0ということにしておく.
** (2-7) 分岐処理に対する前処理の改造
-before:
for (icq = ic; icq < icq1; icq += 5) { // goto先の設定.
i = (int) icq[0];
if ((OpGoto <= i && i <= OpLop) {
icp = *icq[1] + ic;
while ((int) icp[0] == OpGoto) { // 飛び先がOpGotoだったら、さらにその先を読む(最適化).
icp = *icp[2] + ic;
}
icq[1] = (IntP) icp;
}
}
-after:
for (icq = ic; icq < icq1; icq += 5) { // goto先の設定.
i = (int) icq[0];
if (OpGoto <= i && i <= OpLop) || i == OpCall) {
icp = *icq[1] + ic;
while ((int) icp[0] == OpGoto) { // 飛び先がOpGotoだったら、さらにその先を読む(最適化).
icp = *icp[2] + ic;
}
icq[1] = (IntP) icp;
}
}
** (3) テスト
-テストプログラム
print 123;
void fnc()
{
print 789;
}
print 456;
fnc();
fnc();
-実行結果
123
456
789
789
** 次回に続く
次回: ''a22_txt03_2a''
*こめんと欄
#comment