川合のプログラミング言語自作のためのテキスト第三版#7
(15) TL-7
- まずC言語の演算子一覧を書きます。
優先順位 | 演算子 | 形式 | 名前 | 結合方向 | TL-7 |
1 | ( ) | func(x,y,z) | 関数呼び出し演算子 | 左 | × |
1 | [ ] | a[i] | 添え字演算子 | 左 | × |
1 | . | abc.x | ドット演算子 | 左 | × |
1 | -> | p->x | アロー演算子 | 左 | × |
1 | ++ | i++ | 後置インクリメント演算子 | 左 | 〇 |
1 | -- | j-- | 後置デクリメント演算子 | 左 | × |
2 | ++ | ++i | 前置インクリメント演算子 | 右 | 〇 |
2 | -- | --j | 後置デクリメント演算子 | 右 | × |
2 | sizeof | sizeof a | sizeof演算子 | 右 | × |
2 | & | &x | 単項&演算子 | 右 | × |
2 | * | *p | 単項*演算子 | 右 | × |
2 | + | +a | 単項+演算子 | 右 | × |
2 | - | -b | 単項-演算子 | 右 | 〇 |
2 | ~ | ~i | 補数演算子 | 右 | × |
2 | ! | !j | 論理否定演算子 | 右 | × |
3 | ( ) | (typ)obj | 型キャスト演算子 | 右 | × |
4 | * | x * y | 二項*演算子 | 左 | 〇 |
4 | / | x / y | 除算演算子 | 左 | × |
4 | % | x % y | 剰余演算子 | 左 | × |
5 | + | x + y | 二項+演算子 | 左 | 〇 |
5 | - | x - y | 二項-演算子 | 左 | 〇 |
6 | << >> | i << j など | シフト演算子 | 左 | × |
7 | < <= > >= | x < y など | 比較演算子 | 左 | × |
8 | == != | x == y など | 比較演算子 | 左 | 〇 |
9 | & | i & j | ビットAND演算子 | 左 | × |
10 | ^ | i ^ j | ビットXOR演算子 | 左 | × |
11 | | | i | j | ビットOR演算子 | 左 | × |
12 | && | i && j | 論理AND演算子 | 左 | × |
13 | || | i || j | 論理OR演算子 | 左 | × |
14 | ? : | x ? y : z | 条件演算子 | 右 | × |
15 | = | x = y | 単純代入演算子 | 右 | 〇 |
15 | += -= など | x += y など | 複合代入演算子 | 右 | × |
16 | , | x, y | コンマ演算子 | 左 | × |
- 全部の演算子をサポートするとTL-7のプログラムが長くなってしまうので、この中の一部だけを実装することにします、残りは拡張したい人が拡張するということにします。
#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と同じなので省略
enum { TcSemi = 0, TcBrOpn, TcBrCls, TcPlus, TcMinus, TcPlPlus, TcMulti, TcEqu, TcEEq, TcNEq, TcComma, TcWiCard, TcExpr, TcExpr0,
TcTmp0, TcTmp1, TcTmp2, TcTmp3, TcTmp4, TcTmp5, TcTmp6, TcTmp7, TcTmp8, TcTmp9, TcZero };
int phrCmp_tc[100 * 20], phrCmp_exprPc0[9], phrCmp_exprPc1[9], phrCmp_pc1;
int phrCmp(int pid, String phr, int tc[], int pc) // TL-5のとは違う(改造).
{
int i, i1, j, k; // j, kを追加.
if (phrCmp_tc[pid * 20 + 19] == 0) {
i1 = lexer(phr, &phrCmp_tc[pid * 20]);
phrCmp_tc[pid * 20 + 19] = i1 + 1;
}
i1 = phrCmp_tc[pid * 20 + 19] - 1;
for (i = 0; i < i1; i++) {
if (phrCmp_tc[pid * 20 + i] == TcWiCard) { // 任意の1トークンに一致.
pc++;
continue;
}
if (phrCmp_tc[pid * 20 + i] == TcExpr || phrCmp_tc[pid * 20 + i] == TcExpr0) { // 任意の式に一致.
i++;
j = strtol(ts[phrCmp_tc[pid * 20 + i]], 0, 0); // 後続の番号を取得(式番号).
phrCmp_exprPc0[j] = pc;
k = 0; // かっこの対応関係を数える.
for (;;) {
if (tc[pc] == TcSemi) break;
if (tc[pc] == TcComma && k == 0) break;
if (tc[pc] == TcBrOpn) k++;
if (tc[pc] == TcBrCls) k--;
if (k < 0) break;
pc++;
}
phrCmp_exprPc1[j] = pc;
if (phrCmp_tc[pid * 20 + i] == TcExpr && phrCmp_exprPc0[j] == pc) return 0; // "!!**"では、長さ0は不一致とする.
if (k > 0) return 0;
continue;
}
if (phrCmp_tc[pid * 20 + i] != tc[pc]) return 0; // マッチせず.
pc++; // マッチしたので次へ.
}
phrCmp_pc1 = pc;
return 1; // すべてマッチした.
}
int 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, OpLop, OpNeg, OpMul, OpCeq, OpCne };
PtrTyp ic[1000], *icq; // 内部コード.
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() // 未使用の一時変数を確保.
{
int i;
for (i = 0; i < 10; i++) {
if (tmp_flag[i] == 0) break;
}
if (i >= 10) return -1;
tmp_flag[i] = 1;
return i + TcTmp0;
}
void tmpFree(int i) // 一時変数を未使用に戻す. ただし、指定されたトークンコードが一時変数でないときは何もしない.
{
if (TcTmp0 <= i && i <= TcTmp9) {
tmp_flag[i - TcTmp0] = 0;
}
}
int epc, epc1; // exprのためのpcとpc1.
void putIc3(int op, PtrTyp p0, PtrTyp p1, PtrTyp p2) → TL-6と同じなので省略
int exprSub(int priority); // exprSub1()が参照するので、プロトタイプ宣言.
int exprSub1(int i, int priority, int op) // 二項演算子の処理の標準形.
{
int j, k;
epc++;
j = exprSub(priority);
k = tmpAlloc();
putIc3(op, &var[k], &var[i], &var[j]);
tmpFree(i);
tmpFree(j);
return k;
}
int exprSub(int priority)
{
int i = -1, j;
if (tc[epc] == TcBrOpn) { // かっこ.
epc++;
i = exprSub(99);
if (tc[epc] != TcBrCls) return -1;
epc++;
} else if (tc[epc] == TcPlPlus) { // 前置インクリメント.
epc++;
i = exprSub(2);
putIc3(OpAdd1, &var[i], 0, 0);
} else if (tc[epc] == TcMinus) { // 単項マイナス.
epc++;
j = exprSub(2);
i = tmpAlloc();
putIc3(OpNeg, &var[i], &var[j], 0);
tmpFree(j);
} else if (tc[epc] >= TcExpr) { // 変数もしくは定数.
i = tc[epc];
epc++;
}
for (;;) {
if (i < 0) return -1; // ここまででエラーがあれば、処理を打ち切り.
if (epc >= epc1) break;
if (tc[epc] == TcPlPlus) { // 後置インクリメント.
epc++;
j = i;
i = tmpAlloc();
putIc3(OpCpy, &var[i], &var[j], 0);
putIc3(OpAdd1, &var[j], 0, 0);
tmpFree(j);
} else if (tc[epc] == TcMulti && priority >= 4) {
i = exprSub1(i, 3, OpMul); // 左結合なので4より1小さくする.
} else if (tc[epc] == TcPlus && priority >= 5) {
i = exprSub1(i, 4, OpAdd); // 左結合なので5より1小さくする.
} else if (tc[epc] == TcMinus && priority >= 5) {
i = exprSub1(i, 4, OpSub); // 左結合なので5より1小さくする.
} else if (tc[epc] == TcEEq && priority >= 8) {
i = exprSub1(i, 7, OpCeq); // 左結合なので8より1小さくする.
} else if (tc[epc] == TcNEq && priority >= 8) {
i = exprSub1(i, 7, OpCne); // 左結合なので8より1小さくする.
} else if (tc[epc] == TcEqu && priority >= 15) {
epc++;
j = exprSub(15); // 右結合なので15のまま.
putIc3(OpCpy, &var[i], &var[j], 0);
tmpFree(j);
} else
break;
}
return i;
}
int expr(int j)
{
int i;
if (phrCmp_exprPc0[j] == phrCmp_exprPc1[j]) return TcSemi; // エラーではない.
epc = phrCmp_exprPc0[j];
epc1 = phrCmp_exprPc1[j];
i = exprSub(99);
if (epc < epc1) return -1; // 途中で止まってしまったらエラー.
return i;
}
int compile(String s)
{
int pc, pc1, i, tcs0 = tcs;
PtrTyp *icq1;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが多いので、付けてあげる.
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になる).
}
icq = ic;
for (pc = 0; pc < pc1; pc++) { // コンパイル開始.
if (phrCmp( 13, "!!* = !!* + 1 ; if ( !!* != !!* ) goto !!* ;", tc, pc) && tc[pc] == tc[pc + 2] && tc[pc] == tc[pc + 8]) {
putIc3(OpLop, &var[tc[pc + 13]], &var[tc[pc]], &var[tc[pc + 10]]);
pc += 13;
continue;
}
if (tc[pc + 1] == TcEqu) {
if (tc[pc + 3] == TcSemi) { // 単純代入.
putIc3(OpCpy, &var[tc[pc]], &var[tc[pc + 2]], 0);
} else if (phrCmp( 12, "!!* = !!* + 1 ;", tc, pc) && tc[pc] == tc[pc + 2]) { // 高速化のために+1専用の命令を用意(せこくてすみません).
putIc3(OpAdd1, &var[tc[pc]], 0, 0);
} else if (phrCmp( 3, "+ !!* ;", tc, pc + 3)) { // 加算.
putIc3(OpAdd, &var[tc[pc]], &var[tc[pc + 2]], &var[tc[pc + 4]]);
} else if (phrCmp( 4, "- !!* ;", tc, pc + 3)) { // 減算.
putIc3(OpSub, &var[tc[pc]], &var[tc[pc + 2]], &var[tc[pc + 4]]);
} else if (phrCmp( 14, "!!**0 ;", tc, pc)) {
i = expr(0);
if (i < 0) goto err;
tmpFree(i);
} else
goto err;
} else if (phrCmp( 5, "print !!**0 ;", tc, pc)) { // print.
i = expr(0);
if (i < 0) goto err;
putIc3(OpPrint, &var[i], 0, 0);
tmpFree(i);
} else if (phrCmp( 6, ":", tc, pc + 1)) { // ラベル定義命令.
var[tc[pc]] = icq - ic; // ラベルに対応するicqを記録しておく.
pc++; // 1単語だけ読み飛ばす(for文がもう1単語を読み飛ばしてくれる).
continue;
} else if (phrCmp( 7, "goto !!* ;", tc, pc)) { // goto.
putIc3(OpGoto, &var[tc[pc + 1]], 0, 0);
} else if (phrCmp( 8, "if ( !!* !!* !!* ) goto !!* ;", tc, pc)) { // if (...) goto.
i = -1;
if (tc[pc + 3] == TcNEq) { i = OpJne; }
if (tc[pc + 3] == TcEEq) { i = OpJeq; }
if (i < 0) goto err;
putIc3(i, &var[tc[pc + 7]], &var[tc[pc + 2]], &var[tc[pc + 4]]);
} else if (phrCmp( 11, "time ;", tc, pc)) {
putIc3(OpTime, 0, 0, 0);
} else if (phrCmp( 15, "if ( !!**0 ) goto !!* ;", tc, pc)) { // if (...) goto.
i = expr(0);
if (i < 0) goto err;
putIc3(OpJne, &var[tc[phrCmp_exprPc1[0] + 2]], &var[i], &var[TcZero]);
tmpFree(i);
} else if (tc[pc] == TcSemi) {
// 何もしない.
} else if (phrCmp( 14, "!!**0 ;", tc, pc)) { // これはなんでもマッチするので最後にする.
i = expr(0);
if (i < 0) goto err;
tmpFree(i);
} else
goto err;
while (tc[pc] != TcSemi)
pc++;
}
putIc3(OpEnd, 0, 0, 0);
icq1 = icq;
for (icq = ic; icq < icq1; icq += 5) { // goto先の設定.
i = (int) icq[0];
if (i == OpGoto || i == OpJne || i == OpJeq || i == OpLop) {
icq[1] = &ic[*(IntP)icq[1]];
}
}
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;
}
void exec()
{
clock_t t0 = clock();
int i;
PtrTyp *icp = ic;
for (;;) {
switch ((int) icp[0]) {
case OpCpy:
(以下、OpLopまではTL-6aと同じなので省略)
+ case OpNeg:
+ *(IntP)icp[1] = - *(IntP)icp[2];
+ icp += 5;
+ continue;
+ case OpMul:
+ *(IntP)icp[1] = *(IntP)icp[2] * *(IntP)icp[3];
+ icp += 5;
+ continue;
+ case OpCeq:
+ *(IntP)icp[1] = *(IntP)icp[2] == *(IntP)icp[3];
+ icp += 5;
+ continue;
+ case OpCne:
+ *(IntP)icp[1] = *(IntP)icp[2] != *(IntP)icp[3];
+ icp += 5;
+ continue;
}
}
}
int run(String s) → TL-6と同じなので省略
int main(int argc, const char **argv)
{
unsigned char txt[10000];
int i;
+ lexer("; ( ) + - ++ * = == != , !!* !!** !!*** _t0 _t1 _t2 _t3 _t4 _t5 _t6 _t7 _t8 _t9 0", tc); // tc[]に入った結果は使わずに捨てる.
if (argc >= 2) {
(以下、TL-4と同じなので省略)
}
- トータルの行数は455行になっています。かなり増えました。しかしかっこよさもかなり増しています。
- 次に、連続代入ができるようになりました。「x = y = z = 0;」とかそういうやつです。
- TL-6aまでは、なんかこう「おもちゃ言語」の感じがしていた気がするのですが、一気にまともになった気がします!
(16) TL-7の簡単な説明
次回に続く
こめんと欄