Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/planix-v0/sys/src/ape/cmd/pdksh/expr.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * Korn expression evaluation
3
 */
4
/*
5
 * todo: better error handling: if in builtin, should be builtin error, etc.
6
 */
7
 
8
#include "sh.h"
9
#include <ctype.h>
10
 
11
 
12
/* The order of these enums is constrained by the order of opinfo[] */
13
enum token {
14
	/* some (long) unary operators */
15
	O_PLUSPLUS = 0, O_MINUSMINUS,
16
	/* binary operators */
17
	O_EQ, O_NE,
18
	/* assignments are assumed to be in range O_ASN .. O_BORASN */
19
	O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
20
	       O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
21
	O_LSHIFT, O_RSHIFT,
22
	O_LE, O_GE, O_LT, O_GT,
23
	O_LAND,
24
	O_LOR,
25
	O_TIMES, O_DIV, O_MOD,
26
	O_PLUS, O_MINUS,
27
	O_BAND,
28
	O_BXOR,
29
	O_BOR,
30
	O_TERN,
31
	O_COMMA,
32
	/* things after this aren't used as binary operators */
33
	/* unary that are not also binaries */
34
	O_BNOT, O_LNOT,
35
	/* misc */
36
	OPEN_PAREN, CLOSE_PAREN, CTERN,
37
	/* things that don't appear in the opinfo[] table */
38
	VAR, LIT, END, BAD
39
    };
40
#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
41
#define IS_ASSIGNOP(op)	((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
42
 
43
enum prec {
44
	P_PRIMARY = 0,		/* VAR, LIT, (), ~ ! - + */
45
	P_MULT,			/* * / % */
46
	P_ADD,			/* + - */
47
	P_SHIFT,		/* << >> */
48
	P_RELATION,		/* < <= > >= */
49
	P_EQUALITY,		/* == != */
50
	P_BAND,			/* & */
51
	P_BXOR,			/* ^ */
52
	P_BOR,			/* | */
53
	P_LAND,			/* && */
54
	P_LOR,			/* || */
55
	P_TERN,			/* ?: */
56
	P_ASSIGN,		/* = *= /= %= += -= <<= >>= &= ^= |= */
57
	P_COMMA			/* , */
58
    };
59
#define MAX_PREC	P_COMMA
60
 
61
struct opinfo {
62
	char		name[4];
63
	int		len;	/* name length */
64
	enum prec	prec;	/* precidence: lower is higher */
65
};
66
 
67
/* Tokens in this table must be ordered so the longest are first
68
 * (eg, += before +).  If you change something, change the order
69
 * of enum token too.
70
 */
71
static const struct opinfo opinfo[] = {
72
		{ "++",	 2, P_PRIMARY },	/* before + */
73
		{ "--",	 2, P_PRIMARY },	/* before - */
74
		{ "==",	 2, P_EQUALITY },	/* before = */
75
		{ "!=",	 2, P_EQUALITY },	/* before ! */
76
		{ "=",	 1, P_ASSIGN },		/* keep assigns in a block */
77
		{ "*=",	 2, P_ASSIGN },
78
		{ "/=",	 2, P_ASSIGN },
79
		{ "%=",	 2, P_ASSIGN },
80
		{ "+=",	 2, P_ASSIGN },
81
		{ "-=",	 2, P_ASSIGN },
82
		{ "<<=", 3, P_ASSIGN },
83
		{ ">>=", 3, P_ASSIGN },
84
		{ "&=",	 2, P_ASSIGN },
85
		{ "^=",	 2, P_ASSIGN },
86
		{ "|=",	 2, P_ASSIGN },
87
		{ "<<",	 2, P_SHIFT },
88
		{ ">>",	 2, P_SHIFT },
89
		{ "<=",	 2, P_RELATION },
90
		{ ">=",	 2, P_RELATION },
91
		{ "<",	 1, P_RELATION },
92
		{ ">",	 1, P_RELATION },
93
		{ "&&",	 2, P_LAND },
94
		{ "||",	 2, P_LOR },
95
		{ "*",	 1, P_MULT },
96
		{ "/",	 1, P_MULT },
97
		{ "%",	 1, P_MULT },
98
		{ "+",	 1, P_ADD },
99
		{ "-",	 1, P_ADD },
100
		{ "&",	 1, P_BAND },
101
		{ "^",	 1, P_BXOR },
102
		{ "|",	 1, P_BOR },
103
		{ "?",	 1, P_TERN },
104
		{ ",",	 1, P_COMMA },
105
		{ "~",	 1, P_PRIMARY },
106
		{ "!",	 1, P_PRIMARY },
107
		{ "(",	 1, P_PRIMARY },
108
		{ ")",	 1, P_PRIMARY },
109
		{ ":",	 1, P_PRIMARY },
110
		{ "",	 0, P_PRIMARY } /* end of table */
111
	    };
