Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/mkmk/expr.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* expr.c - constant expression evaluation */
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include "mkmk.h"

enum {
	Oeq,
	One,
	Ole,
	Olt,
	Oge,
	Ogt,

	/*
	 * I think this is a GCC-ism, #if NAME will behave
	 * as though the user had typed #if defined(NAME)
	 * if NAME is not defined. I.e. the result is false
	 * rather than an error (which is my reading of the specs).
	 */
	gpp_bodge = 1
};

static int ign;
static State *state;
static char *nxtch;
static jmp_buf expjump;

#define ungetch()       nxtch--
#define getch()         *nxtch++

static int factor(int ign);
static int query(int ign);

/* Skip white space and return terminating char. */
static int
skipws(void)
{
	char c;
	
	do{
		c = getch();
	}while(isspace(c));
	return c;
}

/*
 * resets environment to eval(ign), prints an error 
 * and forces eval to return FALSE.
 */
static void
experr(char *fmt, ...)
{
	va_list args;

	fprint(2, "%s: %s:%d ", argv0, state->file, state->line);
	va_start(args, fmt);
	vfprint(2, fmt, args);
	va_end(args);

	longjmp(expjump, -1);
}

static int
defined(int ign)
{
	int i;
	char c, match, name[64];

	match = skipws();
	if(match != '(')
		ungetch();

	skipws();
	ungetch();
	for(i = 0; i < nelem(name)-1; i++){
		name[i] = getch();
		if(! isalnum(name[i]) && name[i] != '_'){
			ungetch();
			break;
		}
	}
	name[i] = 0;

	if(match == '(' && (c = skipws()) != ')')
		experr("missing brace in #if defined()\n");

	if(ign)
		return 0;

	if(looksym(name, state->langincdirs) == nil)
		return 0;
	return 1;

}

static int
symbol(int ign)
{
	int i;
	char *val;
	char name[128];

	for(i = 0; i < nelem(name)-1; i++){
		name[i] = getch();
		if(! isalnum(name[i]) && name[i] != '_'){
			ungetch();
			break;
		}
	}
	name[i] = 0;

	if(strcmp(name, "defined") == 0)
		return defined(ign);

	if(ign)
		return 0;
	if((val = looksym(name, state->langincdirs)) == nil){
		if(gpp_bodge)
			return 0;
		experr("%s - symbol not known\n", name);
	}
	return strtol(val, nil, 0);
}

/*
 * unary : factor | unop unary
 */
static int
unary(int ign)
{
	int val, c;

	if((c = skipws()) == '+' || c == '-' || c == '~'){
		val = unary(ign);

		switch (c){
		case '+':
			return val;
		case '-':
			return -val;
		case '~':
			return ~val;
		}
	}
	ungetch();
	return factor(ign);
}

/*
 * <term> := <unary> { <expop> <unary> }
 */
static int
expop(int ign)
{
	int vl, vr, n;

	vl = unary(ign);
	switch(skipws()){
	case '*':
		if(getch() != '*'){
			ungetch();
			break;
		}

	case '^':
		vr = expop(ign);
		n = 1;
		while(vr-- > 0)
			n *= vl;
		return n;
	}
	ungetch();
	return vl;
}

/*
 * <term> := <exp> { <mulop> <exp> }
 */
static int
term(int ign)
{
	int c, vl, vr;

	vl = expop(ign);
	while((c = skipws()) == '*' || c == '/' || c == '%'){
		vr = expop(ign);

		switch (c){
		case '*':
			vl *= vr;
			break;
		case '/':
			vl /= vr;
			break;
		case '%':
			vl %= vr;
			break;
		}
	}
	ungetch();
	return vl;
}

/*
 * primary : term { addop term }
 */
static int
primary(int ign)
{
	int c, vl, vr;

	vl = term(ign);
	while((c = skipws()) == '+' || c == '-'){
		vr = term(ign);

		if(c == '+')
			vl += vr;
		else
			vl -= vr;
	}
	ungetch();
	return vl;
}

/*
 * shift : primary { shop primary }
 */
static int
shift(int ign)
{
	int vl, vr, c;

	vl = primary(ign);
	while(((c = skipws()) == '<' || c == '>') && getch() == c){
		vr = primary(ign);

		if(c == '<')
			vl <<= vr;
		else
			vl >>= vr;
	}

	if(c == '<' || c == '>')
		ungetch();
	ungetch();
	return vl;
}

/*
 * num : digit | num digit
 */
static int
num(void)
{
	/*
	 * I cheat and don't use getch() here, strtol()'s 
	 * parsing is correct and I make too many mistakes
	 */
	return strtol(nxtch, &nxtch, 0);

}

