* 「10日くらいでできる!プログラミング言語自作入門」の続編#2-1 (HL-30)
-(by [[K]], 2022.06.17)
** (1) はじめに
-このテキストは、「10日くらいでできる!プログラミング言語自作入門」([[a21_txt01]])の続編にあたります。ですからこの続編テキストのスタート地点は772行の[[HL-9a>a21_txt01_9a]]になります。
-このシリーズでは、言語の実行速度の改善はほとんどしないで、主に言語に新規の命令を追加していきます。
-[[a21_txt02]]のほうの続編で、「はりぼて言語」はJITコンパイラになり見違えるほど速くなったのですが、ここではそこから先を作るのではなく一度HL-9aに戻って、そこからの改造を試みます。・・・なぜそうするのかですが、x86用とx64用に分かれたまま話を続けるのは大変ですし、ARMなどの対応もできていないままなので、ここは一度HL-9aに戻ったほうが有用かなと思ったのです。
-そして、これはごめんなさい案件なのですが、最近ちょっと忙しくて丁寧に書けないので、説明はかなり割愛して書きます。でもここまでをやってきた人なら、何をやっているのかはきっとわかると思います。
** (2) 今回の改造テーマ
-HL-9aでは 10 / 3 の答えは 3 でした。
>print 10/3
3
-まあC言語でも整数の割り算では答えは整数になるのが正しいので、これはどこもおかしくはないのですが、しかしやっぱり、 3.3333 みたいな計算もできたらなあなんて思います。C言語でも 10.0 / 3.0 なら答えは 3.3333 になるのです。
-ということで、今回は小数を扱うための準備をして、次の回で計算をできるようにしたいと思います。
** (3-1) lexerの改造(小数の定数を途中で切らないようにする)
-before:
} else if (isAlphabetOrNumber(s[i])) { // 1文字目が英数字.
while (isAlphabetOrNumber(s[i + len]))
len++;
} else if (strchr("=+-*/!%&~|<>?:.#", s[i]) != 0) { // 1文字目が普通の記号.
-after:
} else if ('0' <= s[i] && s[i] <= '9') { // 1文字目が数字.
while (isAlphabetOrNumber(s[i + len]) || s[i + len] == '.')
len++;
} else if (isAlphabetOrNumber(s[i])) { // 1文字目が英字.
while (isAlphabetOrNumber(s[i + len]))
len++;
} else if (strchr("=+-*/!%&~|<>?:.#", s[i]) != 0) { // 1文字目が普通の記号.
** (3-2) 変数の型を増やす
-before:
AInt var[MAX_TC + 1]; // 変数.
-after:
enum { TypInt, TypDbl };
AInt varTyp[MAX_TC + 1]; // そのトークンは、int型か、double型か?
AInt var[MAX_TC + 1]; // int変数.
double varDbl[MAX_TC + 1]; // double変数.
** (3-3) 定数値のセット処理の拡張
-before:
var[i] = strtol(ts[i], 0, 0); // 定数だった場合に初期値を設定(定数ではないときは0になる).
if (ts[i][0] == 34) { // 先頭がダブルクォーテーション
char *p = malloc(len - 1);
var[i] = (AInt) p;
memcpy(p, ts[i] + 1, len - 2); // 手抜き実装.
p[len - 2] = 0;
}
-after:
var[i] = strtol(ts[i], 0, 0); // 定数だった場合に初期値を設定(定数ではないときは0になる).
varTyp[i] = TypInt;
if (ts[i][0] == 34) { // 先頭がダブルクォーテーション
char *p = malloc(len - 1);
var[i] = (AInt) p;
memcpy(p, ts[i] + 1, len - 2); // 手抜き実装.
p[len - 2] = 0;
} else if (strchr(ts[i], '.') != 0 && '0' <= ts[i][0] && ts[i][0] <= '9') { // 小数点を含んでいて、先頭が数字.
varTyp[i] = TypDbl;
varDbl[i] = strtod(ts[i], 0);
}
** (3-4) OpPrintd と DblP の宣言の追加
-before:
typedef AInt *IntP; // こう書くと IntP は AInt * の代わりに使えるようになる.
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 };
-after:
typedef AInt *IntP; // こう書くと IntP は AInt * の代わりに使えるようになる.
typedef double *DblP; // DblP は double * の代わりになる.
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 };
** (3-5) icpd の宣言と初期化
-before:
for (;;) {
switch ((int) icp[0]) {
-after:
for (;;) {
DblP *icpd = (DblP *) icp;
switch ((int) icp[0]) {
** (3-6) OpPrintd 命令の追加
-OpBitBltの処理の後に以下を追加:
case OpPrintd:
printf("%f\n", *icpd[1]);
icp += 5;
continue;
** (3-7) printfd 命令を追加
- } else if (phrCmp( 8, "!!***0;", pc)) { の前に以下の1行を追加:
} else if (phrCmpPutIc(35, "printd !!**0;", pc, 0, 1, OpPrintd, &e0)) {
** (3-8) varP() 関数の追加
- int exprSub1(int i, int priority, int op) の直前に以下の記述を追加:
IntP varP(AInt i) // トークンコード番号から対応する変数のポインタを求める.
IntP varP(int i) // トークンコード番号から対応する変数のポインタを求める.
{
if (i < 0) return 0; // NULL
if (varTyp[i] == TypInt) return &var[i];
if (varTyp[i] == TypDbl) return (IntP) &varDbl[i];
return 0;
}
** (3-9) phrCmpPutIc() 関数を書き換え
- &var[ ... ] という記述を varP( ... ) に置換します.
-before:
int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err)
{
int e[9], i, i0 = 0;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] = e[7] = e[8] = 0;
if (pi != 0) {
e[0] = *pi = tmpAlloc();
i0 = 1;
}
for (i = i0; i < lenExpr; i++) {
e[i] = expr(i);
}
putIc(op, &var[e[0]], &var[e[1]], &var[e[2]], &var[e[3]]);
if (lenExpr >= 5) {
putIc(OpPrm, &var[e[4]], &var[e[5]], &var[e[6]], &var[e[7]]);
}
for (i = i0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
return 1;
}
return 0;
}
-after:
int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err)
{
int e[9], i, i0 = 0;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] = e[7] = e[8] = 0;
if (pi != 0) {
e[0] = *pi = tmpAlloc();
i0 = 1;
}
for (i = i0; i < lenExpr; i++) {
e[i] = expr(i);
}
putIc(op, varP(e[0]), varP(e[1]), varP(e[2]), varP(e[3]));
if (lenExpr >= 5) {
putIc(OpPrm, varP(e[4]), varP(e[5]), varP(e[6]), varP(e[7]));
}
for (i = i0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
return 1;
}
return 0;
}
** (4) printd 命令のテスト
>printd 12.34
12.340000
** 次回に続く
次回: [[a22_txt03_1a]]
*こめんと欄
#comment