川合のプログラミング言語自作のためのテキスト第三版#6

(13) TL-6

ページ名名前行数.exeの大きさ説明速度のめやす
a21_txt01TL-152行6.00KB初めの一歩、たった52行のシンプルすぎる言語処理系計測不能
a21_txt01_2TL-2125行6.50KB変数名は1文字じゃなくてもOK、見やすいスペースやインデントもOKに計測不能
a21_txt01_3TL-3143行7.00KB条件分岐などをサポートしてループ処理が可能に1150倍
a21_txt01_4TL-4181行7.50KBREPLの導入(これは楽しい!)1150倍
a21_txt01_5TL-5203行7.50KB少し高速化192倍
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef unsigned char *String;	// こう書くと String は unsigned char * の代用になる.

int loadText(String path, String t, int siz) → TL-4と同じなので省略

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

#define MAX_TC  255 // トークンコードの最大値.
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 getTc(String s, int len) → TL-4と同じなので省略

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

int phrCmp_tc[100 * 20], phrCmp_wildCard = -1;

int phrCmp(int pid, String phr, int tc[], int pc) → TL-5と同じなので省略

int pc, pc1, var[MAX_TC + 1], tc[1000];	// 変数(var)とトークンコード(tc).

typedef void *PtrTyp; // こう書くと PtrTyp は void * の代わりに使えるようになる.
typedef int *IntP; // こう書くと IntP は int * の代わりに使えるようになる.

enum { OpCpy = 0, OpAdd, OpSub, OpPrint, OpGoto, OpJeq, OpJne, OpTime, OpEnd, OpAdd1 };

PtrTyp ic[1000]; // 内部コード.

int compile(String s)
{
    int i, tcs0 = tcs, semi = getTc(";", 1);
    PtrTyp *icp = ic, *icp1;
    pc1 = lexer(s, tc);
    tc[pc1++] = semi;	// 末尾に「;」を付け忘れることが多いので、付けてあげる.
    tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = getTc(" ", 1);	// エラー表示用のために末尾にスペースを登録しておく.
    for (i = tcs0; i < tcs; i++) { // 定数の初期値を代入.
        var[i] = strtol(ts[i], 0, 0);	// 定数だった場合に初期値を設定(定数ではないときは0になる).
    }
    for (pc = 0; pc < pc1; pc++) { // コンパイル開始.
        if (phrCmp(  1, "=", tc, pc + 1)) { // 2単語目が"=".
            if (phrCmp(  2, ";", tc, pc + 3)) { // 単純代入.
                icp[0] = (PtrTyp) OpCpy;
                icp[1] = &var[tc[pc]];
                icp[2] = &var[tc[pc + 2]];
                icp += 5;
            } else if (phrCmp( 12, "!!* = !!* + 1 ;", tc, pc) && tc[pc] == tc[pc + 2]) {  // 高速化のために+1専用の命令を用意(せこくてすみません).
                icp[0] = (PtrTyp) OpAdd1;
                icp[1] = &var[tc[pc]];
                icp += 5;
            } else if (phrCmp(  3, "+ !!* ;", tc, pc + 3)) {  // 加算.
                icp[0] = (PtrTyp) OpAdd;
                icp[1] = &var[tc[pc]];
                icp[2] = &var[tc[pc + 2]];
                icp[3] = &var[tc[pc + 4]];
                icp += 5;
            } else if (phrCmp(  4, "- !!* ;", tc, pc + 3)) {  // 減算.
                icp[0] = (PtrTyp) OpSub;
                icp[1] = &var[tc[pc]];
                icp[2] = &var[tc[pc + 2]];
                icp[3] = &var[tc[pc + 4]];
                icp += 5;
            } else
                goto err;
        } else if (phrCmp(  5, "print !!* ;", tc, pc)) { // print.
            icp[0] = (PtrTyp) OpPrint;
            icp[1] = &var[tc[pc + 1]];
            icp += 5;
        } else if (phrCmp(  6, ":", tc, pc + 1)) {	// ラベル定義命令.
            var[tc[pc]] = icp - ic;	// ラベルに対応するicpを記録しておく.
            pc++; // 1単語だけ読み飛ばす(for文がもう1単語を読み飛ばしてくれる).
            continue;
        } else if (phrCmp(  7, "goto !!* ;", tc, pc)) { // goto.
            icp[0] = (PtrTyp) OpGoto;
            icp[1] = &var[tc[pc + 1]];
            icp += 5;
        } else if (phrCmp(  8, "if ( !!* !!* !!* ) goto !!* ;", tc, pc)) {	// if (...) goto.
            icp[0] = (PtrTyp) -1;
            icp[1] = &var[tc[pc + 7]];
            icp[2] = &var[tc[pc + 2]];
            icp[3] = &var[tc[pc + 4]];
            if (phrCmp(  9, "!=", tc, pc + 3)) { icp[0] = (PtrTyp) OpJne; }
            if (phrCmp( 10, "==", tc, pc + 3)) { icp[0] = (PtrTyp) OpJeq; }
            if ((int)icp[0] < 0) goto err;
            icp += 5;
        } else if (phrCmp( 11, "time ;", tc, pc)) {
            icp[0] = (PtrTyp) OpTime;
            icp += 5;
	 } else if (tc[pc] == semi) {
	     // 何もしない.
        } else
            goto err;
        while (tc[pc] != semi)
            pc++;
    }
    icp[0] = (PtrTyp) OpEnd;
    icp++;
    icp1 = icp;
    for (icp = ic; icp < icp1; icp += 5) {  // goto先の設定.
        i = (int) icp[0];
        if (i == OpGoto || i == OpJne || i == OpJeq) {
            icp[1] = &ic[*(IntP)icp[1]];
        }
    }
    return icp1 - 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()
{
    clock_t t0 = clock();
    PtrTyp *icp = ic;
    for (;;) {
        switch ((int) icp[0]) {
        case OpCpy:
            *(IntP)icp[1] = *(IntP)icp[2];
            icp += 5;
            continue;
        case OpAdd:
            *(IntP)icp[1] = *(IntP)icp[2] + *(IntP)icp[3];
            icp += 5;
            continue;
        case OpSub:
            *(IntP)icp[1] = *(IntP)icp[2] - *(IntP)icp[3];
            icp += 5;
            continue;
        case OpPrint:
            printf("%d\n", *(IntP)icp[1]);
            icp += 5;
            continue;
        case OpGoto:
            icp = icp[1];
            continue;
        case OpJeq:
            if (*(IntP)icp[2] == *(IntP)icp[3]) {
                icp = icp[1];
                continue;
            }
            icp += 5;
            continue;
        case OpJne:
            if (*(IntP)icp[2] != *(IntP)icp[3]) {
                icp = icp[1];
                continue;
            }
            icp += 5;
            continue;
        case OpTime:
            printf("time: %.3f[sec]\n", (clock() - t0) / (double) CLOCKS_PER_SEC);
            icp += 5;
            continue;
        case OpEnd:
            return;
        case OpAdd1:
            (*(IntP)icp[1])++;
            icp += 5;
            continue;
        }
    }
}

int run(String s)
{
    if (compile(s) < 0)
        return 1;
    exec();
    return 0;
}

int main(int argc, const char **argv) → TL-4と同じなので省略

(14) TL-6の簡単な説明


次回に続く

こめんと欄


コメントお名前NameLink

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