|
|
1.1 ! root 1: .\" @(#)ssc 6.1 (Berkeley) 5/8/86 ! 2: .\" ! 3: .SH ! 4: Appendix C: An Advanced Example ! 5: .PP ! 6: This Appendix gives an example of a grammar using some ! 7: of the advanced features discussed in Section 10. ! 8: The desk calculator example in Appendix A is ! 9: modified to provide a desk calculator that ! 10: does floating point interval arithmetic. ! 11: The calculator understands floating point ! 12: constants, the arithmetic operations +, \-, *, /, ! 13: unary \-, and = (assignment), and has 26 floating ! 14: point variables, ``a'' through ``z''. ! 15: Moreover, it also understands ! 16: .I intervals , ! 17: written ! 18: .DS ! 19: ( x , y ) ! 20: .DE ! 21: where ! 22: .I x ! 23: is less than or equal to ! 24: .I y . ! 25: There are 26 interval valued variables ``A'' through ``Z'' ! 26: that may also be used. ! 27: The usage is similar to that in Appendix A; assignments ! 28: return no value, and print nothing, while expressions print ! 29: the (floating or interval) value. ! 30: .PP ! 31: This example explores a number of interesting features ! 32: of Yacc and C. ! 33: Intervals are represented by a structure, consisting of the ! 34: left and right endpoint values, stored as ! 35: .I double 's. ! 36: This structure is given a type name, INTERVAL, by using ! 37: .I typedef . ! 38: The Yacc value stack can also contain floating point scalars, and ! 39: integers (used to index into the arrays holding the variable values). ! 40: Notice that this entire strategy depends strongly on being able to ! 41: assign structures and unions in C. ! 42: In fact, many of the actions call functions that return structures ! 43: as well. ! 44: .PP ! 45: It is also worth noting the use of YYERROR to handle error conditions: ! 46: division by an interval containing 0, and an interval presented in ! 47: the wrong order. ! 48: In effect, the error recovery mechanism of Yacc is used to throw away the ! 49: rest of the offending line. ! 50: .PP ! 51: In addition to the mixing of types on the value stack, ! 52: this grammar also demonstrates an interesting use of syntax to ! 53: keep track of the type (e.g. scalar or interval) of intermediate ! 54: expressions. ! 55: Note that a scalar can be automatically promoted to an interval if ! 56: the context demands an interval value. ! 57: This causes a large number of conflicts when the grammar is run through ! 58: Yacc: 18 Shift/Reduce and 26 Reduce/Reduce. ! 59: The problem can be seen by looking at the two input lines: ! 60: .DS ! 61: 2.5 + ( 3.5 \- 4. ) ! 62: .DE ! 63: and ! 64: .DS ! 65: 2.5 + ( 3.5 , 4. ) ! 66: .DE ! 67: Notice that the 2.5 is to be used in an interval valued expression ! 68: in the second example, but this fact is not known until ! 69: the ``,'' is read; by this time, 2.5 is finished, and the parser cannot go back ! 70: and change its mind. ! 71: More generally, it might be necessary to look ahead an arbitrary number of ! 72: tokens to decide whether to convert a scalar to an interval. ! 73: This problem is evaded by having two rules for each binary interval ! 74: valued operator: one when the left operand is a scalar, and one when ! 75: the left operand is an interval. ! 76: In the second case, the right operand must be an interval, ! 77: so the conversion will be applied automatically. ! 78: Despite this evasion, there are still many cases where the ! 79: conversion may be applied or not, leading to the above ! 80: conflicts. ! 81: They are resolved by listing the rules that yield scalars first ! 82: in the specification file; in this way, the conflicts will ! 83: be resolved in the direction of keeping scalar ! 84: valued expressions scalar valued until they are forced to become ! 85: intervals. ! 86: .PP ! 87: This way of handling multiple types is very instructive, but not very general. ! 88: If there were many kinds of expression types, instead of just two, ! 89: the number of rules needed would increase dramatically, and the conflicts ! 90: even more dramatically. ! 91: Thus, while this example is instructive, it is better practice in a ! 92: more normal programming language environment to ! 93: keep the type information as part of the value, and not as part ! 94: of the grammar. ! 95: .PP ! 96: Finally, a word about the lexical analysis. ! 97: The only unusual feature is the treatment of floating point constants. ! 98: The C library routine ! 99: .I atof ! 100: is used to do the actual conversion from a character string ! 101: to a double precision value. ! 102: If the lexical analyzer detects an error, ! 103: it responds by returning a token that ! 104: is illegal in the grammar, provoking a syntax error ! 105: in the parser, and thence error recovery. ! 106: .DS L ! 107: ! 108: %{ ! 109: ! 110: # include <stdio.h> ! 111: # include <ctype.h> ! 112: ! 113: typedef struct interval { ! 114: double lo, hi; ! 115: } INTERVAL; ! 116: ! 117: INTERVAL vmul(), vdiv(); ! 118: ! 119: double atof(); ! 120: ! 121: double dreg[ 26 ]; ! 122: INTERVAL vreg[ 26 ]; ! 123: ! 124: %} ! 125: ! 126: %start lines ! 127: ! 128: %union { ! 129: int ival; ! 130: double dval; ! 131: INTERVAL vval; ! 132: } ! 133: ! 134: %token <ival> DREG VREG /* indices into dreg, vreg arrays */ ! 135: ! 136: %token <dval> CONST /* floating point constant */ ! 137: ! 138: %type <dval> dexp /* expression */ ! 139: ! 140: %type <vval> vexp /* interval expression */ ! 141: ! 142: /* precedence information about the operators */ ! 143: ! 144: %left \'+\' \'\-\' ! 145: %left \'*\' \'/\' ! 146: %left UMINUS /* precedence for unary minus */ ! 147: ! 148: %% ! 149: ! 150: lines : /* empty */ ! 151: | lines line ! 152: ; ! 153: ! 154: line : dexp \'\en\' ! 155: { printf( "%15.8f\en", $1 ); } ! 156: | vexp \'\en\' ! 157: { printf( "(%15.8f , %15.8f )\en", $1.lo, $1.hi ); } ! 158: | DREG \'=\' dexp \'\en\' ! 159: { dreg[$1] = $3; } ! 160: | VREG \'=\' vexp \'\en\' ! 161: { vreg[$1] = $3; } ! 162: | error \'\en\' ! 163: { yyerrok; } ! 164: ; ! 165: ! 166: dexp : CONST ! 167: | DREG ! 168: { $$ = dreg[$1]; } ! 169: | dexp \'+\' dexp ! 170: { $$ = $1 + $3; } ! 171: | dexp \'\-\' dexp ! 172: { $$ = $1 \- $3; } ! 173: | dexp \'*\' dexp ! 174: { $$ = $1 * $3; } ! 175: | dexp \'/\' dexp ! 176: { $$ = $1 / $3; } ! 177: | \'\-\' dexp %prec UMINUS ! 178: { $$ = \- $2; } ! 179: | \'(\' dexp \')\' ! 180: { $$ = $2; } ! 181: ; ! 182: ! 183: vexp : dexp ! 184: { $$.hi = $$.lo = $1; } ! 185: | \'(\' dexp \',\' dexp \')\' ! 186: { ! 187: $$.lo = $2; ! 188: $$.hi = $4; ! 189: if( $$.lo > $$.hi ){ ! 190: printf( "interval out of order\en" ); ! 191: YYERROR; ! 192: } ! 193: } ! 194: | VREG ! 195: { $$ = vreg[$1]; } ! 196: | vexp \'+\' vexp ! 197: { $$.hi = $1.hi + $3.hi; ! 198: $$.lo = $1.lo + $3.lo; } ! 199: | dexp \'+\' vexp ! 200: { $$.hi = $1 + $3.hi; ! 201: $$.lo = $1 + $3.lo; } ! 202: | vexp \'\-\' vexp ! 203: { $$.hi = $1.hi \- $3.lo; ! 204: $$.lo = $1.lo \- $3.hi; } ! 205: | dexp \'\-\' vexp ! 206: { $$.hi = $1 \- $3.lo; ! 207: $$.lo = $1 \- $3.hi; } ! 208: | vexp \'*\' vexp ! 209: { $$ = vmul( $1.lo, $1.hi, $3 ); } ! 210: | dexp \'*\' vexp ! 211: { $$ = vmul( $1, $1, $3 ); } ! 212: | vexp \'/\' vexp ! 213: { if( dcheck( $3 ) ) YYERROR; ! 214: $$ = vdiv( $1.lo, $1.hi, $3 ); } ! 215: | dexp \'/\' vexp ! 216: { if( dcheck( $3 ) ) YYERROR; ! 217: $$ = vdiv( $1, $1, $3 ); } ! 218: | \'\-\' vexp %prec UMINUS ! 219: { $$.hi = \-$2.lo; $$.lo = \-$2.hi; } ! 220: | \'(\' vexp \')\' ! 221: { $$ = $2; } ! 222: ; ! 223: ! 224: %% ! 225: ! 226: # define BSZ 50 /* buffer size for floating point numbers */ ! 227: ! 228: /* lexical analysis */ ! 229: ! 230: yylex(){ ! 231: register c; ! 232: ! 233: while( (c=getchar()) == \' \' ){ /* skip over blanks */ } ! 234: ! 235: if( isupper( c ) ){ ! 236: yylval.ival = c \- \'A\'; ! 237: return( VREG ); ! 238: } ! 239: if( islower( c ) ){ ! 240: yylval.ival = c \- \'a\'; ! 241: return( DREG ); ! 242: } ! 243: ! 244: if( isdigit( c ) || c==\'.\' ){ ! 245: /* gobble up digits, points, exponents */ ! 246: ! 247: char buf[BSZ+1], *cp = buf; ! 248: int dot = 0, exp = 0; ! 249: ! 250: for( ; (cp\-buf)<BSZ ; ++cp,c=getchar() ){ ! 251: ! 252: *cp = c; ! 253: if( isdigit( c ) ) continue; ! 254: if( c == \'.\' ){ ! 255: if( dot++ || exp ) return( \'.\' ); /* will cause syntax error */ ! 256: continue; ! 257: } ! 258: ! 259: if( c == \'e\' ){ ! 260: if( exp++ ) return( \'e\' ); /* will cause syntax error */ ! 261: continue; ! 262: } ! 263: ! 264: /* end of number */ ! 265: break; ! 266: } ! 267: *cp = \'\e0\'; ! 268: if( (cp\-buf) >= BSZ ) printf( "constant too long: truncated\en" ); ! 269: else ungetc( c, stdin ); /* push back last char read */ ! 270: yylval.dval = atof( buf ); ! 271: return( CONST ); ! 272: } ! 273: return( c ); ! 274: } ! 275: ! 276: INTERVAL hilo( a, b, c, d ) double a, b, c, d; { ! 277: /* returns the smallest interval containing a, b, c, and d */ ! 278: /* used by *, / routines */ ! 279: INTERVAL v; ! 280: ! 281: if( a>b ) { v.hi = a; v.lo = b; } ! 282: else { v.hi = b; v.lo = a; } ! 283: ! 284: if( c>d ) { ! 285: if( c>v.hi ) v.hi = c; ! 286: if( d<v.lo ) v.lo = d; ! 287: } ! 288: else { ! 289: if( d>v.hi ) v.hi = d; ! 290: if( c<v.lo ) v.lo = c; ! 291: } ! 292: return( v ); ! 293: } ! 294: ! 295: INTERVAL vmul( a, b, v ) double a, b; INTERVAL v; { ! 296: return( hilo( a*v.hi, a*v.lo, b*v.hi, b*v.lo ) ); ! 297: } ! 298: ! 299: dcheck( v ) INTERVAL v; { ! 300: if( v.hi >= 0. && v.lo <= 0. ){ ! 301: printf( "divisor interval contains 0.\en" ); ! 302: return( 1 ); ! 303: } ! 304: return( 0 ); ! 305: } ! 306: ! 307: INTERVAL vdiv( a, b, v ) double a, b; INTERVAL v; { ! 308: return( hilo( a/v.hi, a/v.lo, b/v.hi, b/v.lo ) ); ! 309: } ! 310: .DE ! 311: .bp
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.