309 lines
4.3 KiB
Text
309 lines
4.3 KiB
Text
/* $NetBSD: calc1.y,v 1.1.1.3 2011/09/10 21:22:09 christos Exp $ */
|
|
|
|
%{
|
|
|
|
/* http://dinosaur.compilertools.net/yacc/index.html */
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
typedef struct interval
|
|
{
|
|
double lo, hi;
|
|
}
|
|
INTERVAL;
|
|
|
|
INTERVAL vmul(double, double, INTERVAL);
|
|
INTERVAL vdiv(double, double, INTERVAL);
|
|
|
|
int dcheck(INTERVAL);
|
|
|
|
double dreg[26];
|
|
INTERVAL vreg[26];
|
|
|
|
%}
|
|
%expect 18
|
|
|
|
%start line
|
|
%union
|
|
{
|
|
int ival;
|
|
double dval;
|
|
INTERVAL vval;
|
|
}
|
|
|
|
%token <ival> DREG VREG /* indices into dreg, vreg arrays */
|
|
%token <dval> CONST /* floating point constant */
|
|
|
|
%type <dval> dexp /* expression */
|
|
%type <vval> vexp /* interval expression */
|
|
|
|
/* precedence information about the operators */
|
|
|
|
%left '+' '-'
|
|
%left '*' '/'
|
|
%left UMINUS /* precedence for unary minus */
|
|
|
|
%% /* beginning of rules section */
|
|
|
|
lines : /* empty */
|
|
| lines line
|
|
;
|
|
|
|
line : dexp '\n'
|
|
{
|
|
(void) printf("%15.8f\n", $1);
|
|
}
|
|
| vexp '\n'
|
|
{
|
|
(void) printf("(%15.8f, %15.8f)\n", $1.lo, $1.hi);
|
|
}
|
|
| DREG '=' dexp '\n'
|
|
{
|
|
dreg[$1] = $3;
|
|
}
|
|
| VREG '=' vexp '\n'
|
|
{
|
|
vreg[$1] = $3;
|
|
}
|
|
| error '\n'
|
|
{
|
|
yyerrok;
|
|
}
|
|
;
|
|
|
|
dexp : CONST
|
|
| DREG
|
|
{
|
|
$$ = dreg[$1];
|
|
}
|
|
| dexp '+' dexp
|
|
{
|
|
$$ = $1 + $3;
|
|
}
|
|
| dexp '-' dexp
|
|
{
|
|
$$ = $1 - $3;
|
|
}
|
|
| dexp '*' dexp
|
|
{
|
|
$$ = $1 * $3;
|
|
}
|
|
| dexp '/' dexp
|
|
{
|
|
$$ = $1 / $3;
|
|
}
|
|
| '-' dexp %prec UMINUS
|
|
{
|
|
$$ = -$2;
|
|
}
|
|
| '(' dexp ')'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
vexp : dexp
|
|
{
|
|
$$.hi = $$.lo = $1;
|
|
}
|
|
| '(' dexp ',' dexp ')'
|
|
{
|
|
$$.lo = $2;
|
|
$$.hi = $4;
|
|
if ( $$.lo > $$.hi )
|
|
{
|
|
(void) printf("interval out of order\n");
|
|
YYERROR;
|
|
}
|
|
}
|
|
| VREG
|
|
{
|
|
$$ = vreg[$1];
|
|
}
|
|
| vexp '+' vexp
|
|
{
|
|
$$.hi = $1.hi + $3.hi;
|
|
$$.lo = $1.lo + $3.lo;
|
|
}
|
|
| dexp '+' vexp
|
|
{
|
|
$$.hi = $1 + $3.hi;
|
|
$$.lo = $1 + $3.lo;
|
|
}
|
|
| vexp '-' vexp
|
|
{
|
|
$$.hi = $1.hi - $3.lo;
|
|
$$.lo = $1.lo - $3.hi;
|
|
}
|
|
| dexp '-' vexp
|
|
{
|
|
$$.hi = $1 - $3.lo;
|
|
$$.lo = $1 - $3.hi;
|
|
}
|
|
| vexp '*' vexp
|
|
{
|
|
$$ = vmul( $1.lo, $1.hi, $3 );
|
|
}
|
|
| dexp '*' vexp
|
|
{
|
|
$$ = vmul ($1, $1, $3 );
|
|
}
|
|
| vexp '/' vexp
|
|
{
|
|
if (dcheck($3)) YYERROR;
|
|
$$ = vdiv ( $1.lo, $1.hi, $3 );
|
|
}
|
|
| dexp '/' vexp
|
|
{
|
|
if (dcheck ( $3 )) YYERROR;
|
|
$$ = vdiv ($1, $1, $3 );
|
|
}
|
|
| '-' vexp %prec UMINUS
|
|
{
|
|
$$.hi = -$2.lo;
|
|
$$.lo = -$2.hi;
|
|
}
|
|
| '(' vexp ')'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
%% /* beginning of subroutines section */
|
|
|
|
#ifdef YYBYACC
|
|
extern int YYLEX_DECL();
|
|
static void YYERROR_DECL();
|
|
#endif
|
|
|
|
#define BSZ 50 /* buffer size for floating point numbers */
|
|
|
|
/* lexical analysis */
|
|
|
|
static void
|
|
yyerror(const char *s)
|
|
{
|
|
fprintf(stderr, "%s\n", s);
|
|
}
|
|
|
|
int
|
|
yylex(void)
|
|
{
|
|
int c;
|
|
|
|
while ((c = getchar()) == ' ')
|
|
{ /* skip over blanks */
|
|
}
|
|
|
|
if (isupper(c))
|
|
{
|
|
yylval.ival = c - 'A';
|
|
return (VREG);
|
|
}
|
|
if (islower(c))
|
|
{
|
|
yylval.ival = c - 'a';
|
|
return (DREG);
|
|
}
|
|
|
|
if (isdigit(c) || c == '.')
|
|
{
|
|
/* gobble up digits, points, exponents */
|
|
char buf[BSZ + 1], *cp = buf;
|
|
int dot = 0, expr = 0;
|
|
|
|
for (; (cp - buf) < BSZ; ++cp, c = getchar())
|
|
{
|
|
|
|
*cp = c;
|
|
if (isdigit(c))
|
|
continue;
|
|
if (c == '.')
|
|
{
|
|
if (dot++ || expr)
|
|
return ('.'); /* will cause syntax error */
|
|
continue;
|
|
}
|
|
|
|
if (c == 'e')
|
|
{
|
|
if (expr++)
|
|
return ('e'); /* will cause syntax error */
|
|
continue;
|
|
}
|
|
|
|
/* end of number */
|
|
break;
|
|
}
|
|
*cp = '\0';
|
|
|
|
if ((cp - buf) >= BSZ)
|
|
printf("constant too long: truncated\n");
|
|
else
|
|
ungetc(c, stdin); /* push back last char read */
|
|
yylval.dval = atof(buf);
|
|
return (CONST);
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
static INTERVAL
|
|
hilo(double a, double b, double c, double d)
|
|
{
|
|
/* returns the smallest interval containing a, b, c, and d */
|
|
/* used by *, / routines */
|
|
INTERVAL v;
|
|
|
|
if (a > b)
|
|
{
|
|
v.hi = a;
|
|
v.lo = b;
|
|
}
|
|
else
|
|
{
|
|
v.hi = b;
|
|
v.lo = a;
|
|
}
|
|
|
|
if (c > d)
|
|
{
|
|
if (c > v.hi)
|
|
v.hi = c;
|
|
if (d < v.lo)
|
|
v.lo = d;
|
|
}
|
|
else
|
|
{
|
|
if (d > v.hi)
|
|
v.hi = d;
|
|
if (c < v.lo)
|
|
v.lo = c;
|
|
}
|
|
return (v);
|
|
}
|
|
|
|
INTERVAL
|
|
vmul(double a, double b, INTERVAL v)
|
|
{
|
|
return (hilo(a * v.hi, a * v.lo, b * v.hi, b * v.lo));
|
|
}
|
|
|
|
int
|
|
dcheck(INTERVAL v)
|
|
{
|
|
if (v.hi >= 0. && v.lo <= 0.)
|
|
{
|
|
printf("divisor interval contains 0.\n");
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
INTERVAL
|
|
vdiv(double a, double b, INTERVAL v)
|
|
{
|
|
return (hilo(a / v.hi, a / v.lo, b / v.hi, b / v.lo));
|
|
}
|