diff lwlib/lw_expr.c @ 346:a82c55070624

Added expression parsing infrastructure and misc fixes
author lost@starbug
date Sat, 27 Mar 2010 19:04:03 -0600
parents 7b4123dce741
children 1649bc7bda5a
line wrap: on
line diff
--- a/lwlib/lw_expr.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwlib/lw_expr.c	Sat Mar 27 19:04:03 2010 -0600
@@ -33,6 +33,12 @@
 
 static lw_expr_fn_t *evaluate_special = NULL;
 static lw_expr_fn2_t *evaluate_var = NULL;
+static lw_expr_fn3_t *parse_term = NULL;
+
+void lw_expr_set_term_parser(lw_expr_fn3_t *fn)
+{
+	parse_term = fn;
+}
 
 void lw_expr_set_special_handler(lw_expr_fn_t *fn)
 {
@@ -353,7 +359,7 @@
 	return 1;
 }
 
-void lw_expr_simplify(lw_expr_t E)
+void lw_expr_simplify(lw_expr_t E, void *priv)
 {
 	struct lw_expr_opers *o;
 
@@ -363,7 +369,7 @@
 	{
 		lw_expr_t te;
 		
-		te = evaluate_special(E -> value, E -> value2);
+		te = evaluate_special(E -> value, E -> value2, priv);
 		if (te)
 		{
 			for (o = E -> operands; o; o = o -> next)
@@ -389,7 +395,7 @@
 	{
 		lw_expr_t te;
 		
-		te = evaluate_var(E -> value2);
+		te = evaluate_var(E -> value2, priv);
 		if (te)
 		{
 			for (o = E -> operands; o; o = o -> next)
@@ -420,7 +426,7 @@
 	
 	// simplify operands
 	for (o = E -> operands; o; o = o -> next)
-		lw_expr_simplify(o -> p);
+		lw_expr_simplify(o -> p, priv);
 
 	for (o = E -> operands; o; o = o -> next)
 	{
@@ -637,3 +643,187 @@
 		return;
 	}
 }
+
+/*
+
+The following two functions are co-routines which evaluate an infix
+expression.  lw_expr_parse_term checks for unary prefix operators then, if
+none found, passes the string off the the defined helper function to
+determine what the term really is. It also handles parentheses.
+
+lw_expr_parse_expr evaluates actual expressions with infix operators. It
+respects the order of operations.
+
+The end of an expression is determined by the presence of any of the
+following conditions:
+
+1. a NUL character
+2. a whitespace character
+3. a )
+4. a ,
+5. any character that is not recognized as a term
+
+lw_expr_parse_term returns NULL if there is no term (end of expr, etc.)
+
+lw_expr_parse_expr returns NULL if there is no expression or on a syntax
+error.
+
+*/
+
+lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec);
+
+lw_expr_t lw_expr_parse_term(char **p, void *priv)
+{
+	lw_expr_t term, term2;
+	
+eval_next:
+	if (!**p || isspace(**p) || **p == ')' || **p == ']')
+		return NULL;
+
+	// parentheses
+	if (**p == '(')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 0);
+		if (**p != ')')
+		{
+			lw_expr_destroy(term);
+			return NULL;
+		}
+		(*p)++;
+		return term;
+	}
+	
+	// unary +
+	if (**p == '+')
+	{
+		(*p)++;
+		goto eval_next;
+	}
+	
+	// unary - (prec 200)
+	if (**p == '-')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 200);
+		if (!term)
+			return NULL;
+		
+		term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_neg, term);
+		lw_expr_destroy(term);
+		return term2;
+	}
+	
+	// unary ^ or ~ (complement, prec 200)
+	if (**p == '^' || **p == '~')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 200);
+		if (!term)
+			return NULL;
+		
+		term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_com, term);
+		lw_expr_destroy(term);
+		return term2;
+	}
+	
+	// non-operator - pass to caller
+	return parse_term(p, priv);
+}
+
+lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec)
+{
+	static const struct operinfo
+	{
+		int opernum;
+		char *operstr;
+		int operprec;
+	} operators[] =
+	{
+		{ lw_expr_oper_plus, "+", 100 },
+		{ lw_expr_oper_minus, "-", 100 },
+		{ lw_expr_oper_times, "*", 100 },
+		{ lw_expr_oper_divide, "/", 150 },
+		{ lw_expr_oper_mod, "%", 150 },
+		{ lw_expr_oper_intdiv, "\\", 150 },
+		
+		{ lw_expr_oper_and, "&&", 25 },
+		{ lw_expr_oper_or, "||", 25 },
+		
+		{ lw_expr_oper_bwand, "&", 50 },
+		{ lw_expr_oper_bwor, "|", 50 },
+		{ lw_expr_oper_bwxor, "^", 50 },
+		
+		{ lw_expr_oper_none, "", 0 }
+	};
+	
+	int opern, i;
+	lw_expr_t term1, term2, term3;
+	
+	if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']')
+		return NULL;
+	
+	term1 = lw_expr_parse_term(p, priv);
+	if (!term1)
+		return NULL;
+
+eval_next:
+	if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']')
+		return term1;
+	
+	// expecting an operator here
+	for (opern = 0; operators[opern].opernum != lw_expr_oper_none; opern++)
+	{
+		for (i = 0; (*p)[i] && operators[opern].operstr[i] && ((*p)[i] == operators[opern].operstr[i]); i++)
+			/* do nothing */;
+		if (operators[opern].operstr[i] == '\0')
+			break;
+	}
+	
+	if (operators[opern].opernum == lw_expr_oper_none)
+	{
+		// unrecognized operator
+		lw_expr_destroy(term1);
+		return NULL;
+	}
+
+	// operator number is in opern, length of oper in i
+	
+	// logic:
+	// if the precedence of this operation is <= to the "prec" flag,
+	// we simply return without advancing the input pointer; the operator
+	// will be evaluated again in the enclosing function call
+	if (operators[opern].operprec <= prec)
+		return term1;
+	
+	// logic:
+	// we have a higher precedence operator here so we will advance the
+	// input pointer to the next term and let the expression evaluator
+	// loose on it after which time we will push our operator onto the
+	// stack and then go on with the expression evaluation
+	(*p) += i;
+	
+	// evaluate next expression(s) of higher precedence
+	term2 = lw_expr_parse_expr(p, priv, operators[opern].operprec);
+	if (!term2)
+	{
+		lw_expr_destroy(term1);
+		return NULL;
+	}
+	
+	// now create operator
+	term3 = lw_expr_build(lw_expr_type_oper, operators[opern].opernum, term1, term2);
+	lw_expr_destroy(term1);
+	lw_expr_destroy(term2);
+	
+	// the new "expression" is the next "left operand"
+	term1 = term3;
+	
+	// continue evaluating
+	goto eval_next;
+}
+
+lw_expr_t lw_expr_parse(char **p, void *priv)
+{
+	return lw_expr_parse_expr(p, priv, 0);
+}