* 川合のプログラミング言語自作のためのテキスト第三版#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


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