asgn: VAR '=' expr { $$=$1->u.val=$3; $1->type = VAR; } ;expr: NUMBER | VAR { if ($1->type == UNDEF) execerror("undefined variable", $1->name); $$ = $1->u.val; } | asgn | BLTIN '(' expr ')' { $$ = (*($1->u.ptr))($3); } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { if ($3 == 0.0) execerror("division by zero", ""); $$ = $1 / $3; } | expr '^' expr { $$ = Pow($1, $3); } | '(' expr ')' { $$ = $2; } | '-' expr %prec UNARYMINUS { $$ = -$2; } ;%%/* end of grammar */...Теперь в грамматике присутствует
asgnexprVAR = exprявляется присваиванием, и, следовательно, ни одно из значений не печатается. Заметьте, кстати, как мы легко добавили к грамматике операцию возведения в степень, являющуюся правоассоциативной.
Для стека
yacc%unionSymbolhoc.hЛексический анализатор распознает имена переменных, находит их в таблице имен и определяет, относятся ли они к переменным (
VARBLTINyylexPIVARОдно из свойств переменной состоит в том, что ей может быть присвоено либо не присвоено значение, поэтому обращение к не определенной переменной должно диагностироваться программой
yyparseVARxxx = 1Ниже приводится измененная часть функции
yylexyylex() /* hoc3 */{ ... if (isalpha(c)) { Symbol *s; char sbuf[100], *p = sbuf; do { *p++ = c; } while ((c=getchar()) != EOF && isalnum(c)); ungetc(c, stdin); *p = '\0'; if ((s=lookup(sbuf)) == 0) s = install(sbuf, UNDEF, 0.0); yylval.sym = s; return s->type == UNDEF ? VAR : s->type; } ...В функции
maininitPImain(argc, argv) /* hoc3 */ char *argv[];{ int fpecatch(); progname = argv[0]; init(); setjmp(begin); signal(SIGFPE, fpecatch); yyparse();}Теперь остался только файл
math.сmath.с<math.h><errno.h>$ cat math.с#include <math.h>#include <errno.h>extern int errno;double errcheck();double Log(x) double x;{ return errcheck(log(x), "log");}double Log10(x) double x;{ return errcheck(log10(x), "log10");