- 追加された行はこの色です。
- 削除された行はこの色です。
* 川合のプログラミング言語自作のためのテキスト第三版#7
-(by [[K]], 2021.02.04)
** (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,
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) { // 任意の式に一致.
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_exprPc0[j] == pc || k > 0) return 0; // "!!**"では、長さ0は不一致とする.
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; // ラベルに対応するicpを記録しておく.
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[]に入った結果は使わずに捨てる.
+ lexer("; ( ) + - ++ * = == != , !!* !!** !!*** _t0 _t1 _t2 _t3 _t4 _t5 _t6 _t7 _t8 _t9 0", tc); // tc[]に入った結果は使わずに捨てる.
if (argc >= 2) {
(以下、TL-4と同じなので省略)
}
-トータルの行数は455行になっています。かなり増えました。しかしかっこよさもかなり増しています。
-まず、print命令で、単に変数や定数を指定するだけではなく、式が書けるようになりました。
>print 1+2*3
7
>print (1+2)*3
9
-こんなふうに、ちゃんと演算子の優先順位も反映されます。
-次に、連続代入ができるようになりました。「x = y = z = 0;」とかそういうやつです。
-インクリメントもできます。
>a = 0; print ++a; print a
1
1
>a = 0; print a++; print a
0
1
-この違いがわかるでしょうか。C言語では、前置インクリメントと後置インクリメントは意味が違うのです。それもきちんと真似できています。
-TL-6aまでは、なんかこう「おもちゃ言語」の感じがしていた気がするのですが、一気にまともになった気がします!
** (16) TL-7の簡単な説明
-(準備中)
** 次回に続く
-次回: ''a21_txt01_8''
*こめんと欄
#comment