changeset 346:a82c55070624

Added expression parsing infrastructure and misc fixes
author lost@starbug
date Sat, 27 Mar 2010 19:04:03 -0600
parents 7416c3f9c321
children 1649bc7bda5a
files TODO lwasm/Makefile.am lwasm/insn_inh.c lwasm/instab.c lwasm/lwasm.c lwasm/lwasm.h lwasm/macro.c lwasm/main.c lwasm/pass1.c lwasm/symbol.c lwlib/lw_expr.c lwlib/lw_expr.h
diffstat 12 files changed, 645 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TODO	Sat Mar 27 19:04:03 2010 -0600
@@ -0,0 +1,1 @@
+[LWASM] Bring macro scheme up to date to use lwlib structures
--- a/lwasm/Makefile.am	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/Makefile.am	Sat Mar 27 19:04:03 2010 -0600
@@ -1,5 +1,5 @@
 AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib -I$(top_builddir)/lwlib -I$(top_srcdir)/lwlib
 bin_PROGRAMS = lwasm
-lwasm_SOURCES = main.c pragma.c input.c pass1.c lwasm.c instab.c symbol.c macro.c
+lwasm_SOURCES = main.c pragma.c input.c pass1.c lwasm.c instab.c symbol.c macro.c insn_inh.c
 lwasm_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -L$(top_builddir)/lwlib -L$(top_srcdir)/lwlib -lgnu -llw
 EXTRA_DIST =  lwasm.h input.h instab.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_inh.c	Sat Mar 27 19:04:03 2010 -0600