112
 
113
 
114
typedef struct expr_state Expr_state;
115
struct expr_state {
116
	const char *expression;		/* expression being evaluated */
117
	const char *tokp;		/* lexical position */
118
	enum token  tok;		/* token from token() */
119
	int	    noassign;		/* don't do assigns (for ?:,&&,||) */
120
	struct tbl *val;		/* value from token() */
121
	struct tbl *evaling;		/* variable that is being recursively
122
					 * expanded (EXPRINEVAL flag set)
123
					 */
124
};
125
 
126
enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
127
		  ET_LVALUE, ET_RDONLY, ET_STR };
128
 
129
static void        evalerr  ARGS((Expr_state *es, enum error_type type,
130
				  const char *str)) GCC_FUNC_ATTR(noreturn);
131
static struct tbl *evalexpr ARGS((Expr_state *es, enum prec prec));
132
static void        token    ARGS((Expr_state *es));
133
static struct tbl *do_ppmm  ARGS((Expr_state *es, enum token op,
134
				  struct tbl *vasn, bool_t is_prefix));
135
static void	   assign_check ARGS((Expr_state *es, enum token op,
136
				      struct tbl *vasn));
137
static struct tbl *tempvar  ARGS((void));
138
static struct tbl *intvar   ARGS((Expr_state *es, struct tbl *vp));
139
 
140
/*
141
 * parse and evalute expression
142
 */
143
int
144
evaluate(expr, rval, error_ok)
145
	const char *expr;
146
	long *rval;
147
	int error_ok;
148
{
149
	struct tbl v;
150
	int ret;
151
 
152
	v.flag = DEFINED|INTEGER;
153
	v.type = 0;
154
	ret = v_evaluate(&v, expr, error_ok);
155
	*rval = v.val.i;
156
	return ret;
157
}
158
 
159
/*
160
 * parse and evalute expression, storing result in vp.
161
 */
162
int
163
v_evaluate(vp, expr, error_ok)
164
	struct tbl *vp;
165
	const char *expr;
166
	volatile int error_ok;
167
{
168
	struct tbl *v;
169
	Expr_state curstate;
170
	Expr_state * const es = &curstate;
171
	int i;
172
 
173
	/* save state to allow recursive calls */
174
	curstate.expression = curstate.tokp = expr;
175
	curstate.noassign = 0;
176
	curstate.evaling = (struct tbl *) 0;
177
 
178
	newenv(E_ERRH);
179
	i = ksh_sigsetjmp(e->jbuf, 0);
180
	if (i) {
181
		/* Clear EXPRINEVAL in of any variables we were playing with */
182
		if (curstate.evaling)
183
			curstate.evaling->flag &= ~EXPRINEVAL;
184
		quitenv();
185
		if (i == LAEXPR) {
186
			if (error_ok == KSH_RETURN_ERROR)
187
				return 0;
188
			errorf(null);
189
		}
190
		unwind(i);
191
		/*NOTREACHED*/
192
	}
193
 
194
	token(es);
195
#if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
196
	if (es->tok == END) {
197
		es->tok = LIT;
198
		es->val = tempvar();
199
	}
200
#endif /* 0 */
201
	v = intvar(es, evalexpr(es, MAX_PREC));
202
 
203
	if (es->tok != END)
204
		evalerr(es, ET_UNEXPECTED, (char *) 0);
205
 
206
	if (vp->flag & INTEGER)
207
		setint_v(vp, v);
208
	else
209
		/* can fail if readony */
210
		setstr(vp, str_val(v), error_ok);
211
 
212
	quitenv();
213
 
214
	return 1;
215
}
216
 
