* 「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

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS