「10日くらいでできる!プログラミング言語自作入門」の続編#1-1

(1) はじめに


(2) HL-11

#include <acl.c>

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;

AInt var[MAX_TC + 1];	// 変数.

int getTc(String s, int len) → HL-8aと同じなので省略

///////////////////////////////////////////////////////////////////////////////

int isAlphabetOrNumber(unsigned char c) → HL-2と同じなので省略

int lexer(String s, int tc[]) → HL-9aと同じなので省略

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と同じなので省略

/////////////////////////////////////////////////////////////////////////////// → ここまではHL-9aと全く同じ、ここから改造が始まる.

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 };

unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]への書き込み用ポインタ.

void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3)  // 移行中の間だけ、以下の形で残しておく.
{
    printf("putIc: error\n");
    exit(1);
}

int getHex(int c) // 16進数に使える文字ならそれを0~15の数に変換、そうでなければ-1を返す関数.
{
    if ('0' <= c && c <= '9') return c - '0';
    if ('a' <= c && c <= 'f') return c - 'a' + 10;
    if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return -1;
}

int get32(unsigned char *p)
{
    return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 16777216;
}

void put32(unsigned char *p, int i)
{
    p[0] =  i        & 0xff; // 1バイト目に、ビット0~7の内容を書き込む.
    p[1] = (i >>  8) & 0xff; // 2バイト目に、ビット8~15の内容を書き込む.
    p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~23の内容を書き込む.
    p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~31の内容を書き込む.
}

void putIcX86_sub(String s, IntP a[])
{
    int i, j, k;
    for (i = 0; s[i] != 0; ) {
        if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' || s[i] == ';') {
            i++;
        } else if (getHex(s[i]) >= 0 && getHex(s[i + 1]) >= 0) { // 16進数2桁.
            *icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
            i += 2;
            icq++;
        } else if (s[i] == '%') {
            j = s[i + 1] - '0';
            if (s[i + 2] == 'm') {	// mod r/m.
                k = s[i + 3] - '0';
                *icq = 0x05 + k * 8;
                put32(icq + 1, (int) a[j]);
                icq += 5;
                i += 4;
                continue;
            }
            if (s[i + 2] == 'i') {	// int.
                put32(icq, (int) a[j]);
                icq += 4;
            }
            if (s[i + 2] == 'c') {	// char.
                *icq = (int) a[j];
                icq++;
            }
            if (s[i + 2] == 'r') {	// relative.
                put32(icq, (int) a[j] - (int) (icq + 4));
                icq += 4;
            }
            i += 3;
        } else {
            printf("putIcX86: error: '%s'\n", s);
            exit(1);
        }
    }
}

void putIcX86(String s, IntP p0, IntP p1, IntP p2, IntP p3)  // ic[]へ簡単に書き込むための便利関数.
{
    IntP a[4];
    a[0] = p0;
    a[1] = p1;
    a[2] = p2;
    a[3] = p3;
    putIcX86_sub(s, a);
}

///////////////////////////////////////////////////////////////////////////////

char tmp_flag[10]; // 一時変数の利用状況を管理.

int tmpAlloc() → HL-7と同じなので省略

void tmpFree(int i) → HL-7と同じなので省略

///////////////////////////////////////////////////////////////////////////////

void sub_print(int i)
{
    printf("%d\n", i);
}

///////////////////////////////////////////////////////////////////////////////

int epc, epc1;	// exprのためのpcとpc1.

int exprSub(int priority);	// exprSub1()が参照するので、プロトタイプ宣言.
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err);

int exprSub1(int i, int priority, int op) → HL-7と同じなので省略

int exprSub(int priority) → HL-9と同じなので省略

int expr(int j) → HL-7と同じなので省略

///////////////////////////////////////////////////////////////////////////////

enum { IfTrue = 0, IfFalse = 1 };

void ifgoto(int i, int not, int label) → HL-8と同じなので省略

int tmpLabelNo;

int tmpLabelAlloc() → HL-8と同じなので省略

#define BInfSiz		10

int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd:block-depth, lbd:loop-block-depth

enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで追加.
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01, ForWpc11, ForWpc02, ForWpc12 };

int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err)  // 移行中の間だけ、以下の形で残しておく.
{
    if (phrCmp(pid, phr, pc)) {
        printf("phrCmpPutIc: error\n");
        exit(1);
    }
    return 0;
}

int phrCmpPutIcX86(int pid, String phr, int pc, int *pi, int lenExpr, void *sub, int *err)
{
    int e[9], i;
    if (phrCmp(pid, phr, pc)) {
        e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] = e[7] = e[8] = 0;
        for (i = 0; i < lenExpr; i++) {
            e[i] = expr(i);
        }
        for (i = 0; i < lenExpr; i++) {
	     putIcX86("8b_%0m0; 89_44_24_%1c;", &var[e[i]], (IntP) (i * 4), 0, 0);
        }
        putIcX86("e8_%0r", sub, 0, 0, 0);
        for (i = 0; i < lenExpr; i++) {
            if (e[i] < 0) {
                *err = -1;
            }
            tmpFree(e[i]);
        }
        if (pi != 0) {
            *pi = tmpAlloc();
            putIcX86("89_%0m0;", &var[*pi], 0, 0, 0);
        }
        return 1;
    }
    return 0;
}

///////////////////////////////////////////////////////////////////////////////

int compile(String s)
{
    int pc, pc1, i, j;
!   unsigned char *icq1, *icp;
    pc1 = lexer(s, tc);
    tc[pc1++] = TcSemi;	// 末尾に「;」を付け忘れることが多いので、付けてあげる.
    tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = TcDot;	// エラー表示用のために末尾にピリオドを登録しておく.
    icq = ic;
+   putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); SUB(ESP,124);
    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)) { // 単純代入.
!           putIcX86("8b_%1m0;  89_%0m0;", &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]]) {
        (中略)
!       } else if (phrCmpPutIcX86(4, "print !!**0;", pc, 0, 1, sub_print, &e0)) {
        (中略)
    }
    if (bd > 0) {
        printf("block nesting error (bd=%d, lbd=%d, pc=%d, pc1=%d\n", bd, lbd, pc, pc1);
        return -1;
    }
!   putIcX86("83_c4_7c; 61; c3;", 0, 0, 0, 0); // ADD(ESP,124); POPAD(); RET();
    icq1 = icq;
    // ここにあった「goto先の設定」はいったん全部削除.
    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;
}

// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.

int run(String s)
{
    if (compile(s) < 0)
        return 1;
+   void (*func)() = (void (*)()) ic;
!   func();
    return 0;
}

// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合は本文中で説明します.

#include <windows.h>

void *mallocRWX(int siz)
{
    return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}

///////////////////////////////////////////////////////////////////////////////

void aMain()
{
    unsigned char txt[10000];
    int i;
+   ic = mallocRWX(1024 * 1024); // 1MB
    lexer(tcInit, tc);
    (中略)
}

(3) HL-11に関する重要な説明

(4) 今回の改造のあらまし

(5) 生成している機械語の説明(putIcX86()やphrCmpPutIcX86()の説明も含む)





こめんと欄


コメントお名前NameLink

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS