1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
|
.\" $OpenBSD: ssc,v 1.1 2002/12/03 21:36:35 mickey Exp $
.\"
.\" Copyright (C) Caldera International Inc. 2001-2002.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code and documentation must retain the above
.\" copyright notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed or owned by Caldera
.\" International, Inc.
.\" 4. Neither the name of Caldera International, Inc. nor the names of other
.\" contributors may be used to endorse or promote products derived from
.\" this software without specific prior written permission.
.\"
.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.\" @(#)ssc 8.1 (Berkeley) 8/14/93
.\"
.SH
Appendix C: An Advanced Example
.PP
This Appendix gives an example of a grammar using some
of the advanced features discussed in Section 10.
The desk calculator example in Appendix A is
modified to provide a desk calculator that
does floating point interval arithmetic.
The calculator understands floating point
constants, the arithmetic operations +, \-, *, /,
unary \-, and = (assignment), and has 26 floating
point variables, ``a'' through ``z''.
Moreover, it also understands
.I intervals ,
written
.DS
( x , y )
.DE
where
.I x
is less than or equal to
.I y .
There are 26 interval valued variables ``A'' through ``Z''
that may also be used.
The usage is similar to that in Appendix A; assignments
return no value, and print nothing, while expressions print
the (floating or interval) value.
.PP
This example explores a number of interesting features
of Yacc and C.
Intervals are represented by a structure, consisting of the
left and right endpoint values, stored as
.I double 's.
This structure is given a type name, INTERVAL, by using
.I typedef .
The Yacc value stack can also contain floating point scalars, and
integers (used to index into the arrays holding the variable values).
Notice that this entire strategy depends strongly on being able to
assign structures and unions in C.
In fact, many of the actions call functions that return structures
as well.
.PP
It is also worth noting the use of YYERROR to handle error conditions:
division by an interval containing 0, and an interval presented in
the wrong order.
In effect, the error recovery mechanism of Yacc is used to throw away the
rest of the offending line.
.PP
In addition to the mixing of types on the value stack,
this grammar also demonstrates an interesting use of syntax to
keep track of the type (e.g. scalar or interval) of intermediate
expressions.
Note that a scalar can be automatically promoted to an interval if
the context demands an interval value.
This causes a large number of conflicts when the grammar is run through
Yacc: 18 Shift/Reduce and 26 Reduce/Reduce.
The problem can be seen by looking at the two input lines:
.DS
2.5 + ( 3.5 \- 4. )
.DE
and
.DS
2.5 + ( 3.5 , 4. )
.DE
Notice that the 2.5 is to be used in an interval valued expression
in the second example, but this fact is not known until
the ``,'' is read; by this time, 2.5 is finished, and the parser cannot go back
and change its mind.
More generally, it might be necessary to look ahead an arbitrary number of
tokens to decide whether to convert a scalar to an interval.
This problem is evaded by having two rules for each binary interval
valued operator: one when the left operand is a scalar, and one when
the left operand is an interval.
In the second case, the right operand must be an interval,
so the conversion will be applied automatically.
Despite this evasion, there are still many cases where the
conversion may be applied or not, leading to the above
conflicts.
They are resolved by listing the rules that yield scalars first
in the specification file; in this way, the conflicts will
be resolved in the direction of keeping scalar
valued expressions scalar valued until they are forced to become
intervals.
.PP
This way of handling multiple types is very instructive, but not very general.
If there were many kinds of expression types, instead of just two,
the number of rules needed would increase dramatically, and the conflicts
even more dramatically.
Thus, while this example is instructive, it is better practice in a
more normal programming language environment to
keep the type information as part of the value, and not as part
of the grammar.
.PP
Finally, a word about the lexical analysis.
The only unusual feature is the treatment of floating point constants.
The C library routine
.I atof
is used to do the actual conversion from a character string
to a double precision value.
If the lexical analyzer detects an error,
it responds by returning a token that
is illegal in the grammar, provoking a syntax error
in the parser, and thence error recovery.
.LD
%{
# include <stdio.h>
# include <ctype.h>
typedef struct interval {
double lo, hi;
} INTERVAL;
INTERVAL vmul(), vdiv();
double atof();
double dreg[ 26 ];
INTERVAL vreg[ 26 ];
%}
%start lines
%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 */
%%
lines : /* empty */
| lines line
;
line : dexp \'\en\'
{ printf( "%15.8f\en", $1 ); }
| vexp \'\en\'
{ printf( "(%15.8f , %15.8f )\en", $1.lo, $1.hi ); }
| DREG \'=\' dexp \'\en\'
{ dreg[$1] = $3; }
| VREG \'=\' vexp \'\en\'
{ vreg[$1] = $3; }
| error \'\en\'
{ 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 ){
printf( "interval out of order\en" );
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; }
;
%%
# define BSZ 50 /* buffer size for floating point numbers */
/* lexical analysis */
yylex(){
register 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, exp = 0;
for( ; (cp\-buf)<BSZ ; ++cp,c=getchar() ){
*cp = c;
if( isdigit( c ) ) continue;
if( c == \'.\' ){
if( dot++ || exp ) return( \'.\' ); /* will cause syntax error */
continue;
}
if( c == \'e\' ){
if( exp++ ) return( \'e\' ); /* will cause syntax error */
continue;
}
/* end of number */
break;
}
*cp = \'\e0\';
if( (cp\-buf) >= BSZ ) printf( "constant too long: truncated\en" );
else ungetc( c, stdin ); /* push back last char read */
yylval.dval = atof( buf );
return( CONST );
}
return( c );
}
INTERVAL hilo( a, b, c, d ) double a, b, c, 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( a, b, v ) double a, b; INTERVAL v; {
return( hilo( a*v.hi, a*v.lo, b*v.hi, b*v.lo ) );
}
dcheck( v ) INTERVAL v; {
if( v.hi >= 0. && v.lo <= 0. ){
printf( "divisor interval contains 0.\en" );
return( 1 );
}
return( 0 );
}
INTERVAL vdiv( a, b, v ) double a, b; INTERVAL v; {
return( hilo( a/v.hi, a/v.lo, b/v.hi, b/v.lo ) );
}
.DE
.bp
|