@@ -0,0 +1,35 @@
+/*
+insn_inh.c
+Copyright © 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <config.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_inh)
+{
+	l -> len = OPLEN(instab[l -> insn].ops[0]);
+}
+
+RESOLVEFUNC(insn_resolve_inh)
+{
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+}
--- a/lwasm/instab.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/instab.c	Sat Mar 27 19:04:03 2010 -0600
@@ -26,11 +26,19 @@
 #define __instab_c_seen__
 #include "instab.h"
 
-// don't need to parse anything for inh
-// so it can just be NULL
-#define insn_parse_inh	NULL
+// inherent
+extern PARSEFUNC(insn_parse_inh);
+extern RESOLVEFUNC(insn_resolve_inh);
 
-extern RESOLVEFUNC(insn_resolve_inh);
+// MACRO pseudo op
+extern PARSEFUNC(pseudo_parse_macro);
+#define pseudo_resolve_macro	NULL
+
+// ENDM pseudo op
+extern PARSEFUNC(pseudo_parse_endm);
+#define pseudo_resolve_endm NULL
+
+
 extern PARSEFUNC(insn_parse_gen8);
 extern RESOLVEFUNC(insn_resolve_gen8);
 extern PARSEFUNC(insn_parse_gen16);
@@ -116,10 +124,6 @@
 extern RESOLVEFUNC(pseudo_resolve_else);
 extern PARSEFUNC(pseudo_parse_endc);
 extern RESOLVEFUNC(pseudo_resolve_endc);
-extern PARSEFUNC(pseudo_parse_macro);
-extern RESOLVEFUNC(pseudo_resolve_macro);
-extern PARSEFUNC(pseudo_parse_endm);
-extern RESOLVEFUNC(pseudo_resolve_endm);
 extern PARSEFUNC(pseudo_parse_setdp);
 extern RESOLVEFUNC(pseudo_resolve_setdp);
 extern PARSEFUNC(pseudo_parse_set);
@@ -321,8 +325,9 @@
 	{ "nega",		{	0x40,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				lwasm_insn_normal},
 	{ "negb",		{	0x50,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				lwasm_insn_normal},
 	{ "negd",		{	0x1040,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				lwasm_insn_is6309},
+*/
 	{ "nop",		{	0x12,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				lwasm_insn_normal},
-	
+/*	
 	{ "oim",		{	0x01,	0x61,	0x71,	-1	},	insn_parse_logicmem,	insn_resolve_logicmem,			lwasm_insn_is6309},
 	{ "ora",		{	0x9a,	0xaa,	0xba,	0x8a},	insn_parse_gen8,		insn_resolve_gen8,				lwasm_insn_normal},
 	{ "orb",		{	0xda,	0xea,	0xfa,	0xca},	insn_parse_gen8,		insn_resolve_gen8,				lwasm_insn_normal},
@@ -463,9 +468,10 @@
 	{ "ifdef",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifdef,		pseudo_resolve_ifdef,			lwasm_insn_cond},
 	{ "ifndef",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifndef,	pseudo_resolve_ifndef,			lwasm_insn_cond},
 
+*/
 	{ "macro",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_macro,		pseudo_resolve_macro,			lwasm_insn_cond | lwasm_insn_setsym},
 	{ "endm",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_endm,		pseudo_resolve_endm,			lwasm_insn_cond | lwasm_insn_setsym | lwasm_insn_endm},
-
+/*
 	{ "setdp", 		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_setdp,		pseudo_resolve_setdp,			lwasm_insn_normal},
 	{ "set",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_set,		pseudo_resolve_set,				lwasm_insn_setsym},
 
--- a/lwasm/lwasm.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/lwasm.c	Sat Mar 27 19:04:03 2010 -0600
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include <lw_expr.h>
 #include <lw_alloc.h>
@@ -32,12 +33,12 @@
 
 #include "lwasm.h"
 
-lw_expr_t lwasm_evaluate_var(char *var)
+lw_expr_t lwasm_evaluate_var(char *var, void *priv)
 {
 	return NULL;
 }
 
-lw_expr_t lwasm_evaluate_special(int t, void *ptr)
+lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv)
 {
 	switch (t)
 	{
@@ -49,6 +50,15 @@
 			return lw_expr_build(lw_expr_type_int, cl -> len);
 		}
 		break;
+		
+	case lwasm_expr_lineaddr:
+		{
+			line_t *cl = ptr;
+			if (cl -> addr)
+				return lw_expr_copy(cl -> addr);
+			else
+				return NULL;
+		}
 	}
 	return NULL;
 }
@@ -85,3 +95,339 @@
 	as -> nextcontext++;
 	return r;
 }
+
+void lwasm_emit(line_t *cl, int byte)
+{
+	if (cl -> outputl == cl -> outputbl)
+	{
+		cl -> output = lw_realloc(cl -> output, cl -> outputbl + 8);
+		cl -> outputbl += 8;
+	}
+	cl -> output[cl -> outputl++] = byte & 0xff;
+}
+
+void lwasm_emitop(line_t *cl, int opc)
+{
+	if (opc > 0x100)
+		lwasm_emit(cl, opc >> 8);
+	lwasm_emit(cl, opc);
+}
+
+lw_expr_t lwasm_parse_term(char **p, void *priv)
+{
+	asmstate_t *as = priv;
+	int val;
+	
+	if (!**p)
+		return NULL;
+	
+	if (**p == '*' || (
+			**p == '.'
+			&& !((*p)[1] >= 'A' && (*p)[1] <= 'Z')
+			&& !((*p)[1] >= 'a' && (*p)[1] <= 'z')
+			&& !((*p)[1] >= '0' && (*p)[1] <= '9')
+		))
+	{
+		// special "symbol" for current line addr (*, .)
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, as -> cl);
+	}
+	
+	// branch points
+	if (**p == '<')
+	{
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_prevbp, as -> cl);
+	}
+	if (**p == '>')
+	{
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_nextbp, as -> cl);
+	}
+	
+	// double ascii constant
+	if (**p == '"')
+	{
+		int v;
+		(*p)++;
+		if (!**p)
+			return NULL;
+		if (!*((*p)+1))
+			return NULL;
+		v = (unsigned char)**p << 8 | (unsigned char)*((*p)+1);
+		(*p) += 2;
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '\'')
+	{
+		int v;
+		
+		(*p)++;
+		if (!**p)
+			return NULL;
+		
+		v = (unsigned char)**p;
+		(*p)++;
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '&')
+	{
+		// decimal constant
+		int v = 0;
+		(*p)++;
+
+		if (!strchr("0123456789", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789", **p))
+		{
+			val = val * 10 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+
+	if (**p == '%')
+	{
+		// binary constant
+		int v = 0;
+		(*p)++;
+
+		if (**p != '0' && **p != '1')
+			return NULL;
+
+		while (**p && (**p == '0' || **p == '1'))
+		{
+			val = val * 2 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '$')
+	{
+		// hexadecimal constant
+		int v = 0, v2;
+		(*p)++;
+
+		if (!strchr("0123456789abcdefABCDEF", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789abcdefABCDEF", **p))
+		{
+			v2 = toupper(**p) - '0';
+			if (v2 > 9)
+				v2 -= 7;
+			val = val * 16 + v2;
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '0' && (*((*p)+1) == 'x' || *((*p)+1) == 'X'))
+	{
+		// hexadecimal constant, C style
+		int v = 0, v2;
+		(*p)+=2;
+
+		if (!strchr("0123456789abcdefABCDEF", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789abcdefABCDEF", **p))
+		{
+			v2 = toupper(**p) - '0';
+			if (v2 > 9)
+				v2 -= 7;
+			val = val * 16 + v2;
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '@' && (*((*p)+1) >= '0' && *((*p)+1) <= '7'))
+	{
+		// octal constant
+		int v = 0;
+		(*p)++;
+
+		if (!strchr("01234567", **p))
+			return NULL;
+
+		while (**p && strchr("01234567", **p))
+		{
+			val = val * 8 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+
+	// symbol or bare decimal or suffix constant here
+	do
+	{
+		int havedol = 0;
+		int l = 0;
+		
+		while ((*p)[l] && strchr(SYMCHARS, (*p)[l]))
+		{
+			if ((*p)[l] == '$')
+				havedol = 1;
+			l++;
+		}
+		if (l == 0)
+			return NULL;
+		
+		if (havedol || **p < '0' || **p > '9')
+		{
+			// have a symbol here
+			char *sym;
+			lw_expr_t term;
+			
+			sym = lw_strndup(*p, l);
+			(*p) += l;
+			term = lw_expr_build(lw_expr_type_var, sym);
+			lw_free(sym);
+			return term;
+		}
+	} while (0);
+	
+	if (!**p)
+		return NULL;
+	
+	// we have a numeric constant here, either decimal or postfix base notation
+	{
+		int decval = 0, binval = 0, hexval = 0, octval = 0;
+		int valtype = 15; // 1 = bin, 2 = oct, 4 = dec, 8 = hex
+		int bindone = 0;
+		int val;
+		int dval;
+		
+		while (1)
+		{
+			if (!**p || !strchr("0123456789ABCDEFabcdefqhoQHO", **p))
+			{
+				// we can legally be bin or decimal here
+				if (bindone)
+				{
+					// just finished a binary value
+					val = binval;
+					break;
+				}
+				else if (valtype & 4)
+				{
+					val = decval;
+					break;
+				}
+				else
+				{
+					// bad value
+					return NULL;
+				}
+			}
+			
+			dval = toupper(**p);
+			(*p)++;
+			
+			if (bindone)
+			{
+				// any characters past "B" means it is not binary
+				bindone = 0;
+				valtype &= 14;
+			}
+			
+			switch (dval)
+			{
+			case 'Q':
+			case 'O':
+				if (valtype & 2)
+				{
+					val = octval;
+					valtype = -1;
+					break;
+				}
+				else
+				{
+					return NULL;
+				}
+				/* can't get here */
+			
+			case 'H':
+				if (valtype & 8)
+				{
+					val = hexval;
+					valtype = -1;
+					break;
+				}
+				else
+				{
+					return NULL;
+				}
+				/* can't get here */
+			
+			case 'B':
+				// this is a bit of a sticky one since B may be a
+				// hex number instead of the end of a binary number
+				// so it falls through to the digit case
+				if (valtype & 1)
+				{
+					// could still be binary of hex
+					bindone = 1;
+					valtype = 9;
+				}
+				/* fall through intented */
+			
+			default:
+				// digit
+				dval -= '0';
+				if (dval > 9)
+					dval -= 7;
+				if (valtype & 8)
+					hexval = hexval * 16 + dval;
+				if (valtype & 4)
+				{
+					if (dval > 9)
+						valtype &= 11;
+					else
+						decval = decval * 10 + dval;
+				}
+				if (valtype & 2)
+				{
+					if (dval > 7)
+						valtype &= 13;
+					else
+						octval = octval * 8 + dval;
+				}
+				if (valtype & 1)
+				{
+					if (dval > 1)
+						valtype &= 14;
+					else
+						binval = binval * 2 + dval;
+				}
+			}
+			if (valtype == -1)
+				break;
+			
+			// return if no more valid types
+			if (valtype == 0)
+				return NULL;
+			
+			val = decval; // in case we fall through	
+		} 
+		
+		// get here if we have a value
+		return lw_expr_build(lw_expr_type_int, val);
+	}
+	// can't get here
+}
+
+lw_expr_t lwasm_parse_expr(asmstate_t *as, char **p)
+{
+	lw_expr_t e;
+	
+	e = lw_expr_parse(p, as);
+	
+	return e;
+}
--- a/lwasm/lwasm.h	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/lwasm.h	Sat Mar 27 19:04:03 2010 -0600
@@ -26,9 +26,20 @@
 #include <lw_stringlist.h>
 #include <lw_stack.h>
 
