お待たせしました。さあついにJITコンパイラです。プログラミング言語の最前線です。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h> // 他のOSではここを書き換える.
void loadText(int argc, const char **argv, unsigned char *t, int siz) → TL-1と同じなので省略
int var[256]; // 変数.
unsigned char txt[10000]; // ソースコード.
int pc = 0; // プログラム・カウンター.
unsigned char *code; // 機械語出力先.
int qc = 0; // 出力用のプログラム・カウンター.
void put32(int i)
{
code[qc++] = i & 0xff;
code[qc++] = (i >> 8) & 0xff;
code[qc++] = (i >> 16) & 0xff;
code[qc++] = (i >> 24) & 0xff;
}
#define EAX 0
#define ECX 1
void getNumber(int reg) // 定数は2桁以上でもOK. レジスタに値をセットするための命令群を出力する.
{
unsigned char c = txt[pc], *p, *q;
if ('0' <= c && c <= '9') { // 数字.
p = &txt[pc];
code[qc++] = 0xb8 + reg; // MOV reg,const.
put32(strtol(p, (char **) &q, 0));
pc += q - p;
return;
}
pc++; // 1文字の変数.
code[qc++] = 0x8b; // MOV reg,[const].
code[qc++] = reg * 8 + 0x05;
put32((int) &var[c]);
}
void sub_print(int i)
{
printf("%d\n", i);
}
void sub_time()
{
printf("time=%.3f[sec]\n", clock() / (double) CLOCKS_PER_SEC);
}
int main(int argc, const char **argv)
{
int i, j, k, pc0, wqc = 0, wqc2 = 0;
void (*func)(); // funcは関数へのポインタ型の変数.
loadText(argc, argv, txt, 10000);
// Windowsに対して、実行権限のあるメモリを特別に要求.
code = VirtualAlloc(0, 1024 * 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
code[qc++] = 0x83; // SUB ESP,124.
code[qc++] = 0xec; // この命令でスタックを調整させる.
code[qc++] = 0x7c;
for (;;) {
pc0 = pc;
if (txt[pc] == 0) // ファイル終端.
break;
if (txt[pc] == '\n' || txt[pc] == ' ' || txt[pc] == '\t' || txt[pc] == ';') { // 空行など.
pc++;
} else if (txt[pc + 1] == '=') { // 2文字目が"=".
i = txt[pc]; // 1文字の変数名.
pc += 2;
getNumber(EAX);
if (txt[pc] == ';') {
} else if (txt[pc] == '+') { // 加算.
pc++;
getNumber(ECX);
code[qc++] = 0x01; // ADD EAX,ECX.
code[qc++] = 0xc8;
} else if (txt[pc] == '-') { // 減算.
pc++;
getNumber(ECX);
code[qc++] = 0x29; // SUB EAX,ECX.
code[qc++] = 0xc8;
} else
goto err;
code[qc++] = 0xa3; // MOV [const],EAX.
put32((int) &var[i]);
} else if (txt[pc] == 'p' && txt[pc + 1] == 'r' && txt[pc + 5] == ' ') { // 最初の2文字しか調べてない(手抜き).
pc += 6;
getNumber(EAX);
code[qc++] = 0x89; // MOV [ESP],EAX.
code[qc++] = 0x04;
code[qc++] = 0x24;
k = (unsigned char *) sub_print - &code[qc + 5];
code[qc++] = 0xe8; // CALL sub_print.
put32(k);
} else if (txt[pc] == 'w' && txt[pc + 1] == 'h' && txt[pc + 5] == ' ' && txt[pc + 7] == '<') { // 最初の2文字しか調べてない(手抜き).
wqc = qc;
pc += 6;
getNumber(EAX); // 1文字の変数名.
pc++;
getNumber(ECX);
if (txt[pc] != '{')
goto err;
pc++;
code[qc++] = 0x39; // CMP EAX,ECX.
code[qc++] = 0xc8;
code[qc++] = 0x0f; // JGE ????
code[qc++] = 0x8d;
wqc2 = qc;
put32(0); // ここは後で更新する.
} else if (txt[pc] == '}') {
pc++;
code[qc++] = 0xe9;
put32(wqc - (qc + 4));
k = qc - (wqc2 + 4);
code[wqc2 + 0] = k & 0xff;
code[wqc2 + 1] = (k >> 8) & 0xff;
code[wqc2 + 2] = (k >> 16) & 0xff;
code[wqc2 + 3] = (k >> 24) & 0xff;
} else if (txt[pc] == 't' && txt[pc + 1] == 'i') { // 最初の2文字しか調べてない(手抜き).
pc += 4;
k = (unsigned char *) sub_time - &code[qc + 5];
code[qc++] = 0xe8; // CALL sub_time.
put32(k);
} else
goto err;
}
code[qc++] = 0x83; // ADD ESP,124.
code[qc++] = 0xc4; // 最初に調整したスタックを元に戻す.
code[qc++] = 0x7c;
code[qc++] = 0xc3; // RET.
// for (i = 0; i < qc; i++) { printf("%02x ", code[i]); } exit(0); // デバッグ用.
func = (void *) code;
func();
exit(0);
err:
printf("syntax error : %.10s\n", &txt[pc0]);
exit(1);
}