ES-BASIC #5
(7) ES-BASICのコア部分
- もしかしたらES-BASICの根幹部分だけを残して、ほかを全部削ったバージョンを作ってみたら、何か得るものがあるのではないかと思ってやってみました。
#include "kll0.h"
unsigned char *code = (unsigned char *) kmallocRWX(1024 * 1024); // 実行権限のついたメモリを確保.
int qc;
void sub_print(int i) { printf("%d\n", i); }
void sub_exit() { exit(0); }
void sub_list(KSrc *src)
{
for (int i = 0, i1 = src->ln.sp.sizPtr(); i < i1; i++) {
KSrcLine *sl = (KSrcLine *) src->ln.sp.getPtr(i);
printf("%4d %.*s\n", sl->ln, sl->src.s, sl->src.p);
}
}
int compile(int l, const char *s, int ln, KLexer *lx, KSrc *src, KSizPtr *varSp)
{
static KPhraseCmp *phr = NEW1(kautoreleasePool, KPhraseCmp)(32, 100, lx->sid); // フレーズ比較支援クラス.
KLexer_Elmt *le = (KLexer_Elmt *) lx->reset()->addCont(l, (const char *) s)->add(1, ";")->p;
for (int i = lx->sidN0, i1 = *lx->sid->n; i < i1; i++)
varSp->addInt(lx->getConstInt(i)); // intではないときは0を返す.
for (int pc = 0, pc1 = lx->saveSidN0()->getElmtLen(); pc < pc1; pc++) {
phr->setPr(le, 0, pc, pc1);
KLexer_Elmt *lp = &le[pc];
if (phr->cmpPr( 1, "@ ;")) continue;
if (phr->cmpPr( 2, "@ @* = @* ;")) { qc += kputBin(&code[qc], "8b_85_%i32 89_85_%i32", lp[2].i * 4, lp[0].i * 4);
} else if (phr->cmpPr( 3, "@ @* = @* + @* ;")) { qc += kputBin(&code[qc], "8b_85_%i32 03_85_%i32 89_85_%i32", lp[2].i * 4, lp[4].i * 4, lp[0].i * 4);
} else if (phr->cmpPr( 4, "@ @* = @* - @* ;")) { qc += kputBin(&code[qc], "8b_85_%i32 2b_85_%i32 89_85_%i32", lp[2].i * 4, lp[4].i * 4, lp[0].i * 4);
} else if (phr->cmpPr( 5, "@ PRINT @* ;")) { qc += kputBin(&code[qc], "8b_85_%i32 89_04_24 e8_%r32", lp[1].i * 4, sub_print);
} else if (phr->cmpPr( 6, "@ EXIT ;")) { qc += kputBin(&code[qc], "e8_%r32", sub_exit);
} else if (phr->cmpPr( 7, "@ LIST ;")) { qc += kputBin(&code[qc], "b8_%i32 89_04_24 e8_%r32", src, sub_list);
} else {
printf("syntax error [%d] : %.*s\n", ln, kminInt(kstrlenCutSpcLf(l, lp[0].p0), 30), lp[0].p0);
return 1;
}
while (le[pc].i != 0)
pc++;
}
return 0;
}
int main()
{
KSizPtr varSp(kmemPool); // 変数の値を記憶しておくところ(可変長領域).
KLexer lx(NEW1(kautoreleasePool, KStringId)(1, "; = + - PRINT EXIT LIST")); // 予約語を登録しておく.
KSrc src; // ソースコードを記憶しておくところ.
char *cmdlin = (char *) kautoreleasePool->alloc(65536), *p;
for (;;) {
ok:
printf("\nOk\n");
skip:
fgets(cmdlin, 65536, stdin);
for (p = cmdlin; *p == ' ' || *p == '\t'; p++);
if (*p == '\n') goto skip;
kupperCaseM(strlen(p), (unsigned char *) p, lx.chrTyp); // 変数名や予約語を大文字化する(BASICっぽくするための演出).
if ('0' <= *p && *p <= '9') {
src.editLine(strchr(p, '\n') - p, p); // 数字で始まる入力だった場合は、editLineに渡せば、あとは適当にやってくれるのでライブラリに丸投げ.
goto skip; // プログラム入力直後はOkを出さない.
}
qc = kputBin(&code[0], "60 83_ec_7c 0xbd_%i32", 0); // [JITコンパイル開始] PUSHAD. SUB ESP,124. MOV EBP,0. この命令でスタックを調整させる.
if (strncmp(p, "RUN", 3) == 0 && p[3] <= ' ') {
for (int i = 0, i1 = src.ln.sp.sizPtr(); i < i1; i++) {
KSrcLine *sl = (KSrcLine *) src.ln.sp.getPtr(i);
if (compile(sl->src.s, sl->src.p, sl->ln, &lx, &src, &varSp) != 0) goto ok;
}
} else {
if (compile(strlen(p), p, -1, &lx, &src, &varSp) != 0) goto ok;
}
qc += kputBin(&code[qc], "83_c4_7c 61 c3"); // ADD ESP,124. 最初に調整したスタックを元に戻す. POPAD. RET.
kputBin(&code[5], "%i32", varSp.p);
((void (*)()) code)(); // codeを関数呼び出し.
}
}
- これで74行。・・・まあ一部普段なら改行するところをあえて改行しないようにしている部分もあるので(そのほうが私には見やすい)、単純に行数だけでは計れないかもしれませんが。
- これだけでも、コマンドラインからプログラムを入力できるし、LISTもできるし、実行はJITコンパイラで高速にできます。
- あとは機能を足していけばいいだけなので、言語ってもうそんなに難しくないと思います。
- 全体としてすごくシンプルになっているのが伝わるでしょうか・・・
- text0021によれば、"kll0.h"はコードを1/3くらいにしてくれるので、逆算すればライブラリを使わなくても300行程度で書けるのかもしれないです。
こめんと欄
|