217
static void
218
evalerr(es, type, str)
219
	Expr_state *es;
220
	enum error_type type;
221
	const char *str;
222
{
223
	char tbuf[2];
224
	const char *s;
225
 
226
	switch (type) {
227
	case ET_UNEXPECTED:
228
		switch (es->tok) {
229
		case VAR:
230
			s = es->val->name;
231
			break;
232
		case LIT:
233
			s = str_val(es->val);
234
			break;
235
		case END:
236
			s = "end of expression";
237
			break;
238
		case BAD:
239
			tbuf[0] = *es->tokp;
240
			tbuf[1] = '\0';
241
			s = tbuf;
242
			break;
243
		default:
244
			s = opinfo[(int)es->tok].name;
245
		}
246
		warningf(TRUE, "%s: unexpected `%s'", es->expression, s);
247
		break;
248
 
249
	case ET_BADLIT:
250
		warningf(TRUE, "%s: bad number `%s'", es->expression, str);
251
		break;
252
 
253
	case ET_RECURSIVE:
254
		warningf(TRUE, "%s: expression recurses on parameter `%s'",
255
			es->expression, str);
256
		break;
257
 
258
	case ET_LVALUE:
259
		warningf(TRUE, "%s: %s requires lvalue",
260
			es->expression, str);
261
		break;
262
 
263
	case ET_RDONLY:
264
		warningf(TRUE, "%s: %s applied to read only variable",
265
			es->expression, str);
266
		break;
267
 
268
	default: /* keep gcc happy */
269
	case ET_STR:
270
		warningf(TRUE, "%s: %s", es->expression, str);
271
		break;
272
	}
273
	unwind(LAEXPR);
274
}
275
 
276
static struct tbl *
277
evalexpr(es, prec)
278
	Expr_state *es;
279
	enum prec prec;
280
{
281
	struct tbl *vl, UNINITIALIZED(*vr), *vasn;
282
	enum token op;
283
	long UNINITIALIZED(res);
284
 
285
	if (prec == P_PRIMARY) {
286
		op = es->tok;
287
		if (op == O_BNOT || op == O_LNOT || op == O_MINUS
288
		    || op == O_PLUS)
289
		{
290
			token(es);
291
			vl = intvar(es, evalexpr(es, P_PRIMARY));
292
			if (op == O_BNOT)
293
				vl->val.i = ~vl->val.i;
294
			else if (op == O_LNOT)
295
				vl->val.i = !vl->val.i;
296
			else if (op == O_MINUS)
297
				vl->val.i = -vl->val.i;
298
			/* op == O_PLUS is a no-op */
299
		} else if (op == OPEN_PAREN) {
300
			token(es);
301
			vl = evalexpr(es, MAX_PREC);
302
			if (es->tok != CLOSE_PAREN)
303
				evalerr(es, ET_STR, "missing )");
304
			token(es);
305
		} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
306
			token(es);
307
			vl = do_ppmm(es, op, es->val, TRUE);
308
			token(es);
309
		} else if (op == VAR || op == LIT) {
310
			vl = es->val;
311
			token(es);
312
		} else {
313
			evalerr(es, ET_UNEXPECTED, (char *) 0);
314
			/*NOTREACHED*/
315
		}
316
		if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
317
			vl = do_ppmm(es, es->tok, vl, FALSE);
318
			token(es);
319
		}
320
		return vl;
321
	}
322
	vl = evalexpr(es, ((int) prec) - 1);
323
	for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
324
		op = es->tok)