/*
 * constant: num | SYMBOL | 'char'
 * Note: constant(ign) handles multi-byte constants
 */
static int
constant(int ign)
{
	int i;
	int value;
	char c;
	int v[sizeof(int)];

	c = skipws();
	if(isdigit(c)){
		ungetch();
		return num();
	}

	if(isalpha(c) || c == '_'){
		ungetch();
		return symbol(ign);
	}
	if(c != '\'')
		experr("%c unknown character constant '%s'\n", c, nxtch);

	for (i = 0; i < sizeof(int); i++){
		if((c = getch()) == '\''){
			ungetch();
			break;
		}
		if(c == '\\'){
			switch (c = getch()){
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
				ungetch();
				c = num();
				break;
			case 'n':
				c = 012;
				break;
			case 'r':
				c = 015;
				break;
			case 't':
				c = 011;
				break;
			case 'b':
				c = 010;
				break;
			case 'f':
				c = 014;
				break;
			}
		}
		v[i] = c;
	}
	if(i == 0 || getch() != '\'')
		experr("unterminated character constant\n");
	for (value = 0; --i >= 0;){
		value <<= 8;
		value += v[i];
	}
	return value;
}

/*
 * factor : constant | '(' query ')'
 */
static int
factor(int ign)
{
	int val;

	if(skipws() == '('){
		val = query(ign);
		if(skipws() != ')')
			experr("')' missing\n");
		return val;
	}
	ungetch();
	return constant(ign);
}

/*
 * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
 */
static int
geteqrel(void)
{
	int c1, c2;

	c1 = skipws();
	c2 = getch();

	switch (c1){
	case '=':
		if(c2 != '=')
			ungetch();
		return Oeq;
	case '!':
		if(c2 == '=')
			return One;
		ungetch();
		ungetch();
		return -1;
	case '<':
		if(c2 == '=')
			return Ole;
		ungetch();
		return Olt;
	case '>':
		if(c2 == '=')
			return Oge;
		ungetch();
		return Ogt;
	default:
		ungetch();
		ungetch();
		return -1;
	}
}

/*
 * eqrel : shift { eqrelop shift }
 */
static int
eqrel(int ign)
{
	int vl, vr, eqrel;

	vl = shift(ign);
	while((eqrel = geteqrel()) != -1){
		vr = shift(ign);

		switch (eqrel){
		case Oeq:
			vl = (vl == vr);
			break;
		case One:
			vl = (vl != vr);
			break;

		case Ole:
			vl = (vl <= vr);
			break;
		case Olt:
			vl = (vl < vr);
			break;
		case Ogt:
			vl = (vl > vr);
			break;
		case Oge:
			vl = (vl >= vr);
			break;
		}
	}
	return vl;
}
/*
 * not : eqrel | '!' not
 */
static int
not(int ign)
{
	int val, c;

	if((c = skipws()) == '!' && getch() != '='){
		ungetch();
		val = not(ign);
		return !val;
	}

	if(c == '!')
		ungetch();
	ungetch();
	return eqrel(ign);
}

/*
 * land : not { '&&' not }
 */
static int
land(int ign)
{
	int vl, vr;

	vl = not(ign);
	while(skipws() == '&'){
		if(getch() != '&')
			ungetch();
		if(! vl)
			ign++;
		vr = not(ign);
		vl = vl && vr;
	}
	ungetch();
	return vl;
}

/*
 * lor : land { '||' land }
 */
static int
lor(int ign)
{
	int vl, vr;

	vl = land(ign);
	while(skipws() == '|'){
		if(getch() != '|')
			ungetch();
		if(vl)
			ign++;
		vr = land(ign);
		vl = vl || vr;
	}

	ungetch();
	return vl;
}

/*
 * query : lor | lor '?' query ':' query
 */
static int
query(int ign)
{
	int tst, rc;

	tst = lor(ign);
	if(skipws() != '?'){
		ungetch();
		return tst;
	}

	if(tst){
		rc = query(ign);
		if(skipws() != ':')
			experr("':' not found in ternary conditional\n");

		query(ign+1);
	}
	else{
		query(ign+1);
		if(skipws() != ':')
			experr("':' not found in ternary conditional\n");
		rc = query(ign);
	}

	return rc;
}

int
expr(State *s, char *buf)
{
	int rc;
	char c;

	nxtch = buf;
	state = s;
	if(setjmp(expjump) != 0)
		return 0;

	rc = query(ign);
	if((c = skipws()) == 0)
		return rc;

	fprint(2, "%s: %s:%d '%c%s' trailing garbage after expression\n",
		argv0, s->file, s->line, c, nxtch);
	return rc;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.