川合のプログラミング言語自作のためのテキスト第三版#8
(1) HL-8a
- もう少し詳しく言うと、こんな感じです。
- [1]変数の値(var[])の型をintではなく、intptr_tにする。
- こうすることで、変数にポインタの値を入れることもできるようになる。それを使って配列や文字列リテラルをサポートする。
- intのままでも32bitなら問題ないけど、x64などのようにintとポインタの幅が異なる環境ではうまくいかなくなるので、一般的にはintptr_tにしたほうがよい。
- [2]getTc()やlexer()を改造して、文字列リテラルを受け付けるようにする。
- そしてcompile()にprints命令を追加して、exec()にOpPrintsを追加して、文字リテラルを表示できるようにする。
- [3]配列については、OpArySet, OpAryGet, OpAryNew, OpAryInitという内部命令を追加して、配列の操作をする。exprSub()とcompile()とexec()を改造して対応させる。
- [4]ついでのおまけ。加算と減算だけ、「a=b+c;」みたいな形式をサポートしていたが、これをすべての二項演算子に拡大する。
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h> // intptr_tを使うため.
typedef unsigned char *String; // こう書くと String は unsigned char * の代用になる.
int loadText(String path, String t, int siz) → HL-4と同じなので省略
///////////////////////////////////////////////////////////////////////////////
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当たり平均10バイトを想定.
int tcs = 0, tcb = 0;
int var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-4と同じなので省略
///////////////////////////////////////////////////////////////////////////////
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなので省略
int lexer(String s, int tc[]) → HL-2と同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, Tc4, Tc5, Tc6, Tc7, Tc8, TcBrOpn, TcBrCls, TcSqBrOpn, TcSqBrCls, TcCrBrOpn, TcCrBrCls,
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinus, TcAster, TcSlash, TcPerce, TcAnd, TcShr, TcPlPlus, TcEqu,
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, TcTmp3, TcTmp4, TcTmp5, TcTmp6, TcTmp7, TcTmp8, TcTmp9 };
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } == != < >= <= > + - * / % & >> ++ = , !!** !!*** _t0 _t1 _t2 _t3 _t4 _t5 _t6 _t7 _t8 _t9";
///////////////////////////////////////////////////////////////////////////////
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:一致したフレーズの次のトークンをさす, wpc[]:ワイルドカードのトークンの場所をさす.
int phrCmp(int pid, String phr, int pc) → HL-7と同じなので省略
///////////////////////////////////////////////////////////////////////////////
typedef int *IntP; // こう書くと IntP は int * の代わりに使えるようになる.
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 };
IntP ic[10000], *icq; // ic[]:内部コード、icq:ic[]への書き込み用ポインタ.
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) → HL-6と同じなので省略
///////////////////////////////////////////////////////////////////////////////
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() → HL-7と同じなので省略
void tmpFree(int i) → HL-7と同じなので省略
///////////////////////////////////////////////////////////////////////////////
int epc, epc1; // exprのためのpcとpc1.
int exprSub(int priority); // exprSub1()が参照するので、プロトタイプ宣言.
int expr(int j);
int exprSub1(int i, int priority, int op) → HL-7と同じなので省略
int exprSub(int priority) → HL-7と同じなので省略
int expr(int j) → HL-7と同じなので省略
/////////////////////////////////////////////////////////////////////////////// → ここから下は全部HL-8で新規追加(ここより上は一切変化なし)
enum { IfTrue = 0, IfFalse = 1 };
void ifgoto(int i, int not, int label)
{
int j = wpc[i];
if (j + 3 == wpc1[i] && TcEEq <= tc[j + 1] && tc[j + 1] <= TcGt) { // 条件式の長さが3トークンで、真ん中が比較演算子だったら.
putIc(((tc[j + 1] - TcEEq) ^ not) + OpJeq, &var[label], &var[tc[j]], &var[tc[j + 2]], 0);
} else {
i = expr(i);
putIc(OpJne - not, &var[label], &var[i], &var[Tc0], 0);
tmpFree(i);
}
}
int tmpLabelNo;
int tmpLabelAlloc()
{
char s[10];
sprintf(s, "_l%d", tmpLabelNo);
tmpLabelNo++;
return getTc(s, strlen(s));
}
#define BInfSiz 10
int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd:block-depth, lbd:loop-block-depth
enum { BlkIf = 1, BlkFor };
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01, ForWpc11, ForWpc02, ForWpc12 };
/////////////////////////////////////////////////////////////////////////////// → HL-8での新規追加の範囲終了
int compile(String s)
{
! int pc, pc1, i, j;
! IntP *icq1, *icp;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが多いので、付けてあげる.
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = TcDot; // エラー表示用のために末尾にピリオドを登録しておく.
icq = ic;
for (i = 0; i < 10; i++) { ・// 一時変数をすべて未使用にする.
tmp_flag[i] = 0;
}
+ tmpLabelNo = 0;
+ bd = lbd = 0;
for (pc = 0; pc < pc1; ) { // コンパイル開始.
! int e0 = 0, e2 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0, 0);
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2 < !!*3) goto !!*4;", pc) && tc[wpc[0]] == tc[wpc[1]] && tc[wpc[0]] == tc[wpc[2]]) {
putIc(OpLop, &var[tc[wpc[4]]], &var[tc[wpc[0]]], &var[tc[wpc[3]]], 0);
} else if (phrCmp( 9, "!!*0 = !!*1 + 1;", pc) && tc[wpc[0]] == tc[wpc[1]]) { // 高速化のために+1専用の命令を用意(せこくてすみません).
putIc(OpAdd1, &var[tc[wpc[0]]], 0, 0, 0);
} else if (phrCmp( 2, "!!*0 = !!*1 + !!*2;", pc)) { // 加算.
putIc(OpAdd, &var[tc[wpc[0]]], &var[tc[wpc[1]]], &var[tc[wpc[2]]], 0);
} else if (phrCmp( 3, "!!*0 = !!*1 - !!*2;", pc)) { // 減算.
putIc(OpSub, &var[tc[wpc[0]]], &var[tc[wpc[1]]], &var[tc[wpc[2]]], 0);
} else if (phrCmp( 4, "print !!**0;", pc)) { // print.
e0 = expr(0);
putIc(OpPrint, &var[e0], 0, 0, 0);
} else if (phrCmp( 0, "!!*0:", pc)) { // ラベル定義命令.
var[tc[wpc[0]]] = icq - ic; // ラベルに対応するicqを記録しておく.
} else if (phrCmp( 5, "goto !!*0;", pc)) { // goto.
! putIc(OpGoto, &var[tc[wpc[0]]], &var[tc[wpc[0]]], 0, 0); // OpGotoの仕様変更.
! } else if (phrCmp( 6, "if (!!**0) goto !!*1;", pc)) {
! ifgoto(0, IfTrue, tc[wpc[1]]);
} else if (phrCmp( 7, "time;", pc)) {
putIc(OpTime, 0, 0, 0, 0);
+ } else if (phrCmp(11, "if ( !!**0 ) {", pc)) { // ブロックif.
+ bd += BInfSiz;
+ binf[bd] = BlkIf;
+ binf[bd + IfLabel0] = tmpLabelAlloc(); // 条件不成立のときの飛び先.
+ binf[bd + IfLabel1] = 0;
+ ifgoto(0, 1, binf[bd + IfLabel0]); // 条件を満たさなければ、binf[bd + IfLabel0]へgotoする.
+ } else if (phrCmp(12, "} else {", pc) && binf[bd] == BlkIf) {
+ binf[bd + IfLabel1] = tmpLabelAlloc(); // else節の終端.
+ putIc(OpGoto, &var[binf[bd + IfLabel1]], &var[binf[bd + IfLabel1]], 0, 0);
+ var[binf[bd + IfLabel0]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ } else if (phrCmp( 13, "}", pc) && binf[bd] == BlkIf) {
+ if (binf[bd + IfLabel1] == 0) {
+ var[binf[bd + IfLabel0]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ } else {
+ var[binf[bd + IfLabel1]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ }
+ bd -= BInfSiz;
+ } else if (phrCmp(14, "for (!!***0; !!***1; !!***2) {", pc)) { // for文
+ bd += BInfSiz;
+ binf[bd] = BlkFor; // ブロックのタイプ.
+ binf[bd + ForLopBgn] = tmpLabelAlloc(); // ループの頭に戻る用.
+ binf[bd + ForCont ] = tmpLabelAlloc(); // continue用.
+ binf[bd + ForBrk ] = tmpLabelAlloc(); // break用.
+ binf[bd + ForLbd0 ] = lbd; // 古い値を保存.
+ binf[bd + ForWpc01 ] = wpc [1];
+ binf[bd + ForWpc11 ] = wpc1[1];
+ binf[bd + ForWpc02 ] = wpc [2];
+ binf[bd + ForWpc12 ] = wpc1[2];
+ lbd = bd;
+ e0 = expr(0);
+ if (wpc[1] < wpc1[1]) { // !!***1に何らかの式が書いてあった.
+ ifgoto(1, IfFalse, binf[bd + ForBrk]); // 最初から条件不成立ならbreakへ.
+ }
+ var[binf[bd + ForLopBgn]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ } else if (phrCmp(15, "}", pc) && binf[bd] == BlkFor) {
+ var[binf[bd + ForCont]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ i = binf[bd + ForWpc01];
+ j = binf[bd + ForWpc02];
+ if (i + 3 == binf[bd + ForWpc11] && j + 2 == binf[bd + ForWpc12] && tc[i] == tc[j] && tc[i + 1] == TcLt && tc[j + 1] == TcPlPlus) {
+ // !!***1が「i < ?」かつ、!!***2が「i++」だったら(変数名はiじゃなくてもいいけど、共通である必要がある).
+ putIc(OpLop, &var[binf[bd + ForLopBgn]], &var[tc[i]], &var[tc[i + 2]], 0);
+ } else {
+ wpc [1] = binf[bd + ForWpc01];
+ wpc1[1] = binf[bd + ForWpc11];
+ wpc [2] = binf[bd + ForWpc02];
+ wpc1[2] = binf[bd + ForWpc12];
+ e2 = expr(2);
+ if (wpc[1] < wpc1[1]) { // !!***1に何らかの式が書いてあった.
+ ifgoto(1, IfTure, binf[bd + ForLopBgn]);
+ } else {
+ putIc(OpGoto, &var[binf[bd + ForLopBgn]], &var[binf[bd + ForLopBgn]], 0, 0);
+ }
+ }
+ var[binf[bd + ForBrk]] = icq - ic; // ラベルに対応するicqを記録しておく.
+ lbd = binf[bd + ForLbd0]; // 以前の値を復元.
+ bd -= BInfSiz;
+ } else if (phrCmp(16, "continue;", pc) && lbd > 0) {
+ putIc(OpGoto, &var[binf[lbd + ForCont]], &var[binf[lbd + ForCont]], 0, 0);
+ } else if (phrCmp(17, "break;", pc) && lbd > 0) {
+ putIc(OpGoto, &var[binf[lbd + ForBrk ]], &var[binf[lbd + ForBrk ]], 0, 0);
+ } else if (phrCmp(18, "if ( !!**0 ) continue;", pc) && lbd > 0) {
+ ifgoto(0, IfTrue, binf[lbd + ForCont]);
+ } else if (phrCmp(19, "if ( !!**0 ) break;", pc) && lbd > 0) {
+ ifgoto(0, IfTrue, binf[lbd + ForBrk ]);
} else if (phrCmp( 8, "!!***0;", pc)) { // これはかなりマッチしやすいので最後にする.
e0 = expr(0);
} else
goto err;
tmpFree(e0);
+ tmpFree(e2);
! if (e0 < 0 || e2 < 0) goto err;
pc = ppc1;
}
+ if (bd > 0) {
+ printf("block nesting error (bd=%d, lbd=%d, pc=%d, pc1=%d\n", bd, lbd, pc, pc1);
+ return -1;
+ }
putIc(OpEnd, 0, 0, 0, 0);
icq1 = icq;
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;
}
}
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], ts[tc[pc + 1]], ts[tc[pc + 2]], ts[tc[pc + 3]]);
return -1;
}
void exec()
{
(中略)
+ case OpJge: if (*icp[2] >= *icp[3]) { icp = (IntP *) icp[1]; continue; } icp += 5; continue;
+ case OpJle: if (*icp[2] <= *icp[3]) { icp = (IntP *) icp[1]; continue; } icp += 5; continue;
+ case OpJgt: if (*icp[2] > *icp[3]) { icp = (IntP *) icp[1]; continue; } icp += 5; continue;
(中略)
}
int run(String s) → HL-6と同じなので省略
///////////////////////////////////////////////////////////////////////////////
int main(int argc, const char **argv) → HL-5と同じなので省略
- トータルの行数は560行になっています。
- 後で説明しますが、ブロックifとfor文を入れただけではなく、OpGotoの最適化の追加や、今までサボっていたOpJge, OpJle, OpJgtの追加もしました。
(2) HL-8の簡単な説明
- 今回からは、HL-8で新規に追加されたもの、変更を加えた部分のみ説明します(長くなってきたので)。
- 関数:
- void ifgoto(int i, int not, int label)
- 条件式wpc[i]を評価して、その結果に応じてlabel(トークンコード)に分岐するコードを内部コードに出力します。
- notはフラグで、IfTrueの場合は、条件が成立した場合に分岐させます。IfFalseの場合は、条件が不成立の場合に分岐させます。
- int tmpLabelAlloc()
- 一時ラベル(一時変数のラベル版)を発行してトークンコードで返します。
- 一時ラベルは使い終わった後に再利用するということはないので、Freeはありません。
- 変数:
- int tmpLabelNo
- 一時ラベルのラベル名を重複なく生成するための通し番号です。
- int binf[], bd, lbd;
- binf[]はblock-info.で、つまりコードブロックの情報です。bdはblock-depthで、ブロックの深さ、lbdはループ命令(今はforしかないですが)のbdです。
- bdやlbdは1ずつ増減するのではなく、BInfSizずつ増減します。
- binf[bd]は今のコードブロックが何のコードブロックなのかを表します。現状では、BlkIfかBlkForしかありません。
- binf[bd - BInfSiz]は今のコードブロックの1つ外側、binf[bd - BInfSiz * 2]は今のコードブロックの2つ外側の情報になります。
- よし詳しい情報は、binf[bd + 1~]に入っています。
- こういう変数がないと、ソースコード中に「 } 」があっても、ifの終わりなのか、forの終わりなのか、すぐには判断できなくなってしまいます。
(3) ブロックif文について
- ブロックif文のためにcompile()に追加された部分を中心に説明します。
- まず HL-8 では、ブロックif文を次のように変換することで、実現しています。
} else if (phrCmp(11, "if ( !!**0 ) {", pc)) { // ブロックif.
bd += BInfSiz;
binf[bd] = BlkIf;
binf[bd + IfLabel0] = tmpLabelAlloc(); // 条件不成立のときの飛び先.
binf[bd + IfLabel1] = 0;
ifgoto(0, 1, binf[bd + IfLabel0]); // 条件を満たさなければ、binf[bd + IfLabel0]へgotoする.
- ブロックif文が現れたら、新しいコードブロックが始まるので、binf[]を準備します。
- そして_tmpLabel0を準備します。
- 最後に「if (!条件式) goto _tmpLabel0;」に相当する内部コードをifgoto()で生成します。
} else if (phrCmp(12, "} else {", pc) && binf[bd] == BlkIf) {
binf[bd + IfLabel1] = tmpLabelAlloc(); // else節の終端.
putIc(OpGoto, &var[binf[bd + IfLabel1]], &var[binf[bd + IfLabel1]], 0, 0);
var[binf[bd + IfLabel0]] = icq - ic; // ラベルに対応するicqを記録しておく.
- 「 } else { 」が来たら、これは上記の[2]の場合に相当します。
- _tmpLabel1も必要になるのでそれを準備したのち、goto _tmpLabel1;を出力します。
- ここで、OpGotoの仕様が変わって、飛び先を二度指定するようになっています。なぜそうしなければいけないかは後で説明します。
- そして、_tmpLabel0のラベルはここだよと宣言します。
- これで「 } else { 」の処理はおしまいです。
} else if (phrCmp( 13, "}", pc) && binf[bd] == BlkIf) {
if (binf[bd + IfLabel1] == 0) {
var[binf[bd + IfLabel0]] = icq - ic; // ラベルに対応するicqを記録しておく.
} else {
var[binf[bd + IfLabel1]] = icq - ic; // ラベルに対応するicqを記録しておく.
}
bd -= BInfSiz;
- ブロックif文のコードブロックを閉じたときの処理です。
- これは[1]の場合と[2]の場合とで異なります。
- [1]の場合は、「_tmpLabel0:」の処理をやります。
- [2]の場合は、「_tmpLabel1:」の処理をやります。
- 最後にbdをBInfSizだけ減じて、コードブロックを終了します。
(4) for文について
- 上記のブロックif文と似たような方法で、for文も実現しています。
- for文の変換は次のようにしています。
} else if (phrCmp(14, "for (!!***0; !!***1; !!***2) {", pc)) { // for文
bd += BInfSiz;
binf[bd] = BlkFor; // ブロックのタイプ.
binf[bd + ForLopBgn] = tmpLabelAlloc(); // ループの頭に戻る用.
binf[bd + ForCont ] = tmpLabelAlloc(); // continue用.
binf[bd + ForBrk ] = tmpLabelAlloc(); // break用.
binf[bd + ForLbd0 ] = lbd; // 古い値を保存.
binf[bd + ForWpc01 ] = wpc [1];
binf[bd + ForWpc11 ] = wpc1[1];
binf[bd + ForWpc02 ] = wpc [2];
binf[bd + ForWpc12 ] = wpc1[2];
lbd = bd;
e0 = expr(0);
if (wpc[1] < wpc1[1]) { // !!***1に何らかの式が書いてあった.
ifgoto(1, IfFalse, binf[bd + ForBrk]); // 最初から条件不成立ならbreakへ.
}
var[binf[bd + ForLopBgn]] = icq - ic; // ラベルに対応するicqを記録しておく.
- for文では、あとで(=コードブロックを閉じるときに)条件式や式2の部分を利用するので、それらも全部binf[]にしまっておきます。
- 「e0 = expr(0);」で 式0 の部分を内部コードとして出力させます。
- そして条件式に何か書いてあれば、条件不成立時にはループに侵入しないようにします。
- 最後に _tmpLabel0: に相当する処理をしています。
} else if (phrCmp(15, "}", pc) && binf[bd] == BlkFor) {
var[binf[bd + ForCont]] = icq - ic; // ラベルに対応するicqを記録しておく.
i = binf[bd + ForWpc01];
j = binf[bd + ForWpc02];
if (i + 3 == binf[bd + ForWpc11] && j + 2 == binf[bd + ForWpc12] && tc[i] == tc[j] && tc[i + 1] == TcLt && tc[j + 1] == TcPlPlus) {
// !!***1が「i < ?」かつ、!!***2が「i++」だったら(変数名はiじゃなくてもいいけど、共通である必要がある).
putIc(OpLop, &var[binf[bd + ForLopBgn]], &var[tc[i]], &var[tc[i + 2]], 0);
} else {
wpc [1] = binf[bd + ForWpc01];
wpc1[1] = binf[bd + ForWpc11];
wpc [2] = binf[bd + ForWpc02];
wpc1[2] = binf[bd + ForWpc12];
e2 = expr(2);
if (wpc[1] < wpc1[1]) { // !!***1に何らかの式が書いてあった.
ifgoto(1, IfTrue, binf[bd + ForLopBgn]);
} else {
putIc(OpGoto, &var[binf[bd + ForLopBgn]], &var[binf[bd + ForLopBgn]], 0, 0);
}
}
var[binf[bd + ForBrk]] = icq - ic; // ラベルに対応するicqを記録しておく.
lbd = binf[bd + ForLbd0]; // 以前の値を復元.
bd -= BInfSiz;
- これはfor文のコードブロックを閉じるときの処理です。
- ちょっと長くなっていますが、これはOpLopが使える場合には使ってやろうとして、そのせいでややこしくなっています。
- OpLopを使わない、一般の場合の処理を見れば、「 _tmpLabel1: 」したあとで、「e2 = expr(2);」で 式2 を内部コードに変換し、さらに条件式を評価させて _tmpLabel0 に条件分岐させています。
- そして最後に「 _tmpLabel2: 」しています。
} else if (phrCmp(16, "continue;", pc) && lbd > 0) {
putIc(OpGoto, &var[binf[lbd + ForCont]], &var[binf[lbd + ForCont]], 0, 0);
} else if (phrCmp(17, "break;", pc) && lbd > 0) {
putIc(OpGoto, &var[binf[lbd + ForBrk ]], &var[binf[lbd + ForBrk ]], 0, 0);
- これはcontinue文とbreak分です。どちらもOpGotoで無条件分岐しています。
} else if (phrCmp(18, "if ( !!**0 ) continue;", pc) && lbd > 0) {
ifgoto(0, IfTrue, binf[lbd + ForCont]);
} else if (phrCmp(19, "if ( !!**0 ) break;", pc) && lbd > 0) {
ifgoto(0, IfTrue, binf[lbd + ForBrk ]);
- これは条件付きのcontinueとbreak;です。ifgotoで処理しています。
(5) OpGoto最適化について
(6) ifgoto()の仕組みについて
- これは短くて簡単な関数ではありますが、説明があったほうがいいかもしれないと思ったので書きます。
void ifgoto(int i, int not, int label)
{
int j = wpc[i];
if (j + 3 == wpc1[i] && TcEEq <= tc[j + 1] && tc[j + 1] <= TcGt) { // 条件式の長さが3トークンで、真ん中が比較演算子だったら.
putIc(((tc[j + 1] - TcEEq) ^ not) + OpJeq, &var[label], &var[tc[j]], &var[tc[j + 2]], 0);
} else {
i = expr(i);
putIc(OpJne - not, &var[label], &var[i], &var[Tc0], 0);
tmpFree(i);
}
}
- まずnot=0、つまりIfTrueを指定した場合だけを考えます。
- すると、条件式の長さが3で、かつ真ん中が比較演算子の場合は、TcEEq~TcGtがそのままOpJeq~OpJgtになります(^0という演算は何もしないのと同じなので)。
- そしてそれ以外の場合は、式をexpr(i);で評価した後で、その結果がTc0と比較されて、OpJneで条件分岐することになります。
- 「式の値がゼロでなければ分岐せよ」なので、これでいいわけです。
- では今度はnot=1、つまりIfFalseを指定した場合だけを考えます。
- 条件式の長さが3で、かつ真ん中が比較演算子の場合、
真ん中の比較演算子(=tc[j+1]) | tc[j+1]-TcEEq(→A値) | A値^1(→B値) | B値+OpJeq |
TcEEq(==) | 0 | 1 | OpJne(!=) |
TcNEq(!=) | 1 | 0 | OpJeq(==) |
TcLt (< ) | 2 | 3 | OpJge(>=) |
TcGe (>=) | 3 | 2 | OpJlt(< ) |
TcLe (<=) | 4 | 5 | OpJgt(> ) |
TcGt (> ) | 5 | 4 | OpJle(<=) |
- とまあこんな風に計算されるおかげで、ちゃんと期待通り条件が不成立の時に分岐するようになります。
- それ以外の条件式の時は、OpJne-notがOpJeqになるので、式の値が0のときだけ分岐するようになり、これも期待通りになります。
次回に続く
こめんと欄