325
	{
326
		token(es);
327
		vasn = vl;
328
		if (op != O_ASN) /* vl may not have a value yet */
329
			vl = intvar(es, vl);
330
		if (IS_ASSIGNOP(op)) {
331
			assign_check(es, op, vasn);
332
			vr = intvar(es, evalexpr(es, P_ASSIGN));
333
		} else if (op != O_TERN && op != O_LAND && op != O_LOR)
334
			vr = intvar(es, evalexpr(es, ((int) prec) - 1));
335
		if ((op == O_DIV || op == O_MOD || op == O_DIVASN
336
		     || op == O_MODASN) && vr->val.i == 0)
337
		{
338
			if (es->noassign)
339
				vr->val.i = 1;
340
			else
341
				evalerr(es, ET_STR, "zero divisor");
342
		}
343
		switch ((int) op) {
344
		case O_TIMES:
345
		case O_TIMESASN:
346
			res = vl->val.i * vr->val.i;
347
			break;
348
		case O_DIV:
349
		case O_DIVASN:
350
			res = vl->val.i / vr->val.i;
351
			break;
352
		case O_MOD:
353
		case O_MODASN:
354
			res = vl->val.i % vr->val.i;
355
			break;
356
		case O_PLUS:
357
		case O_PLUSASN:
358
			res = vl->val.i + vr->val.i;
359
			break;
360
		case O_MINUS:
361
		case O_MINUSASN:
362
			res = vl->val.i - vr->val.i;
363
			break;
364
		case O_LSHIFT:
365
		case O_LSHIFTASN:
366
			res = vl->val.i << vr->val.i;
367
			break;
368
		case O_RSHIFT:
369
		case O_RSHIFTASN:
370
			res = vl->val.i >> vr->val.i;
371
			break;
372
		case O_LT:
373
			res = vl->val.i < vr->val.i;
374
			break;
375
		case O_LE:
376
			res = vl->val.i <= vr->val.i;
377
			break;
378
		case O_GT:
379
			res = vl->val.i > vr->val.i;
380
			break;
381
		case O_GE:
382
			res = vl->val.i >= vr->val.i;
383
			break;
384
		case O_EQ:
385
			res = vl->val.i == vr->val.i;
386
			break;
387
		case O_NE:
388
			res = vl->val.i != vr->val.i;
389
			break;
390
		case O_BAND:
391
		case O_BANDASN:
392
			res = vl->val.i & vr->val.i;
393
			break;
394
		case O_BXOR:
395
		case O_BXORASN:
396
			res = vl->val.i ^ vr->val.i;
397
			break;
398
		case O_BOR:
399
		case O_BORASN:
400
			res = vl->val.i | vr->val.i;
401
			break;
402
		case O_LAND:
403
			if (!vl->val.i)
404
				es->noassign++;
405
			vr = intvar(es, evalexpr(es, ((int) prec) - 1));
406
			res = vl->val.i && vr->val.i;
407
			if (!vl->val.i)
408
				es->noassign--;
409
			break;
410
		case O_LOR:
411
			if (vl->val.i)
412
				es->noassign++;
413
			vr = intvar(es, evalexpr(es, ((int) prec) - 1));
414
			res = vl->val.i || vr->val.i;
415
			if (vl->val.i)
416
				es->noassign--;
417
			break;
418
		case O_TERN:
419
			{
420
				int e = vl->val.i != 0;
421
				if (!e)
422
					es->noassign++;
423
				vl = evalexpr(es, MAX_PREC);
424
				if (!e)
425
					es->noassign--;
426
				if (es->tok != CTERN)
427
					evalerr(es, ET_STR, "missing :");
428
				token(es);
429
				if (e)
430
					es->noassign++;
431
				vr = evalexpr(es, P_TERN);
432
				if (e)
433
					es->noassign--;
434
				vl = e ? vl : vr;
435
			}
436
			break;
437
		case O_ASN:
438
			res = vr->val.i;
439
			break;
440
		case O_COMMA:
441
			res = vr->val.i;
442
			break;
443
		}
444
		if (IS_ASSIGNOP(op)) {
445
			vr->val.i = res;
446
			if (vasn->flag & INTEGER)
447
				setint_v(vasn, vr);
448
			else
449
				setint(vasn, res);
450
			vl = vr;
451
		} else if (op != O_TERN)
452
			vl->val.i = res;
453
	}
454
	return vl;
455
}
456
 
457
static void
458
token(es)
459
	Expr_state *es;