+
+// these are allowed chars BELOW 0x80 for symbols
+// first is symbol start chars, second is anywhere in symbol
+#define SSYMCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_@$"
+#define SYMCHARS SSYMCHARS ".?0123456789"
+
+typedef struct asmstate_s asmstate_t;
+
 enum
 {
-	lwasm_expr_linelen = 1
+	lwasm_expr_linelen = 1,			// length of ref'd line
+	lwasm_expr_lineaddr = 2,		// addr of ref'd line
+	lwasm_expr_nextbp = 3,			// next branch point
+	lwasm_expr_prevbp = 4			// previous branch point
 };
 
 enum lwasm_output_e
@@ -78,9 +89,13 @@
 	int insn;							// number of insn in insn table
 	int symset;							// set if the line symbol was consumed by the instruction
 	char *sym;							// symbol, if any, on the line
+	unsigned char *output;				// output bytes
+	int outputl;						// size of output
+	int outputbl;						// size of output buffer
 	lwasm_error_t *err;					// list of errors
 	line_t *prev;						// previous line
 	line_t *next;						// next line
+	asmstate_t *as;						// assembler state data ptr
 };
 
 enum
@@ -113,7 +128,7 @@
 	macrotab_t *next;					// next macro in list
 };
 
-typedef struct
+struct asmstate_s
 {
 	int output_format;					// output format
 	int target;							// assembly target
@@ -127,6 +142,8 @@
 
 	line_t *line_head;					// start of lines list
 	line_t *line_tail;					// tail of lines list
+
+	line_t *cl;							// current line pointer
 	
 	int context;						// the current "context"
 	int nextcontext;					// the next available context
@@ -140,7 +157,7 @@
 	void *input_data;					// opaque data used by the input system
 	lw_stringlist_t include_list;		// include paths
 	lw_stack_t file_dir;				// stack of the "current file" dir
-} asmstate_t;
+};
 
 #ifndef ___symbol_c_seen___
 
