1/*	$NetBSD: calc1.y,v 1.1.1.6 2016/01/09 21:59:45 christos Exp $	*/
2
3%{
4
5/* http://dinosaur.compilertools.net/yacc/index.html */
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <ctype.h>
10#include <math.h>
11
12typedef struct interval
13{
14    double lo, hi;
15}
16INTERVAL;
17
18INTERVAL vmul(double, double, INTERVAL);
19INTERVAL vdiv(double, double, INTERVAL);
20
21extern int yylex(void);
22static void yyerror(const char *s);
23
24int dcheck(INTERVAL);
25
26double dreg[26];
27INTERVAL vreg[26];
28
29%}
30%expect 18
31
32%start line
33%union
34{
35	int ival;
36	double dval;
37	INTERVAL vval;
38}
39
40%token <ival> DREG VREG		/* indices into dreg, vreg arrays */
41%token <dval> CONST		/* floating point constant */
42
43%type <dval> dexp		/* expression */
44%type <vval> vexp		/* interval expression */
45
46	/* precedence information about the operators */
47
48%left '+' '-'
49%left '*' '/'
50%left UMINUS			/* precedence for unary minus */
51
52%%	/* beginning of rules section */
53
54lines   : /* empty */
55	| lines line
56	;
57
58line	: dexp '\n'
59	{
60		(void) printf("%15.8f\n", $1);
61	}
62	| vexp '\n'
63	{
64		(void) printf("(%15.8f, %15.8f)\n", $1.lo, $1.hi);
65	}
66	| DREG '=' dexp '\n'
67	{
68		dreg[$1] = $3;
69	}
70	| VREG '=' vexp '\n'
71	{
72		vreg[$1] = $3;
73	}
74	| error '\n'
75	{
76		yyerrok;
77	}
78	;
79
80dexp	: CONST
81	| DREG
82	{
83		$$ = dreg[$1];
84	}
85	| dexp '+' dexp
86	{
87		$$ = $1 + $3;
88	}
89	| dexp '-' dexp
90	{
91		$$ = $1 - $3;
92	}
93	| dexp '*' dexp
94	{
95		$$ = $1 * $3;
96	}
97	| dexp '/' dexp
98	{
99		$$ = $1 / $3;
100	}
101	| '-' dexp %prec UMINUS
102	{
103		$$ = -$2;
104	}
105	| '(' dexp ')'
106	{
107		$$ = $2;
108	}
109	;
110
111vexp	: dexp
112	{
113		$$.hi = $$.lo = $1;
114	}
115	| '(' dexp ',' dexp ')'
116	{
117		$$.lo = $2;
118		$$.hi = $4;
119		if ( $$.lo > $$.hi )
120		{
121			(void) printf("interval out of order\n");
122			YYERROR;
123		}
124	}
125	| VREG
126	{
127		$$ = vreg[$1];
128	}
129	| vexp '+' vexp
130	{
131		$$.hi = $1.hi + $3.hi;
132		$$.lo = $1.lo + $3.lo;
133	}
134	| dexp '+' vexp
135	{
136		$$.hi = $1 + $3.hi;
137		$$.lo = $1 + $3.lo;
138	}
139	| vexp '-' vexp
140	{
141		$$.hi = $1.hi - $3.lo;
142		$$.lo = $1.lo - $3.hi;
143	}
144	| dexp '-' vexp
145	{
146		$$.hi = $1 - $3.lo;
147		$$.lo = $1 - $3.hi;
148	}
149	| vexp '*' vexp
150	{
151		$$ = vmul( $1.lo, $1.hi, $3 );
152	}
153	| dexp '*' vexp
154	{
155		$$ = vmul ($1, $1, $3 );
156	}
157	| vexp '/' vexp
158	{
159		if (dcheck($3)) YYERROR;
160		$$ = vdiv ( $1.lo, $1.hi, $3 );
161	}
162	| dexp '/' vexp
163	{
164		if (dcheck ( $3 )) YYERROR;
165		$$ = vdiv ($1, $1, $3 );
166	}
167	| '-' vexp %prec UMINUS
168	{
169		$$.hi = -$2.lo;
170		$$.lo = -$2.hi;
171	}
172	| '(' vexp ')'
173	{
174		$$ = $2;
175	}
176	;
177
178%%	/* beginning of subroutines section */
179
180#define BSZ 50			/* buffer size for floating point numbers */
181
182	/* lexical analysis */
183
184static void
185yyerror(const char *s)
186{
187    fprintf(stderr, "%s\n", s);
188}
189
190int
191yylex(void)
192{
193    int c;
194
195    while ((c = getchar()) == ' ')
196    {				/* skip over blanks */
197    }
198
199    if (isupper(c))
200    {
201	yylval.ival = c - 'A';
202	return (VREG);
203    }
204    if (islower(c))
205    {
206	yylval.ival = c - 'a';
207	return (DREG);
208    }
209
210    if (isdigit(c) || c == '.')
211    {
212	/* gobble up digits, points, exponents */
213	char buf[BSZ + 1], *cp = buf;
214	int dot = 0, expr = 0;
215
216	for (; (cp - buf) < BSZ; ++cp, c = getchar())
217	{
218
219	    *cp = (char) c;
220	    if (isdigit(c))
221		continue;
222	    if (c == '.')
223	    {
224		if (dot++ || expr)
225		    return ('.');	/* will cause syntax error */
226		continue;
227	    }
228
229	    if (c == 'e')
230	    {
231		if (expr++)
232		    return ('e');	/*  will  cause  syntax  error  */
233		continue;
234	    }
235
236	    /*  end  of  number  */
237	    break;
238	}
239	*cp = '\0';
240
241	if ((cp - buf) >= BSZ)
242	    printf("constant  too  long:  truncated\n");
243	else
244	    ungetc(c, stdin);	/*  push  back  last  char  read  */
245	yylval.dval = atof(buf);
246	return (CONST);
247    }
248    return (c);
249}
250
251static INTERVAL
252hilo(double a, double b, double c, double d)
253{
254    /*  returns  the  smallest  interval  containing  a,  b,  c,  and  d  */
255    /*  used  by  *,  /  routines  */
256    INTERVAL v;
257
258    if (a > b)
259    {
260	v.hi = a;
261	v.lo = b;
262    }
263    else
264    {
265	v.hi = b;
266	v.lo = a;
267    }
268
269    if (c > d)
270    {
271	if (c > v.hi)
272	    v.hi = c;
273	if (d < v.lo)
274	    v.lo = d;
275    }
276    else
277    {
278	if (d > v.hi)
279	    v.hi = d;
280	if (c < v.lo)
281	    v.lo = c;
282    }
283    return (v);
284}
285
286INTERVAL
287vmul(double a, double b, INTERVAL v)
288{
289    return (hilo(a * v.hi, a * v.lo, b * v.hi, b * v.lo));
290}
291
292int
293dcheck(INTERVAL v)
294{
295    if (v.hi >= 0. && v.lo <= 0.)
296    {
297	printf("divisor  interval  contains  0.\n");
298	return (1);
299    }
300    return (0);
301}
302
303INTERVAL
304vdiv(double a, double b, INTERVAL v)
305{
306    return (hilo(a / v.hi, a / v.lo, b / v.hi, b / v.lo));
307}
308