acl4のプログラムのページ0002
(1) 「式の評価」プログラム
- a4_0008までの関数があれば、そこそこ面白いことができるはずだと思ったので、適当に書いてみました。
(2) p0002a の実行例
>p0002a "1+2*3"
7.000000
>p0002a "2*(3+4)"
14.000000
- ちゃんと演算子の優先順が正しく働いていることがわかります。
- もとはといえば、プリプロセッサを作るには #if とかをやる必要があって、それなら式の評価は必要そうだなと思ったのです。
- プリプロセッサでは整数演算しかやらないので、 double でやる理由はないのですが、まあ何となく今回は double でやってみました。
- 45行でできたっていうのは、なかなか良いと思います。
(3) p0002a.c [45行]
#define a_Version 1
#include <acl4.c>
double eval(Token0 *t0, int pri)
{
const char *t = Token1_get(t0), *s0; double d = 0.0; uint32_t c = t0->c;
if (c == '(') { d = eval(t0, 99); Token1_get(t0); goto op2; }
if (c == '+' && pri >= 2) { d = + eval(t0, 2); goto op2; }
if (c == '-' && pri >= 2) { d = - eval(t0, 2); goto op2; }
if (c == '!' && pri >= 2) { d = ! (int) eval(t0, 2); goto op2; }
if (c == '~' && pri >= 2) { d = ~ (int) eval(t0, 2); goto op2; }
d = strtod(t, NULL);
op2:
s0 = t0->s; t = Token1_get(t0); c = t0->c; // トークンを取ってくる前に、押し戻しのためにt0->sを控えておく.
if (c == '*' && pri >= 4) { d = d * eval(t0, 3); goto op2; }
if (c == '/' && pri >= 4) { d = d / eval(t0, 3); goto op2; }
if (c == '%' && pri >= 4) { d = (int) d % (int) eval(t0, 3); goto op2; }
if (c == '+' && pri >= 5) { d = d + eval(t0, 4); goto op2; }
if (c == '-' && pri >= 5) { d = d - eval(t0, 4); goto op2; }
if (c == ('<' | '<' << 8) && pri >= 6) { d = (int) d << (int) eval(t0, 5); goto op2; }
if (c == ('>' | '>' << 8) && pri >= 6) { d = (int) d >> (int) eval(t0, 5); goto op2; }
if (c == ('<' | '=' << 8) && pri >= 7) { d = d <= eval(t0, 6); goto op2; }
if (c == '<' && pri >= 7) { d = d < eval(t0, 6); goto op2; }
if (c == ('>' | '=' << 8) && pri >= 7) { d = d >= eval(t0, 6); goto op2; }
if (c == '>' && pri >= 7) { d = d > eval(t0, 6); goto op2; }
if (c == ('=' | '=' << 8) && pri >= 8) { d = d == eval(t0, 7); goto op2; }
if (c == ('!' | '=' << 8) && pri >= 8) { d = d != eval(t0, 7); goto op2; }
if (c == '&' && pri >= 9) { d = (int) d & (int) eval(t0, 8); goto op2; }
if (c == '^' && pri >= 10) { d = (int) d ^ (int) eval(t0, 9); goto op2; }
if (c == '|' && pri >= 11) { d = (int) d | (int) eval(t0, 10); goto op2; }
if (c == ('&' | '&' << 8) && pri >= 12) { d = (int) d && (int) eval(t0, 11); goto op2; }
if (c == ('|' | '|' << 8) && pri >= 13) { d = (int) d || (int) eval(t0, 12); goto op2; }
t0->s = s0; return d; // 一度読み込んだ未解釈の演算子をt0に押し戻してからreturn.
}
int main(int argc, const char **argv)
{
VecChr vc[1]; VecChr_iniArg(vc, argc, argv, 1, 3);
Token0 t0[1]; Token0_ini1(t0);
t0->s = vc->p; t0->s1 = vc->p + vc->n;
printf("%f\n", eval(t0, 99));
VecChr_din(vc);
a_malloc_debugList(_arg);
return 0;
}
(4) 発展版?
- p0002a が簡単にできて面白かったので、ちょっと拡張してみようと思いました。
- sin, cos, tan, log, exp, sqrt を使えるようにしました。
- 変数を使えるようにしました。代入演算子も追加しました。
- ; という演算子を入れました。
- print も入れました。
- これだけ入れても86行でした。
>p0002b "a=b=c=3; c=c-1; print a+b*c"
9.000000
>p0002b "print a0=0; print a1=1; print a2=a1+a0; print a3=a2+a1; print a4=a3+a2; print a5=a4+a3; print a6=a5+a4;"
0.000000
1.000000
1.000000
2.000000
3.000000
5.000000
8.000000
- VecChr_iniArg を使っているので、あらかじめファイルを作っておいて、それを指定して実行させることももちろん可能です。
- ついでなので、 t0008a を使ってファイルを作ってそれを実行させてみました。
- ( t0008a は a4_0008 で作った、たった11行のサンプルプログラムです。)
>t0008a "print a0=0;\nprint a1=1;\nprint a2=a1+a0;\nprint a3=a2+a1;\nprint a4=a3+a2;\nprint a5=a4+a3;\nprint a6=a5+a4;\n" > p0002b.txt
>t0008a file:p0002b.txt
print a0=0;
print a1=1;
print a2=a1+a0;
print a3=a2+a1;
print a4=a3+a2;
print a5=a4+a3;
print a6=a5+a4;
>p0002b file:p0002b.txt
0.000000
1.000000
1.000000
2.000000
3.000000
5.000000
8.000000
- 感想としては、もはやこれは簡易言語だと言ってもいいかもしれないと思いました(?!)。たった86行でこんなに遊べていいのかな???(笑)
(5) p0002b.c [86行]
#define a_Version 1
#include <acl4.c>
class_(Var) { SetElm elm[1]; double d; };
Set0 setVar[1];
double *getVarPtr(const char *s, intptr_t n)
{
Var *v = Set0_findKn(setVar, s, n);
if (v == NULL) {
v = malloc_(_arg_ sizeof (Var));
v->elm->k = s;
v->elm->n = n;
v->d = 0.0;
Set0_add(setVar, v->elm);
}
return &(v->d);
}
int np = 0;
double eval(Token0 *t0, int pri)
{
const char *t = Token1_get(t0), *s0; double d = 0.0, *pd = NULL; uint32_t c = t0->c, n = t0->len;
if (n == 0) return d;
if (c == '(') { d = eval(t0, 99); Token1_get(t0); goto op2; }
if (c == '+' && pri >= 2) { d = + eval(t0, 2); goto op2; }
if (c == '-' && pri >= 2) { d = - eval(t0, 2); goto op2; }
if (c == '!' && pri >= 2) { d = ! (int) eval(t0, 2); goto op2; }
if (c == '~' && pri >= 2) { d = ~ (int) eval(t0, 2); goto op2; }
if (pri >= 2 && n == 3 && memcmp(t, "sin", 3) == 0) { d = sin( eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 3 && memcmp(t, "cos", 3) == 0) { d = cos( eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 3 && memcmp(t, "tan", 3) == 0) { d = tan( eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 3 && memcmp(t, "exp", 3) == 0) { d = exp( eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 3 && memcmp(t, "log", 3) == 0) { d = log( eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 4 && memcmp(t, "sqrt", 4) == 0) { d = sqrt(eval(t0, 2)); goto op2; }
if (pri >= 2 && n == 5 && memcmp(t, "print", 5) == 0) { d = eval(t0, 97); printf("%f\n", d); np++; goto op2; }
if ('0' <= *t && *t <= '9')
d = strtod(t, NULL);
else {
pd = getVarPtr(t, n);
d = *pd;
}
op2:
s0 = t0->s; t = Token1_get(t0); c = t0->c; n = t0->len;
if (n == 0) return d;
if (c == '*' && pri >= 4) { d = d * eval(t0, 3); goto op2; }
if (c == '/' && pri >= 4) { d = d / eval(t0, 3); goto op2; }
if (c == '%' && pri >= 4) { d = (int) d % (int) eval(t0, 3); goto op2; }
if (c == '+' && pri >= 5) { d = d + eval(t0, 4); goto op2; }
if (c == '-' && pri >= 5) { d = d - eval(t0, 4); goto op2; }
if (c == ('<' | '<' << 8) && pri >= 6) { d = (int) d << (int) eval(t0, 5); goto op2; }
if (c == ('>' | '>' << 8) && pri >= 6) { d = (int) d >> (int) eval(t0, 5); goto op2; }
if (c == ('<' | '=' << 8) && pri >= 7) { d = d <= eval(t0, 6); goto op2; }
if (c == '<' && pri >= 7) { d = d < eval(t0, 6); goto op2; }
if (c == ('>' | '=' << 8) && pri >= 7) { d = d >= eval(t0, 6); goto op2; }
if (c == '>' && pri >= 7) { d = d > eval(t0, 6); goto op2; }
if (c == ('=' | '=' << 8) && pri >= 8) { d = d == eval(t0, 7); goto op2; }
if (c == ('!' | '=' << 8) && pri >= 8) { d = d != eval(t0, 7); goto op2; }
if (c == '&' && pri >= 9) { d = (int) d & (int) eval(t0, 8); goto op2; }
if (c == '^' && pri >= 10) { d = (int) d ^ (int) eval(t0, 9); goto op2; }
if (c == '|' && pri >= 11) { d = (int) d | (int) eval(t0, 10); goto op2; }
if (c == ('&' | '&' << 8) && pri >= 12) { d = (int) d && (int) eval(t0, 11); goto op2; }
if (c == ('|' | '|' << 8) && pri >= 13) { d = (int) d || (int) eval(t0, 12); goto op2; }
if (c == '=' && pri >= 15) { d = eval(t0, 15); if (pd != NULL) { *pd = d; } goto op2; }
if (c == ';' && pri >= 98) { d = eval(t0, 97); goto op2; }
t0->s = s0; return d; // 一度読み込んだ未解釈の演算子をt0に押し戻してからreturn.
}
int main(int argc, const char **argv)
{
VecChr vc[1]; VecChr_iniArg(vc, argc, argv, 1, 3);
Token0 t0[1]; Token0_ini1(t0);
Set0_ini(setVar);
t0->s = vc->p; t0->s1 = vc->p + vc->n;
double d = eval(t0, 99);
if (np == 0)
printf("%f\n", d);
VecChr_din(vc);
intptr_t i, n = setVar->tbl->n / sizeof (Var *);
for (i = 0; i < n; i++)
free_(_arg_ ((Var **) setVar->tbl->p)[i], sizeof (Var));
Set0_din(setVar);
a_malloc_debugList(_arg);
return 0;
}
(99) 更新履歴
- 2026.02.03(火) 初版
- 2026.02.04(水) p0002b を追加