@@ -153,8 +170,11 @@
 
 extern void lwasm_register_error(asmstate_t *as, line_t *cl, const char *msg, ...);
 extern int lwasm_next_context(asmstate_t *as);
+extern void lwasm_emit(line_t *cl, int byte);
+extern void lwasm_emitop(line_t *cl, int opc);
 
 #endif
 
+#define OPLEN(op) (((op)>0xFF)?2:1)
 
 #endif /* ___lwasm_h_seen___ */
--- a/lwasm/macro.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/macro.c	Sat Mar 27 19:04:03 2010 -0600
@@ -34,10 +34,12 @@
 #include "input.h"
 #include "instab.h"
 
-PARSEFUNC(pseudo_macro_parse)
+PARSEFUNC(pseudo_parse_macro)
 {
 	macrotab_t *m;
 	
+	l -> len = 0;
+	
 	if (as -> skipcond)
 	{
 		as -> skipmacro = 1;
@@ -80,8 +82,10 @@
 	as -> inmacro = 1;
 }
 
-PARSEFUNC(pseudo_endm_parse)
+PARSEFUNC(pseudo_parse_endm)
 {
+	l -> len = 0;
+
 	if (as -> skipcond)
 	{
 		as -> skipmacro = 0;
--- a/lwasm/main.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/main.c	Sat Mar 27 19:04:03 2010 -0600
@@ -165,9 +165,9 @@
 assembler on the first file
 */
 extern void do_pass1(asmstate_t *as);
-extern lw_expr_t lwasm_evaluate_special(int t, void *ptr);
-extern lw_expr_t lwasm_evaluate_var(char *var);
-
+extern lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv);
+extern lw_expr_t lwasm_evaluate_var(char *var, void *priv);
+extern lw_expr_t lwasm_parse_term(char **p, void *priv);
 int main(int argc, char **argv)
 {
 	/* assembler state */
--- a/lwasm/pass1.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/pass1.c	Sat Mar 27 19:04:03 2010 -0600
@@ -22,6 +22,7 @@
 #include <config.h>
 
 #include <stdio.h>
+#include <string.h>
 
 #include <lw_alloc.h>
 #include <lw_string.h>
@@ -78,11 +79,10 @@
 		printf("%s\n", line);
 		
 		cl = lw_alloc(sizeof(line_t));
-		cl -> next = NULL;
+		memset(cl, 0, sizeof(line_t));
 		cl -> prev = as -> line_tail;
-		cl -> len = -1;
 		cl -> insn = -1;
-		cl -> err = NULL;
+		cl -> as = as;
 		if (!as -> line_tail)
 		{
 			as -> line_head = cl;
@@ -95,9 +95,10 @@
 			te = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, cl -> prev);
 			cl -> addr = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, cl -> prev -> addr, te);
 			lw_expr_destroy(te);
-			lw_expr_simplify(cl -> addr);
+			lw_expr_simplify(cl -> addr, as);
 		}
 		as -> line_tail = cl;
+		as -> cl = cl;
 
 		// blank lines don't count for anything
 		// except a local symbol context break
@@ -222,6 +223,7 @@
 				// no parse func means operand doesn't matter
 				if (instab[opnum].parse)
 				{
+					cl -> len = -1;
 					// call parse function
 					(instab[opnum].parse)(as, cl, &p1);
 					
--- a/lwasm/symbol.c	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwasm/symbol.c	Sat Mar 27 19:04:03 2010 -0600
@@ -29,10 +29,6 @@
 
 #include "lwasm.h"
 
-// these are allowed chars BELOW 0x80
-#define SSYMCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_@$"
-#define SYMCHARS SSYMCHARS ".?0123456789"
-
 struct symtabe *register_symbol(asmstate_t *as, line_t *cl, char *sym, lw_expr_t val, int flags)
 {
 	struct symtabe *se;
--- 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);
+}
--- a/lwlib/lw_expr.h	Thu Mar 25 23:17:54 2010 -0600
+++ b/lwlib/lw_expr.h	Sat Mar 27 19:04:03 2010 -0600
@@ -45,7 +45,8 @@
 	lw_expr_oper_and,			// boolean and
 	lw_expr_oper_or,			// boolean or
 	lw_expr_oper_neg,			// unary negation, 2's complement
-	lw_expr_oper_com			// unary 1's complement
+	lw_expr_oper_com,			// unary 1's complement
+	lw_expr_oper_none = 0
 };
 
 #ifdef ___lw_expr_c_seen___
@@ -66,9 +67,9 @@
 	struct lw_expr_opers *operands;		// ptr to list of operands (for operators)
 };
 
-typedef lw_expr_t lw_expr_fn_t(int t, void *ptr);
-typedef lw_expr_t lw_expr_fn2_t(char *var);
-
+typedef lw_expr_t lw_expr_fn_t(int t, void *ptr, void *priv);
+typedef lw_expr_t lw_expr_fn2_t(char *var, void *priv);
+typedef lw_expr_t lw_expr_fn3_t(char **p, void *priv);
 
 #else /* def ___lw_expr_c_seen___ */
 
@@ -81,13 +82,17 @@
 extern lw_expr_t lw_expr_build(int exprtype, ...);
 extern void lw_expr_print(lw_expr_t E);
 extern int lw_expr_compare(lw_expr_t E1, lw_expr_t E2);
-extern void lw_expr_simplify(lw_expr_t E);
+extern void lw_expr_simplify(lw_expr_t E, void *priv);
 
-typedef lw_expr_t lw_expr_fn_t(int t, void *ptr);
-typedef lw_expr_t lw_expr_fn2_t(char *var);
+typedef lw_expr_t lw_expr_fn_t(int t, void *ptr, void *priv);
+typedef lw_expr_t lw_expr_fn2_t(char *var, void *priv);
+typedef lw_expr_t lw_expr_fn3_t(char **p, void *priv);
 
 extern void lw_expr_set_special_handler(lw_expr_fn_t *fn);
 extern void lw_expr_set_var_handler(lw_expr_fn2_t *fn);
+extern void lw_expr_set_term_parser(lw_expr_fn3_t *fn);
+
+extern lw_expr_t lw_expr_parse(char **p, void *priv);
 
 #endif /* def ___lw_expr_c_seen___ */