a21_txt01_7
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* 川合のプログラミング言語自作のためのテキスト第三版#7
-(by [[K]], 2021.03.09)
** (1) HL-7
-まずC言語の演算子一覧を書きます。
|優先順位|演算子|形式|名前|結合方向|HL-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|コンマ演算子|左|×|
-全部の演算子をサポートするとHL-7のプログラムが長くなって...
-この取り合わせだと「この演算子があって、あの演算子がない...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
int var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-4と同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-2と同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) // HL-5のとは違...
{
int i0 = pid * 32, i, i1, j, k, t;
if (phrCmp_tc[i0 + 31] == 0) {
i1 = lexer(phr, &phrCmp_tc[i0]);
phrCmp_tc[i0 + 31] = i1;
}
i1 = phrCmp_tc[i0 + 31];
for (i = 0; i < i1; i++) {
t = phrCmp_tc[i0 + i];
if (t == TcWiCard || t == TcExpr || t == TcExpr0...
i++;
j = phrCmp_tc[i0 + i] - Tc0; // 後続の番号を...
wpc[j] = pc;
if (t == TcWiCard) {
pc++;
continue;
}
k = 0; // カッコの深さ.
for (;;) {
if (tc[pc] == TcSemi) break;
if (tc[pc] == TcComma && k == 0) break;
if (tc[pc] == TcBrOpn || tc[pc] == TcSqB...
if (tc[pc] == TcBrCls || tc[pc] == TcSqB...
if (k < 0) break;
pc++;
}
wpc1[j] = pc; // 式の終了位置を記憶.
if (t == TcExpr && wpc[j] == pc) return 0; /...
if (k > 0) return 0; // カッコの深さがおかし...
continue;
}
if (t != tc[pc]) return 0; // マッチせず.
pc++;
}
ppc1 = pc;
return 1; // マッチした.
}
////////////////////////////////////////////////////////...
typedef int *IntP; // こう書くと IntP は int * の代わり...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
IntP ic[10000], *icq; // ic[]:内部コード、icq:ic[]への書...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) →...
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() // 未使用の一時変数を確保.
{
int i;
for (i = 0; i < 10; i++) {
if (tmp_flag[i] == 0) break;
}
if (i >= 10) {
printf("tmpAlloc: error\n");
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.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int exprSub1(int i, int priority, int op) // 二項演算子...
{
int j, k;
epc++;
j = exprSub(priority);
k = tmpAlloc();
putIc(op, &var[k], &var[i], &var[j], 0);
tmpFree(i);
tmpFree(j);
if (i < 0 || j < 0) return -1;
return k;
}
int exprSub(int priority)
{
int i = -1, e0 = 0; // iはここまでの計算結果が入って...
ppc1 = 0;
if (phrCmp(99, "( !!**0 )", epc)) { // かっこ.
i = expr(0);
} else if (tc[epc] == TcPlPlus) { // 前置インクリメ...
epc++;
i = exprSub(2);
putIc(OpAdd1, &var[i], 0, 0, 0);
} else if (tc[epc] == TcMinus) { // 単項マイナス.
epc++;
e0 = exprSub(2);
i = tmpAlloc();
putIc(OpNeg, &var[i], &var[e0], 0, 0);
} else { // 変数もしくは定数.
i = tc[epc];
epc++;
}
if (ppc1 > 0)
epc = ppc1;
for (;;) {
tmpFree(e0);
if (i < 0 || e0 < 0) return -1; // ここまででエ...
if (epc >= epc1) break;
e0 = 0;
if (tc[epc] == TcPlPlus) { // 後置インクリメント.
epc++;
e0 = i;
i = tmpAlloc();
putIc(OpCpy, &var[i], &var[e0], 0, 0);
putIc(OpAdd1, &var[e0], 0, 0, 0);
} else if (TcAster <= tc[epc] && tc[epc] <= TcPe...
i = exprSub1(i, 3, tc[epc] - TcAster + OpMul...
} else if (TcPlus <= tc[epc] && tc[epc] <= TcMi...
i = exprSub1(i, 4, tc[epc] - TcPlus + OpAdd...
} else if (tc[epc] == TcShr && priority >= 6) {
i = exprSub1(i, 5, OpShr); // 左結合なので6...
} else if (TcLt <= tc[epc] && tc[epc] <= TcGt...
i = exprSub1(i, 6, tc[epc] - TcLt + OpClt...
} else if (TcEEq <= tc[epc] && tc[epc] <= TcNE...
i = exprSub1(i, 7, tc[epc] - TcEEq + OpCeq...
} else if (tc[epc] == TcAnd && priority >= 9) {
i = exprSub1(i, 8, OpAnd); // 左結合なので9...
} else if (tc[epc] == TcEqu && priority >= 15) {
epc++;
e0 = exprSub(15); // 右結合なので15のまま.
putIc(OpCpy, &var[i], &var[e0], 0, 0);
} else
break;
}
return i;
}
int expr(int j)
{
int i, k, old_epc = epc, old_epc1 = epc1, s[19]; // ...
if (wpc[j] == wpc1[j]) return 0;
for (k = 0; k < 9; k++) { // wpc[], wpc1[]を保存する.
s[k] = wpc [k];
s[k + 9] = wpc1[k];
}
s[18] = ppc1; // ppc1を保存する.
epc = wpc [j]; // epc, epc1を準備してexprSub()を呼...
epc1 = wpc1[j];
i = exprSub(99);
if (epc < epc1) return -1; // 式を最後まで解釈できな...
epc = old_epc; // 保存しておいた変数をすべて復元する.
epc1 = old_epc1;
for (k = 0; k < 9; k++) {
wpc [k] = s[k];
wpc1[k] = s[k + 9];
}
ppc1 = s[18];
return i;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i;
IntP *icq1;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
for (i = 0; i < 10; i++) { ・// 一時変数をすべて未使...
tmp_flag[i] = 0;
}
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
putIc(OpLop, &var[tc[wpc[4]]], &var[tc[wpc[0...
} else if (phrCmp( 9, "!!*0 = !!*1 + 1;", pc) &&...
putIc(OpAdd1, &var[tc[wpc[0]]], 0, 0, 0);
} else if (phrCmp( 2, "!!*0 = !!*1 + !!*2;", pc)...
putIc(OpAdd, &var[tc[wpc[0]]], &var[tc[wpc[...
} else if (phrCmp( 3, "!!*0 = !!*1 - !!*2;", pc)...
putIc(OpSub, &var[tc[wpc[0]]], &var[tc[wpc[...
! } else if (phrCmp( 4, "print !!**0;", pc)) { // ...
+ e0 = expr(0);
! putIc(OpPrint, &var[e0], 0, 0, 0);
} else if (phrCmp( 0, "!!*0:", pc)) { // ラベル...
var[tc[wpc[0]]] = icq - ic; // ラベルに対応...
} else if (phrCmp( 5, "goto !!*0;", pc)) { // go...
putIc(OpGoto, &var[tc[wpc[0]]], 0, 0, 0);
} else if (phrCmp( 6, "if (!!*0 !!*1 !!*2) goto ...
putIc(OpJeq + (tc[wpc[1]] - TcEEq), &var[tc[...
} else if (phrCmp( 7, "time;", pc)) {
putIc(OpTime, 0, 0, 0, 0);
! } else if (phrCmp( 8, "!!***0;", pc)) { // これ...
! e0 = expr(0);
} else
goto err;
tmpFree(e0);
if (e0 < 0) goto err;
pc = ppc1;
}
putIc(OpEnd, 0, 0, 0, 0);
icq1 = icq;
for (icq = ic; icq < icq1; icq += 5) { // goto先の...
i = (int) icq[0];
if (OpGoto <= i && i <= OpLop) {
icq[1] = (IntP) (*icq[1] + ic);
}
}
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
void exec()
{
clock_t t0 = clock();
IntP *icp = ic;
int i;
for (;;) {
switch ((int) icp[0]) {
case OpNeg: *icp[1] = - *icp[2]; ic...
case OpAdd1: (*icp[1])++; ic...
case OpMul: *icp[1] = *icp[2] * *icp[3]; ic...
case OpDiv: *icp[1] = *icp[2] / *icp[3]; ic...
case OpMod: *icp[1] = *icp[2] % *icp[3]; ic...
case OpAdd: *icp[1] = *icp[2] + *icp[3]; ic...
case OpSub: *icp[1] = *icp[2] - *icp[3]; ic...
case OpShr: *icp[1] = *icp[2] >> *icp[3]; ic...
case OpClt: *icp[1] = *icp[2] < *icp[3]; ic...
case OpCge: *icp[1] = *icp[2] >= *icp[3]; ic...
case OpCle: *icp[1] = *icp[2] <= *icp[3]; ic...
case OpCgt: *icp[1] = *icp[2] > *icp[3]; ic...
case OpCeq: *icp[1] = *icp[2] == *icp[3]; ic...
case OpCne: *icp[1] = *icp[2] != *icp[3]; ic...
case OpAnd: *icp[1] = *icp[2] & *icp[3]; ic...
case OpCpy: *icp[1] = *icp[2]; ic...
case OpPrint:
printf("%d\n", *icp[1]);
icp += 5;
continue;
case OpGoto: icp = (In...
case OpJeq: if (*icp[2] == *icp[3]) { icp = (In...
case OpJne: if (*icp[2] != *icp[3]) { icp = (In...
case OpJlt: if (*icp[2] < *icp[3]) { icp = (In...
case OpTime:
printf("time: %.3f[sec]\n", (clock() - t0) /...
icp += 5;
continue;
case OpEnd:
return;
case OpLop:
i = *icp[2];
i++;
*icp[2] = i;
if (i < *icp[3]) {
icp = (IntP *) icp[1];
continue;
}
icp += 5;
continue;
}
}
}
int run(String s) → HL-6と同じなので省略
////////////////////////////////////////////////////////...
int main(int argc, const char **argv) → HL-5と同じなので...
-トータルの行数は445行になっています。かなり増えました。...
----
-まず、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言語では、前置インクリメン...
-HL-6aまでは、なんかこう「おもちゃ言語」の感じがしていた...
** (2) HL-7の簡単な説明
-関数:
--void loadText(String path, String t, int siz)
---ファイルパスpathで指定されたソースファイルをtに読み込...
--int getTc(String s, int len)
---トークン(単語)をsに渡すと、それに対応するトークンコ...
--int isAlphabetOrNumber(unsigned char c)
---引数で渡された文字コードが、英数字であれば1を返す。そ...
---アンダースコアもHL-6の中ではアルファベットということに...
---この関数は以下のlexer()の下請け。
--int lexer(String s, int tc[])
---sにプログラムのソースコードを渡す。すると、tc[]にトー...
---より詳しい動作は、[[a21_txt01_2a]]を参照のこと。
--int phrCmp(int pid, String phr, int pc)
---tc[pc]からのトークンコード列がphrで指定されたトークン...
---pidはフレーズIDで、この番号を使ってphrCmp_tc[]のどこに...
---なお、処理できるフレーズの最大長はこのプログラムの場合...
---HL-7以降のphrCmpでは、以下の3種類のワイルドカードが使...
---[1] !!*# : 任意の1トークンにマッチする(#は0~8までの...
---[2] !!**# : 任意の式にマッチする(#は0~8までの数字)...
---[3] !!***# : 任意の式にマッチする(#は0~8までの数字)...
--void putIc(int op, IntP p1, IntP p2, IntP p3, IntP p4)
---引数で渡された内容を内部コードのic[]に書き込む関数。関...
--int tmpAlloc()
---未使用の一時変数を確保。変数名のトークンコードで返す。
---compile()の下請け関数。
--void tmpFree(int i)
---iが一時変数を指すトークンコードであれば、その一時変数...
---そうでなければ、何もしない。
---compile()の下請け関数。
--int exprSub1(int i, int priority, int op)
---二項演算子処理の標準形。exprSub()の下請け関数。
--int exprSub(int priority)
---式の解釈の処理の主要部分。expr()の下請け関数。
--int expr(int j)
---式の解釈処理のための便利関数。
---exprSub()が動けるように、変数epc、epc1などを準備して、...
---exprSub()の中でphrCmp()やexpr()を呼び出すこともあり得...
---引数jは、ワイルドカード番号で、そのワイルドカードにマ...
--int compile(String s)
---与えられた文字列をプログラムだと解釈して、内部コードを...
---関数run()の下請け関数。
--void exec()
---ic[]に格納された内部コードを高速に実行する。
---関数run()の下請け関数。
--int run(String s)
---言語処理の本体。HL-3までのmain()に相当。
---内部的にはcompile()してrun()しているだけ。
--int main(int argc, const char **argv)
---REPLの処理をしている。
-変数:
--String ts[]
---getTc()が管理している配列変数で、トークンコードからト...
--int tl[]
---getTc()が管理している配列変数で、トークンコードからト...
--unsigned char tcBuf[]
---getTc()が管理している変数で、トークン文字列の実体を保...
--int tcs, tcb
---どちらもgetTc()が管理している変数で、tcsは今までに発行...
---tcbはtcBuf[]の未使用領域を指している。
---もしtcBuf[]やtcbの役割がピンとこない場合は、[[a21_txt0...
--int var[]
---変数の値を記憶しておくための変数。トークンコードをその...
--int tc[]
---プログラムをトークンコード列に変換したものがここに入る。
--int phrCmp_tc[]
---phrCmp()が管理している変数で、phrCmp_tc[]にはフレーズ...
--int ppc1, wpc[], wpc1[]
---フレーズが一致した場合、ppc1に一致したフレーズの次のト...
---wpc[]にはワイルドカードで一致した位置が入る。
---ワイルドカードが式の場合、wpc1[]には式の終端が入る(厳...
--IntP ic[], *icq
---ic[]は内部コード(internal-code)を格納しておくための...
--int epc, epc1
---exprのためのpc, pc1に相当。
----
-HL-7の見どころは何といっても式の評価です。しかもそれをス...
** (3) 式の評価について (exprSub(), exprSub1(), tmpAlloc...
-ここでの式の評価というのは、つまり
print a + b - c * d + e
-みたいなプログラムをコンパイルした時に
OpAdd(_t0, a, b ); // _t0 = a + b
OpMul(_t1, c, d ); // _t1 = c * d
OpSub(_t2, _t0, _t1); // _t2 = _t0 - _t1
OpAdd(_t0, _t2, e ); // _t0 = _t2 + e
OpPrint(_t0);
-みたいな内部コード列に変換することです。基本的には式を左...
-これが難なくできるようになることがゴールです。このことだ...
----
-exprSub()という関数が、この式の評価の大半を処理していま...
-それではここで、exprSub(99)した場合を考えてみましょう。
-exprSub()では、変数epcを使って式を解釈していきます。
-関数に入ると、まずはepcの場所にカッコや前置演算子が来て...
-そうすると処理は
} else { // 変数もしくは定数.
i = tc[epc];
epc++;
}
-ここに来ます。これで、iに変数もしくは定数のトークンコー...
-もし「print a;」のように、後続の演算子がなければ、このi...
-さてもしこの後に演算子が来るとどうでしょうか。たとえば二...
} else if (TcPlus <= tc[epc] && tc[epc] <= TcMinus && p...
i = exprSub1(i, 4, tc[epc] - TcPlus + OpAdd); // 左...
-このif文の意味するところは、epcの場所に書いてあるトーク...
-このif文が成立するとどうなるのかというと、exprSub1()を使...
epc++;
j = exprSub(4);
k = tmpAlloc();
putIc(op, &var[k], &var[i], &var[j], 0);
tmpFree(i);
tmpFree(j);
i = k;
-やっていることは、まずepc++して、「+」を読み進めます。
-次に、exprSub(4)を呼び出して、その結果をjで受けます。こ...
-こうすることで、掛け算とか割り算を含む項が後続していれば...
-その次に tmpAlloc() を実行して、それをkに入れます。これ...
-次のputIc()で内部コードを生成しています。opの部分は、「+...
-次の2つのtmpFree()で、もしiやjが一時変数であったら、未使...
-そして最後に、iにkを代入します。これは、「ここまでの計算...
-以降はこの処理を、式の終わりまでやるか、もしくは指定され...
** (4) 左結合と右結合について
-C言語のほとんどの演算子は左結合ですが、一部右結合のもの...
-「a + b - c」という式は、+も-も左結合の演算子なので、一...
-意外なのは右結合の演算子です。「a = b = 3」の場合、=は右...
-でもこれは、そうでなければ困るというのがわかるでしょうか...
-exprSub()でこの右結合を実現するにはどうすればいいでしょ...
-結局、左結合か右結合かは、exprSub()の再起呼び出しの際に...
** (5) その他のこまごまとした説明 (expr(), phrCmp(), com...
-関数expr()は、exprSub()を利用する際に必要な準備をして、e...
--expr()の引数では、wpc[]の番号を指定します。この仕様にし...
--そしてexpr()の処理中にphrCmp()やexpr()を利用するかもし...
--その後は、保存した値をすべて復元して、終了しています。
-phrCmp()もHL-6aまでの内容に対してバージョンアップしてい...
--主な変更点は、任意の式にマッチする !!** や !!*** という...
--ただし、phrCmp()での式の判定はすごく雑で、「a + b」や「...
--ただそれだけであまりにも心配だと思ったので、expr()内で...
-compile()では、print命令と最後の任意の式のところだけに「...
-最後に任意の式の評価があるのだから、もう「!!*0 = !!*1 + ...
--なぜなら、もしこれらの記述をなくしてしまうと、単純な「a...
OpAdd(_t0, b, c);
OpCpy(a, _t0);
--という2命令に変換されてしまうからです。今はこういう余計...
OpAdd(a, b, c);
--という1命令に変換されます。だからHL-6aに対しての速度低...
-exec()では、追加された演算に対する演算命令をたくさん追加...
** 次回に続く
-次回: [[a21_txt01_8]]
--次回は ブロックif文やfor文をやります。
*こめんと欄
#comment
終了行:
* 川合のプログラミング言語自作のためのテキスト第三版#7
-(by [[K]], 2021.03.09)
** (1) HL-7
-まずC言語の演算子一覧を書きます。
|優先順位|演算子|形式|名前|結合方向|HL-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|コンマ演算子|左|×|
-全部の演算子をサポートするとHL-7のプログラムが長くなって...
-この取り合わせだと「この演算子があって、あの演算子がない...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
int var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-4と同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-2と同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) // HL-5のとは違...
{
int i0 = pid * 32, i, i1, j, k, t;
if (phrCmp_tc[i0 + 31] == 0) {
i1 = lexer(phr, &phrCmp_tc[i0]);
phrCmp_tc[i0 + 31] = i1;
}
i1 = phrCmp_tc[i0 + 31];
for (i = 0; i < i1; i++) {
t = phrCmp_tc[i0 + i];
if (t == TcWiCard || t == TcExpr || t == TcExpr0...
i++;
j = phrCmp_tc[i0 + i] - Tc0; // 後続の番号を...
wpc[j] = pc;
if (t == TcWiCard) {
pc++;
continue;
}
k = 0; // カッコの深さ.
for (;;) {
if (tc[pc] == TcSemi) break;
if (tc[pc] == TcComma && k == 0) break;
if (tc[pc] == TcBrOpn || tc[pc] == TcSqB...
if (tc[pc] == TcBrCls || tc[pc] == TcSqB...
if (k < 0) break;
pc++;
}
wpc1[j] = pc; // 式の終了位置を記憶.
if (t == TcExpr && wpc[j] == pc) return 0; /...
if (k > 0) return 0; // カッコの深さがおかし...
continue;
}
if (t != tc[pc]) return 0; // マッチせず.
pc++;
}
ppc1 = pc;
return 1; // マッチした.
}
////////////////////////////////////////////////////////...
typedef int *IntP; // こう書くと IntP は int * の代わり...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
IntP ic[10000], *icq; // ic[]:内部コード、icq:ic[]への書...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) →...
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() // 未使用の一時変数を確保.
{
int i;
for (i = 0; i < 10; i++) {
if (tmp_flag[i] == 0) break;
}
if (i >= 10) {
printf("tmpAlloc: error\n");
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.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int exprSub1(int i, int priority, int op) // 二項演算子...
{
int j, k;
epc++;
j = exprSub(priority);
k = tmpAlloc();
putIc(op, &var[k], &var[i], &var[j], 0);
tmpFree(i);
tmpFree(j);
if (i < 0 || j < 0) return -1;
return k;
}
int exprSub(int priority)
{
int i = -1, e0 = 0; // iはここまでの計算結果が入って...
ppc1 = 0;
if (phrCmp(99, "( !!**0 )", epc)) { // かっこ.
i = expr(0);
} else if (tc[epc] == TcPlPlus) { // 前置インクリメ...
epc++;
i = exprSub(2);
putIc(OpAdd1, &var[i], 0, 0, 0);
} else if (tc[epc] == TcMinus) { // 単項マイナス.
epc++;
e0 = exprSub(2);
i = tmpAlloc();
putIc(OpNeg, &var[i], &var[e0], 0, 0);
} else { // 変数もしくは定数.
i = tc[epc];
epc++;
}
if (ppc1 > 0)
epc = ppc1;
for (;;) {
tmpFree(e0);
if (i < 0 || e0 < 0) return -1; // ここまででエ...
if (epc >= epc1) break;
e0 = 0;
if (tc[epc] == TcPlPlus) { // 後置インクリメント.
epc++;
e0 = i;
i = tmpAlloc();
putIc(OpCpy, &var[i], &var[e0], 0, 0);
putIc(OpAdd1, &var[e0], 0, 0, 0);
} else if (TcAster <= tc[epc] && tc[epc] <= TcPe...
i = exprSub1(i, 3, tc[epc] - TcAster + OpMul...
} else if (TcPlus <= tc[epc] && tc[epc] <= TcMi...
i = exprSub1(i, 4, tc[epc] - TcPlus + OpAdd...
} else if (tc[epc] == TcShr && priority >= 6) {
i = exprSub1(i, 5, OpShr); // 左結合なので6...
} else if (TcLt <= tc[epc] && tc[epc] <= TcGt...
i = exprSub1(i, 6, tc[epc] - TcLt + OpClt...
} else if (TcEEq <= tc[epc] && tc[epc] <= TcNE...
i = exprSub1(i, 7, tc[epc] - TcEEq + OpCeq...
} else if (tc[epc] == TcAnd && priority >= 9) {
i = exprSub1(i, 8, OpAnd); // 左結合なので9...
} else if (tc[epc] == TcEqu && priority >= 15) {
epc++;
e0 = exprSub(15); // 右結合なので15のまま.
putIc(OpCpy, &var[i], &var[e0], 0, 0);
} else
break;
}
return i;
}
int expr(int j)
{
int i, k, old_epc = epc, old_epc1 = epc1, s[19]; // ...
if (wpc[j] == wpc1[j]) return 0;
for (k = 0; k < 9; k++) { // wpc[], wpc1[]を保存する.
s[k] = wpc [k];
s[k + 9] = wpc1[k];
}
s[18] = ppc1; // ppc1を保存する.
epc = wpc [j]; // epc, epc1を準備してexprSub()を呼...
epc1 = wpc1[j];
i = exprSub(99);
if (epc < epc1) return -1; // 式を最後まで解釈できな...
epc = old_epc; // 保存しておいた変数をすべて復元する.
epc1 = old_epc1;
for (k = 0; k < 9; k++) {
wpc [k] = s[k];
wpc1[k] = s[k + 9];
}
ppc1 = s[18];
return i;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i;
IntP *icq1;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
for (i = 0; i < 10; i++) { ・// 一時変数をすべて未使...
tmp_flag[i] = 0;
}
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
putIc(OpLop, &var[tc[wpc[4]]], &var[tc[wpc[0...
} else if (phrCmp( 9, "!!*0 = !!*1 + 1;", pc) &&...
putIc(OpAdd1, &var[tc[wpc[0]]], 0, 0, 0);
} else if (phrCmp( 2, "!!*0 = !!*1 + !!*2;", pc)...
putIc(OpAdd, &var[tc[wpc[0]]], &var[tc[wpc[...
} else if (phrCmp( 3, "!!*0 = !!*1 - !!*2;", pc)...
putIc(OpSub, &var[tc[wpc[0]]], &var[tc[wpc[...
! } else if (phrCmp( 4, "print !!**0;", pc)) { // ...
+ e0 = expr(0);
! putIc(OpPrint, &var[e0], 0, 0, 0);
} else if (phrCmp( 0, "!!*0:", pc)) { // ラベル...
var[tc[wpc[0]]] = icq - ic; // ラベルに対応...
} else if (phrCmp( 5, "goto !!*0;", pc)) { // go...
putIc(OpGoto, &var[tc[wpc[0]]], 0, 0, 0);
} else if (phrCmp( 6, "if (!!*0 !!*1 !!*2) goto ...
putIc(OpJeq + (tc[wpc[1]] - TcEEq), &var[tc[...
} else if (phrCmp( 7, "time;", pc)) {
putIc(OpTime, 0, 0, 0, 0);
! } else if (phrCmp( 8, "!!***0;", pc)) { // これ...
! e0 = expr(0);
} else
goto err;
tmpFree(e0);
if (e0 < 0) goto err;
pc = ppc1;
}
putIc(OpEnd, 0, 0, 0, 0);
icq1 = icq;
for (icq = ic; icq < icq1; icq += 5) { // goto先の...
i = (int) icq[0];
if (OpGoto <= i && i <= OpLop) {
icq[1] = (IntP) (*icq[1] + ic);
}
}
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
void exec()
{
clock_t t0 = clock();
IntP *icp = ic;
int i;
for (;;) {
switch ((int) icp[0]) {
case OpNeg: *icp[1] = - *icp[2]; ic...
case OpAdd1: (*icp[1])++; ic...
case OpMul: *icp[1] = *icp[2] * *icp[3]; ic...
case OpDiv: *icp[1] = *icp[2] / *icp[3]; ic...
case OpMod: *icp[1] = *icp[2] % *icp[3]; ic...
case OpAdd: *icp[1] = *icp[2] + *icp[3]; ic...
case OpSub: *icp[1] = *icp[2] - *icp[3]; ic...
case OpShr: *icp[1] = *icp[2] >> *icp[3]; ic...
case OpClt: *icp[1] = *icp[2] < *icp[3]; ic...
case OpCge: *icp[1] = *icp[2] >= *icp[3]; ic...
case OpCle: *icp[1] = *icp[2] <= *icp[3]; ic...
case OpCgt: *icp[1] = *icp[2] > *icp[3]; ic...
case OpCeq: *icp[1] = *icp[2] == *icp[3]; ic...
case OpCne: *icp[1] = *icp[2] != *icp[3]; ic...
case OpAnd: *icp[1] = *icp[2] & *icp[3]; ic...
case OpCpy: *icp[1] = *icp[2]; ic...
case OpPrint:
printf("%d\n", *icp[1]);
icp += 5;
continue;
case OpGoto: icp = (In...
case OpJeq: if (*icp[2] == *icp[3]) { icp = (In...
case OpJne: if (*icp[2] != *icp[3]) { icp = (In...
case OpJlt: if (*icp[2] < *icp[3]) { icp = (In...
case OpTime:
printf("time: %.3f[sec]\n", (clock() - t0) /...
icp += 5;
continue;
case OpEnd:
return;
case OpLop:
i = *icp[2];
i++;
*icp[2] = i;
if (i < *icp[3]) {
icp = (IntP *) icp[1];
continue;
}
icp += 5;
continue;
}
}
}
int run(String s) → HL-6と同じなので省略
////////////////////////////////////////////////////////...
int main(int argc, const char **argv) → HL-5と同じなので...
-トータルの行数は445行になっています。かなり増えました。...
----
-まず、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言語では、前置インクリメン...
-HL-6aまでは、なんかこう「おもちゃ言語」の感じがしていた...
** (2) HL-7の簡単な説明
-関数:
--void loadText(String path, String t, int siz)
---ファイルパスpathで指定されたソースファイルをtに読み込...
--int getTc(String s, int len)
---トークン(単語)をsに渡すと、それに対応するトークンコ...
--int isAlphabetOrNumber(unsigned char c)
---引数で渡された文字コードが、英数字であれば1を返す。そ...
---アンダースコアもHL-6の中ではアルファベットということに...
---この関数は以下のlexer()の下請け。
--int lexer(String s, int tc[])
---sにプログラムのソースコードを渡す。すると、tc[]にトー...
---より詳しい動作は、[[a21_txt01_2a]]を参照のこと。
--int phrCmp(int pid, String phr, int pc)
---tc[pc]からのトークンコード列がphrで指定されたトークン...
---pidはフレーズIDで、この番号を使ってphrCmp_tc[]のどこに...
---なお、処理できるフレーズの最大長はこのプログラムの場合...
---HL-7以降のphrCmpでは、以下の3種類のワイルドカードが使...
---[1] !!*# : 任意の1トークンにマッチする(#は0~8までの...
---[2] !!**# : 任意の式にマッチする(#は0~8までの数字)...
---[3] !!***# : 任意の式にマッチする(#は0~8までの数字)...
--void putIc(int op, IntP p1, IntP p2, IntP p3, IntP p4)
---引数で渡された内容を内部コードのic[]に書き込む関数。関...
--int tmpAlloc()
---未使用の一時変数を確保。変数名のトークンコードで返す。
---compile()の下請け関数。
--void tmpFree(int i)
---iが一時変数を指すトークンコードであれば、その一時変数...
---そうでなければ、何もしない。
---compile()の下請け関数。
--int exprSub1(int i, int priority, int op)
---二項演算子処理の標準形。exprSub()の下請け関数。
--int exprSub(int priority)
---式の解釈の処理の主要部分。expr()の下請け関数。
--int expr(int j)
---式の解釈処理のための便利関数。
---exprSub()が動けるように、変数epc、epc1などを準備して、...
---exprSub()の中でphrCmp()やexpr()を呼び出すこともあり得...
---引数jは、ワイルドカード番号で、そのワイルドカードにマ...
--int compile(String s)
---与えられた文字列をプログラムだと解釈して、内部コードを...
---関数run()の下請け関数。
--void exec()
---ic[]に格納された内部コードを高速に実行する。
---関数run()の下請け関数。
--int run(String s)
---言語処理の本体。HL-3までのmain()に相当。
---内部的にはcompile()してrun()しているだけ。
--int main(int argc, const char **argv)
---REPLの処理をしている。
-変数:
--String ts[]
---getTc()が管理している配列変数で、トークンコードからト...
--int tl[]
---getTc()が管理している配列変数で、トークンコードからト...
--unsigned char tcBuf[]
---getTc()が管理している変数で、トークン文字列の実体を保...
--int tcs, tcb
---どちらもgetTc()が管理している変数で、tcsは今までに発行...
---tcbはtcBuf[]の未使用領域を指している。
---もしtcBuf[]やtcbの役割がピンとこない場合は、[[a21_txt0...
--int var[]
---変数の値を記憶しておくための変数。トークンコードをその...
--int tc[]
---プログラムをトークンコード列に変換したものがここに入る。
--int phrCmp_tc[]
---phrCmp()が管理している変数で、phrCmp_tc[]にはフレーズ...
--int ppc1, wpc[], wpc1[]
---フレーズが一致した場合、ppc1に一致したフレーズの次のト...
---wpc[]にはワイルドカードで一致した位置が入る。
---ワイルドカードが式の場合、wpc1[]には式の終端が入る(厳...
--IntP ic[], *icq
---ic[]は内部コード(internal-code)を格納しておくための...
--int epc, epc1
---exprのためのpc, pc1に相当。
----
-HL-7の見どころは何といっても式の評価です。しかもそれをス...
** (3) 式の評価について (exprSub(), exprSub1(), tmpAlloc...
-ここでの式の評価というのは、つまり
print a + b - c * d + e
-みたいなプログラムをコンパイルした時に
OpAdd(_t0, a, b ); // _t0 = a + b
OpMul(_t1, c, d ); // _t1 = c * d
OpSub(_t2, _t0, _t1); // _t2 = _t0 - _t1
OpAdd(_t0, _t2, e ); // _t0 = _t2 + e
OpPrint(_t0);
-みたいな内部コード列に変換することです。基本的には式を左...
-これが難なくできるようになることがゴールです。このことだ...
----
-exprSub()という関数が、この式の評価の大半を処理していま...
-それではここで、exprSub(99)した場合を考えてみましょう。
-exprSub()では、変数epcを使って式を解釈していきます。
-関数に入ると、まずはepcの場所にカッコや前置演算子が来て...
-そうすると処理は
} else { // 変数もしくは定数.
i = tc[epc];
epc++;
}
-ここに来ます。これで、iに変数もしくは定数のトークンコー...
-もし「print a;」のように、後続の演算子がなければ、このi...
-さてもしこの後に演算子が来るとどうでしょうか。たとえば二...
} else if (TcPlus <= tc[epc] && tc[epc] <= TcMinus && p...
i = exprSub1(i, 4, tc[epc] - TcPlus + OpAdd); // 左...
-このif文の意味するところは、epcの場所に書いてあるトーク...
-このif文が成立するとどうなるのかというと、exprSub1()を使...
epc++;
j = exprSub(4);
k = tmpAlloc();
putIc(op, &var[k], &var[i], &var[j], 0);
tmpFree(i);
tmpFree(j);
i = k;
-やっていることは、まずepc++して、「+」を読み進めます。
-次に、exprSub(4)を呼び出して、その結果をjで受けます。こ...
-こうすることで、掛け算とか割り算を含む項が後続していれば...
-その次に tmpAlloc() を実行して、それをkに入れます。これ...
-次のputIc()で内部コードを生成しています。opの部分は、「+...
-次の2つのtmpFree()で、もしiやjが一時変数であったら、未使...
-そして最後に、iにkを代入します。これは、「ここまでの計算...
-以降はこの処理を、式の終わりまでやるか、もしくは指定され...
** (4) 左結合と右結合について
-C言語のほとんどの演算子は左結合ですが、一部右結合のもの...
-「a + b - c」という式は、+も-も左結合の演算子なので、一...
-意外なのは右結合の演算子です。「a = b = 3」の場合、=は右...
-でもこれは、そうでなければ困るというのがわかるでしょうか...
-exprSub()でこの右結合を実現するにはどうすればいいでしょ...
-結局、左結合か右結合かは、exprSub()の再起呼び出しの際に...
** (5) その他のこまごまとした説明 (expr(), phrCmp(), com...
-関数expr()は、exprSub()を利用する際に必要な準備をして、e...
--expr()の引数では、wpc[]の番号を指定します。この仕様にし...
--そしてexpr()の処理中にphrCmp()やexpr()を利用するかもし...
--その後は、保存した値をすべて復元して、終了しています。
-phrCmp()もHL-6aまでの内容に対してバージョンアップしてい...
--主な変更点は、任意の式にマッチする !!** や !!*** という...
--ただし、phrCmp()での式の判定はすごく雑で、「a + b」や「...
--ただそれだけであまりにも心配だと思ったので、expr()内で...
-compile()では、print命令と最後の任意の式のところだけに「...
-最後に任意の式の評価があるのだから、もう「!!*0 = !!*1 + ...
--なぜなら、もしこれらの記述をなくしてしまうと、単純な「a...
OpAdd(_t0, b, c);
OpCpy(a, _t0);
--という2命令に変換されてしまうからです。今はこういう余計...
OpAdd(a, b, c);
--という1命令に変換されます。だからHL-6aに対しての速度低...
-exec()では、追加された演算に対する演算命令をたくさん追加...
** 次回に続く
-次回: [[a21_txt01_8]]
--次回は ブロックif文やfor文をやります。
*こめんと欄
#comment
ページ名: