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

(13) TL-6

ページ名名前行数.exeの大きさ説明速度のめやす
a21_txt01TL-152行6.00KB初めの一歩、たった52行のシンプルすぎる言語処理系計測不能
a21_txt01_2TL-2123行7.00KB変数名は1文字じゃなくてもOK、見やすいスペースやインデントもOKに計測不能
a21_txt01_3TL-3141行7.00KB条件分岐などをサポートしてループ処理が可能に320倍
a21_txt01_4TL-4180行7.50KBREPLの導入(これは楽しい!)320倍
a21_txt01_5TL-5208行8.00KB少し高速化203倍
#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と同じなので省略

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

#define MAX_TC  255 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
unsigned char tcBuf[10000];
int tcs = 0, tcb = 0;

int getTc(String s) → TL-4と同じなので省略

int phrCmp_tc[100 * 20];
unsigned char phrCmp_buf[100 * 100];

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

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

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)
{
    unsigned char buf[10000];
    int i, tcs0 = tcs, semi = getTc(";");
    PtrTyp *icp = ic, *icp1;
    strcpy(s + strlen(s), ";");	// 末尾に「;」を付け忘れることが多いので、付けてあげる.
    pc1 = lexer(s, buf, t);
    t[pc1] = t[pc1 + 1] = t[pc1 + 2] = t[pc1 + 3] = "";	// エラー表示用のために末尾にいくつか長さ0の文字列を登録しておく.
    for (pc = 0; pc < pc1; pc++) { // プログラム中で使われているすべてのトークンを調べて、tc[]の初期化.
        tc[pc] = getTc(t[pc]);
    }
    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", t[pc], t[pc + 1], t[pc + 2], t[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