Home | History | Annotate | Line # | Download | only in m4
expr.c revision 1.7
      1 /*	$NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1989, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Ozan Yigit at York University.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "@(#)expr.c	8.2 (Berkeley) 4/29/95";
     42 #else
     43 static char rcsid[] = "$NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls Exp $";
     44 #endif
     45 #endif /* not lint */
     46 
     47 #include <sys/cdefs.h>
     48 #include <stdio.h>
     49 
     50 /*
     51  *      expression evaluator: performs a standard recursive
     52  *      descent parse to evaluate any expression permissible
     53  *      within the following grammar:
     54  *
     55  *      expr    :       query EOS
     56  *      query   :       lor
     57  *              |       lor "?" query ":" query
     58  *      lor     :       land { "||" land }
     59  *      land    :       not { "&&" not }
     60  *	not	:	eqrel
     61  *		|	'!' not
     62  *      eqrel   :       shift { eqrelop shift }
     63  *      shift   :       primary { shop primary }
     64  *      primary :       term { addop term }
     65  *      term    :       exp { mulop exp }
     66  *	exp	:	unary { expop unary }
     67  *      unary   :       factor
     68  *              |       unop unary
     69  *      factor  :       constant
     70  *              |       "(" query ")"
     71  *      constant:       num
     72  *              |       "'" CHAR "'"
     73  *      num     :       DIGIT
     74  *              |       DIGIT num
     75  *      shop    :       "<<"
     76  *              |       ">>"
     77  *      eqrel   :       "="
     78  *              |       "=="
     79  *              |       "!="
     80  *      	|       "<"
     81  *              |       ">"
     82  *              |       "<="
     83  *              |       ">="
     84  *
     85  *
     86  *      This expression evaluator is lifted from a public-domain
     87  *      C Pre-Processor included with the DECUS C Compiler distribution.
     88  *      It is hacked somewhat to be suitable for m4.
     89  *
     90  *      Originally by:  Mike Lutz
     91  *                      Bob Harper
     92  */
     93 
     94 #define TRUE    1
     95 #define FALSE   0
     96 #define EOS     (char) 0
     97 #define EQL     0
     98 #define NEQ     1
     99 #define LSS     2
    100 #define LEQ     3
    101 #define GTR     4
    102 #define GEQ     5
    103 #define OCTAL   8
    104 #define DECIMAL 10
    105 
    106 static char *nxtch;		       /* Parser scan pointer */
    107 
    108 static int query __P((void));
    109 static int lor __P((void));
    110 static int land __P((void));
    111 static int not __P((void));
    112 static int eqrel __P((void));
    113 static int shift __P((void));
    114 static int primary __P((void));
    115 static int term __P((void));
    116 static int exp __P((void));
    117 static int unary __P((void));
    118 static int factor __P((void));
    119 static int constant __P((void));
    120 static int num __P((void));
    121 static int geteqrel __P((void));
    122 static int skipws __P((void));
    123 static void experr __P((char *));
    124 
    125 /*
    126  * For longjmp
    127  */
    128 #include <setjmp.h>
    129 static jmp_buf expjump;
    130 
    131 /*
    132  * macros:
    133  *      ungetch - Put back the last character examined.
    134  *      getch   - return the next character from expr string.
    135  */
    136 #define ungetch()       nxtch--
    137 #define getch()         *nxtch++
    138 
    139 int
    140 expr(expbuf)
    141 char *expbuf;
    142 {
    143 	register int rval;
    144 
    145 	nxtch = expbuf;
    146 	if (setjmp(expjump) != 0)
    147 		return FALSE;
    148 
    149 	rval = query();
    150 	if (skipws() == EOS)
    151 		return rval;
    152 
    153 	printf("m4: ill-formed expression.\n");
    154 	return FALSE;
    155 }
    156 
    157 /*
    158  * query : lor | lor '?' query ':' query
    159  */
    160 static int
    161 query()
    162 {
    163 	register int bool, true_val, false_val;
    164 
    165 	bool = lor();
    166 	if (skipws() != '?') {
    167 		ungetch();
    168 		return bool;
    169 	}
    170 
    171 	true_val = query();
    172 	if (skipws() != ':')
    173 		experr("bad query");
    174 
    175 	false_val = query();
    176 	return bool ? true_val : false_val;
    177 }
    178 
    179 /*
    180  * lor : land { '||' land }
    181  */
    182 static int
    183 lor()
    184 {
    185 	register int c, vl, vr;
    186 
    187 	vl = land();
    188 	while ((c = skipws()) == '|') {
    189 		if (getch() != '|')
    190 			ungetch();
    191 		vr = land();
    192 		vl = vl || vr;
    193 	}
    194 
    195 	ungetch();
    196 	return vl;
    197 }
    198 
    199 /*
    200  * land : not { '&&' not }
    201  */
    202 static int
    203 land()
    204 {
    205 	register int c, vl, vr;
    206 
    207 	vl = not();
    208 	while ((c = skipws()) == '&') {
    209 		if (getch() != '&')
    210 			ungetch();
    211 		vr = not();
    212 		vl = vl && vr;
    213 	}
    214 
    215 	ungetch();
    216 	return vl;
    217 }
    218 
    219 /*
    220  * not : eqrel | '!' not
    221  */
    222 static int
    223 not()
    224 {
    225 	register int val, c;
    226 
    227 	if ((c = skipws()) == '!' && getch() != '=') {
    228 		ungetch();
    229 		val = not();
    230 		return !val;
    231 	}
    232 
    233 	if (c == '!')
    234 		ungetch();
    235 	ungetch();
    236 	return eqrel();
    237 }
    238 
    239 /*
    240  * eqrel : shift { eqrelop shift }
    241  */
    242 static int
    243 eqrel()
    244 {
    245 	register int vl, vr, eqrel;
    246 
    247 	vl = shift();
    248 	while ((eqrel = geteqrel()) != -1) {
    249 		vr = shift();
    250 
    251 		switch (eqrel) {
    252 
    253 		case EQL:
    254 			vl = (vl == vr);
    255 			break;
    256 		case NEQ:
    257 			vl = (vl != vr);
    258 			break;
    259 
    260 		case LEQ:
    261 			vl = (vl <= vr);
    262 			break;
    263 		case LSS:
    264 			vl = (vl < vr);
    265 			break;
    266 		case GTR:
    267 			vl = (vl > vr);
    268 			break;
    269 		case GEQ:
    270 			vl = (vl >= vr);
    271 			break;
    272 		}
    273 	}
    274 	return vl;
    275 }
    276 
    277 /*
    278  * shift : primary { shop primary }
    279  */
    280 static int
    281 shift()
    282 {
    283 	register int vl, vr, c;
    284 
    285 	vl = primary();
    286 	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
    287 		vr = primary();
    288 
    289 		if (c == '<')
    290 			vl <<= vr;
    291 		else
    292 			vl >>= vr;
    293 	}
    294 
    295 	if (c == '<' || c == '>')
    296 		ungetch();
    297 	ungetch();
    298 	return vl;
    299 }
    300 
    301 /*
    302  * primary : term { addop term }
    303  */
    304 static int
    305 primary()
    306 {
    307 	register int c, vl, vr;
    308 
    309 	vl = term();
    310 	while ((c = skipws()) == '+' || c == '-') {
    311 		vr = term();
    312 
    313 		if (c == '+')
    314 			vl += vr;
    315 		else
    316 			vl -= vr;
    317 	}
    318 
    319 	ungetch();
    320 	return vl;
    321 }
    322 
    323 /*
    324  * <term> := <exp> { <mulop> <exp> }
    325  */
    326 static int
    327 term()
    328 {
    329 	register int c, vl, vr;
    330 
    331 	vl = exp();
    332 	while ((c = skipws()) == '*' || c == '/' || c == '%') {
    333 		vr = exp();
    334 
    335 		switch (c) {
    336 		case '*':
    337 			vl *= vr;
    338 			break;
    339 		case '/':
    340 			vl /= vr;
    341 			break;
    342 		case '%':
    343 			vl %= vr;
    344 			break;
    345 		}
    346 	}
    347 	ungetch();
    348 	return vl;
    349 }
    350 
    351 /*
    352  * <term> := <unary> { <expop> <unary> }
    353  */
    354 static int
    355 exp()
    356 {
    357 	register c, vl, vr, n;
    358 
    359 	vl = unary();
    360 	switch (c = skipws()) {
    361 
    362 	case '*':
    363 		if (getch() != '*') {
    364 			ungetch();
    365 			break;
    366 		}
    367 
    368 	case '^':
    369 		vr = exp();
    370 		n = 1;
    371 		while (vr-- > 0)
    372 			n *= vl;
    373 		return n;
    374 	}
    375 
    376 	ungetch();
    377 	return vl;
    378 }
    379 
    380 /*
    381  * unary : factor | unop unary
    382  */
    383 static int
    384 unary()
    385 {
    386 	register int val, c;
    387 
    388 	if ((c = skipws()) == '+' || c == '-' || c == '~') {
    389 		val = unary();
    390 
    391 		switch (c) {
    392 		case '+':
    393 			return val;
    394 		case '-':
    395 			return -val;
    396 		case '~':
    397 			return ~val;
    398 		}
    399 	}
    400 
    401 	ungetch();
    402 	return factor();
    403 }
    404 
    405 /*
    406  * factor : constant | '(' query ')'
    407  */
    408 static int
    409 factor()
    410 {
    411 	register int val;
    412 
    413 	if (skipws() == '(') {
    414 		val = query();
    415 		if (skipws() != ')')
    416 			experr("bad factor");
    417 		return val;
    418 	}
    419 
    420 	ungetch();
    421 	return constant();
    422 }
    423 
    424 /*
    425  * constant: num | 'char'
    426  * Note: constant() handles multi-byte constants
    427  */
    428 static int
    429 constant()
    430 {
    431 	register int i;
    432 	register int value;
    433 	register char c;
    434 	int v[sizeof(int)];
    435 
    436 	if (skipws() != '\'') {
    437 		ungetch();
    438 		return num();
    439 	}
    440 	for (i = 0; i < sizeof(int); i++) {
    441 		if ((c = getch()) == '\'') {
    442 			ungetch();
    443 			break;
    444 		}
    445 		if (c == '\\') {
    446 			switch (c = getch()) {
    447 			case '0':
    448 			case '1':
    449 			case '2':
    450 			case '3':
    451 			case '4':
    452 			case '5':
    453 			case '6':
    454 			case '7':
    455 				ungetch();
    456 				c = num();
    457 				break;
    458 			case 'n':
    459 				c = 012;
    460 				break;
    461 			case 'r':
    462 				c = 015;
    463 				break;
    464 			case 't':
    465 				c = 011;
    466 				break;
    467 			case 'b':
    468 				c = 010;
    469 				break;
    470 			case 'f':
    471 				c = 014;
    472 				break;
    473 			}
    474 		}
    475 		v[i] = c;
    476 	}
    477 	if (i == 0 || getch() != '\'')
    478 		experr("illegal character constant");
    479 	for (value = 0; --i >= 0;) {
    480 		value <<= 8;
    481 		value += v[i];
    482 	}
    483 	return value;
    484 }
    485 
    486 /*
    487  * num : digit | num digit
    488  */
    489 static int
    490 num()
    491 {
    492 	register int rval, c, base;
    493 	int ndig;
    494 
    495 	base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
    496 	rval = 0;
    497 	ndig = 0;
    498 	while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) {
    499 		rval *= base;
    500 		rval += (c - '0');
    501 		c = getch();
    502 		ndig++;
    503 	}
    504 	ungetch();
    505 
    506 	if (ndig == 0)
    507 		experr("bad constant");
    508 
    509 	return rval;
    510 
    511 }
    512 
    513 /*
    514  * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
    515  */
    516 static int
    517 geteqrel()
    518 {
    519 	register int c1, c2;
    520 
    521 	c1 = skipws();
    522 	c2 = getch();
    523 
    524 	switch (c1) {
    525 
    526 	case '=':
    527 		if (c2 != '=')
    528 			ungetch();
    529 		return EQL;
    530 
    531 	case '!':
    532 		if (c2 == '=')
    533 			return NEQ;
    534 		ungetch();
    535 		ungetch();
    536 		return -1;
    537 
    538 	case '<':
    539 		if (c2 == '=')
    540 			return LEQ;
    541 		ungetch();
    542 		return LSS;
    543 
    544 	case '>':
    545 		if (c2 == '=')
    546 			return GEQ;
    547 		ungetch();
    548 		return GTR;
    549 
    550 	default:
    551 		ungetch();
    552 		ungetch();
    553 		return -1;
    554 	}
    555 }
    556 
    557 /*
    558  * Skip over any white space and return terminating char.
    559  */
    560 static int
    561 skipws()
    562 {
    563 	register char c;
    564 
    565 	while ((c = getch()) <= ' ' && c > EOS)
    566 		;
    567 	return c;
    568 }
    569 
    570 /*
    571  * resets environment to eval(), prints an error
    572  * and forces eval to return FALSE.
    573  */
    574 static void
    575 experr(msg)
    576 char *msg;
    577 {
    578 	printf("m4: %s in expr.\n", msg);
    579 	longjmp(expjump, -1);
    580 }
    581