freebsd-skq/usr.bin/yacc/tests/calc1.y
jmmv 41a8d8c295 Migrate most of tools/regression/usr.bin/ to the new tests layout.
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.
2014-03-16 08:04:06 +00:00

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));
}