The next step (a small one) is to add "memory" to hoc1, to make hoc2. The memory is 26 variables, named a through z. We 'll also
add some error handling. If you try hoc1, you'll recognize that its approach to syntax errors is to print message and die, and its treatment of arithmetic errors like division by zero is reprehensible.
The lexical analyzer yylex has to recognize letters as variables; the grammar has to include productions of the form
An expression can contain an assignment, which permits multiple assignments like
The easiest way to store the values of the variables is in a 26-element array;the single-letter variable name can be used to index tye array. But if the grammar is to process both variable names and values in the same stack, yacc has to be told that its stack contains a union of a double and an int, not just a double. This is done with a %union declaration near the top. A #define or a typedef is fine for setting the stack to a basic type like double, but the %union mechanism is required for union types because yacc checks for consistency in expressions like $$=$2.
Error handling comes in several pieces. The obvious one is a test for a zero divisor; if one occurs, an error routine execerror is called.
A second test is to catch the "floating point exception" signal that occurs what a floating point number overflows. The signal is set in main.
The final part of error recovery is the addition of a production for error. "error" is a reserved work in a yacc grammar; it provides a way to anticipate and recover from a syntax error.
- %{
-
double mem[26]; /* memory for variables 'a'..'z' */
-
%}
-
%union { /* stack type */
-
double val; /* actual value */
-
int index; /* index into mem[] */
-
}
-
%token <val> NUMBER
-
%token <index> VAR
-
%type <val> expr
-
%right '='
-
%left '+' '-' /* left associative, same precedence */
-
%left '*' '/' /* left assoc., higher precedence */
-
%left UNARYMINUS /* new */
-
%%
-
list: /* nothing */
-
| list '\n'
-
| list expr '\n' {printf("\t%.8g\n", $2);}
-
| list error '\n' {yyerrok;}
-
;
-
expr: NUMBER
-
| VAR {$$ = mem[$1]; }
-
| VAR '=' expr {$$=mem[$1]=$3;}
-
| '-' expr %prec UNARYMINUS {$$ = -$2; } /* new */
-
| 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 ')' { $$ = $2;}
-
;
-
%%
-
/* end of grammar */
-
-
#include <stdio.h>
-
#include <ctype.h>
-
#include <signal.h>
-
#include <setjmp.h>
-
jmp_buf begin;
-
char *progname; /* for error messages */
-
int lineno = 1;
-
int fpecatch();
-
main(int argc, char* argv[])
-
{
-
progname = argv[0];
-
setjmp(begin);
-
signal(SIGFPE, fpecatch);
-
yyparse();
-
}
-
-
execerror(char *s, char *t) /* recover from run-time error */
-
{
-
warning(s, t);
-
longjmp(begin, 0);
-
}
-
-
fpecatch() /* catch floating point exceptions */
-
{
-
execerror("floating point exeption", (char *)0);
-
}
-
yylex()
-
{
-
int c;
-
while ((c=getchar()) == ' ' || c == '\t')
-
;
-
if (c == EOF)
-
return 0;
-
if (c == '.' || isdigit(c))
-
{
-
ungetc(c, stdin);
-
scanf("%lf", &yylval.val);
-
return NUMBER;
-
}
-
if (islower(c))
-
{
-
yylval.index = c - 'a'; /* ASCII only */
-
return VAR;
-
}
-
if (c == '\n')
-
lineno++;
-
return c;
-
}
-
-
yyerror(char *s)
-
{
-
warning (s, (char *)0);
-
}
-
-
warning (char *s, char *t)
-
{
-
fprintf(stderr, "%s: %s", progname, s);
-
if (t)
-
fprintf(stderr, "%s", t);
-
fprintf(stderr, " near line %d\n", lineno);
-
}
阅读(550) | 评论(0) | 转发(0) |