460
{
461
	const char *cp;
462
	int c;
463
	char *tvar;
464
 
465
	/* skip white space */
466
	for (cp = es->tokp; (c = *cp), isspace(c); cp++)
467
		;
468
	es->tokp = cp;
469
 
470
	if (c == '\0')
471
		es->tok = END;
472
	else if (letter(c)) {
473
		for (; letnum(c); c = *cp)
474
			cp++;
475
		if (c == '[') {
476
			int len;
477
 
478
			len = array_ref_len(cp);
479
			if (len == 0)
480
				evalerr(es, ET_STR, "missing ]");
481
			cp += len;
482
		}
483
#ifdef KSH
484
		else if (c == '(' /*)*/ ) {
485
		    /* todo: add math functions (all take single argument):
486
		     * abs acos asin atan cos cosh exp int log sin sinh sqrt
487
		     * tan tanh
488
		     */
489
		    ;
490
		}
491
#endif /* KSH */
492
		if (es->noassign) {
493
			es->val = tempvar();
494
			es->val->flag |= EXPRLVALUE;
495
		} else {
496
			tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP);
497
			es->val = global(tvar);
498
			afree(tvar, ATEMP);
499
		}
500
		es->tok = VAR;
501
	} else if (digit(c)) {
502
		for (; c != '_' && (letnum(c) || c == '#'); c = *cp++)
503
			;
504
		tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP);
505
		es->val = tempvar();
506
		es->val->flag &= ~INTEGER;
507
		es->val->type = 0;
508
		es->val->val.s = tvar;
509
		if (setint_v(es->val, es->val) == NULL)
510
			evalerr(es, ET_BADLIT, tvar);
511
		afree(tvar, ATEMP);
512
		es->tok = LIT;
513
	} else {
514
		int i, n0;
515
 
516
		for (i = 0; (n0 = opinfo[i].name[0]); i++)
517
			if (c == n0
518
			    && strncmp(cp, opinfo[i].name, opinfo[i].len) == 0)
519
			{
520
				es->tok = (enum token) i;
521
				cp += opinfo[i].len;
522
				break;
523
			}
524
		if (!n0)
525
			es->tok = BAD;
526
	}
527
	es->tokp = cp;
528
}
529
 
530
/* Do a ++ or -- operation */
531
static struct tbl *
532
do_ppmm(es, op, vasn, is_prefix)
533
	Expr_state *es;
534
	enum token op;
535
	struct tbl *vasn;
536
	bool_t is_prefix;
537
{
538
	struct tbl *vl;
539
	int oval;
540
 
541
	assign_check(es, op, vasn);
542
 
543
	vl = intvar(es, vasn);
544
	oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
545
	if (vasn->flag & INTEGER)
546
		setint_v(vasn, vl);
547
	else
548
		setint(vasn, vl->val.i);
549
	if (!is_prefix)		/* undo the inc/dec */
550
		vl->val.i = oval;
551
 
552
	return vl;
553
}
554
 
555
static void
556
assign_check(es, op, vasn)
557
	Expr_state *es;
558
	enum token op;
559
	struct tbl *vasn;
560
{
561
	if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
562
		evalerr(es, ET_LVALUE, opinfo[(int) op].name);
563
	else if (vasn->flag & RDONLY)
564
		evalerr(es, ET_RDONLY, opinfo[(int) op].name);
565
}
566
 
567
static struct tbl *
568
tempvar()
569
{
570
	register struct tbl *vp;
571
 
572
	vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP);
573
	vp->flag = ISSET|INTEGER;
574
	vp->type = 0;
575
	vp->areap = ATEMP;
576
	vp->val.i = 0;
577
	vp->name[0] = '\0';
578
	return vp;
579
}
580
 
581
/* cast (string) variable to temporary integer variable */
582
static struct tbl *
583
intvar(es, vp)
584
	Expr_state *es;
585
	struct tbl *vp;
586
{
587
	struct tbl *vq;
588
 
589
	/* try to avoid replacing a temp var with another temp var */
590
	if (vp->name[0] == '\0'
591
	    && (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
592
		return vp;
593
 
594
	vq = tempvar();
595
	if (setint_v(vq, vp) == NULL) {
596
		if (vp->flag & EXPRINEVAL)
597
			evalerr(es, ET_RECURSIVE, vp->name);
598
		es->evaling = vp;
599
		vp->flag |= EXPRINEVAL;
600
		v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR);
601
		vp->flag &= ~EXPRINEVAL;
602
		es->evaling = (struct tbl *) 0;
603
	}
604
	return vq;
605
}