41a8d8c295
I'm starting with the easy cases. The leftovers need to be looked at a bit more closely. Note that this change _does_ modify the code of the old tests. This is required in order to allow the code to locate the data files in the source directory instead of the current directory, because Kyua automatically changes the latter to a temporary directory. Also note that at least one test is known to be broken here. Actually, the test is not really broken: it's marked as a TODO but unfortunately Kyua's TAP parser currently does not understand that. Will have to be fixed separately.
306 lines
4.2 KiB
Plaintext
306 lines
4.2 KiB
Plaintext
%{
|
|
|
|
/* 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);
|
|
|
|
extern int yylex(void);
|
|
static void yyerror(const char *s);
|
|
|
|
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 */
|
|
|
|
#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));
|
|
}
|