/***************************************************************************

    express.c

    Generic expressions engine.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

****************************************************************************

    Operator precedence
    ===================
    0x0000       ( )
    0x0001       ++ (postfix), -- (postfix)
    0x0002       ++ (prefix), -- (prefix), ~, !, - (unary), + (unary), b@, w@, d@, q@
    0x0003       *, /, %
    0x0004       + -
    0x0005       << >>
    0x0006       < <= > >=
    0x0007       == !=
    0x0008       &
    0x0009       ^
    0x000a       |
    0x000b       &&
    0x000c       ||
    0x000d       = *= /= %= += -= <<= >>= &= |= ^=
    0x000e       ,
    0x000f       func()

***************************************************************************/

#include "emu.h"
#include "express.h"
#include <ctype.h>



/***************************************************************************
    DEBUGGING
***************************************************************************/

#define DEBUG_TOKENS			0



/***************************************************************************
    CONSTANTS
***************************************************************************/

#ifndef DEFAULT_BASE
#define DEFAULT_BASE			16			// hex unless otherwise specified
#endif


// token.value values if token.is_operator()
enum
{
	TVL_LPAREN,
	TVL_RPAREN,
	TVL_PLUSPLUS,
	TVL_MINUSMINUS,
	TVL_PREINCREMENT,
	TVL_PREDECREMENT,
	TVL_POSTINCREMENT,
	TVL_POSTDECREMENT,
	TVL_COMPLEMENT,
	TVL_NOT,
	TVL_UPLUS,
	TVL_UMINUS,
	TVL_MULTIPLY,
	TVL_DIVIDE,
	TVL_MODULO,
	TVL_ADD,
	TVL_SUBTRACT,
	TVL_LSHIFT,
	TVL_RSHIFT,
	TVL_LESS,
	TVL_LESSOREQUAL,
	TVL_GREATER,
	TVL_GREATEROREQUAL,
	TVL_EQUAL,
	TVL_NOTEQUAL,
	TVL_BAND,
	TVL_BXOR,
	TVL_BOR,
	TVL_LAND,
	TVL_LOR,
	TVL_ASSIGN,
	TVL_ASSIGNMULTIPLY,
	TVL_ASSIGNDIVIDE,
	TVL_ASSIGNMODULO,
	TVL_ASSIGNADD,
	TVL_ASSIGNSUBTRACT,
	TVL_ASSIGNLSHIFT,
	TVL_ASSIGNRSHIFT,
	TVL_ASSIGNBAND,
	TVL_ASSIGNBXOR,
	TVL_ASSIGNBOR,
	TVL_COMMA,
	TVL_MEMORYAT,
	TVL_EXECUTEFUNC
};



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

// a symbol entry representing a register, with read/write callbacks
class integer_symbol_entry : public symbol_entry
{
public:
	// construction/destruction
	integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, UINT64 *ptr = NULL);
	integer_symbol_entry(symbol_table &table, const char *name, UINT64 constval);
	integer_symbol_entry(symbol_table &table, const char *name, void *ref, symbol_table::getter_func getter, symbol_table::setter_func setter);

	// symbol access
	virtual bool is_lval() const;
	virtual UINT64 value() const;
	virtual void set_value(UINT64 newvalue);

private:
	// internal helpers
	static UINT64 internal_getter(symbol_table &table, void *symref);
	static void internal_setter(symbol_table &table, void *symref, UINT64 value);

	// internal state
	symbol_table::getter_func	m_getter;
	symbol_table::setter_func	m_setter;
	UINT64						m_value;
};


// a symbol entry representing a function
class function_symbol_entry : public symbol_entry
{
public:
	// construction/destruction
	function_symbol_entry(symbol_table &table, const char *name, void *ref, int minparams, int maxparams, symbol_table::execute_func execute);

	// symbol access
	virtual bool is_lval() const;
	virtual UINT64 value() const;
	virtual void set_value(UINT64 newvalue);

	// execution helper
	virtual UINT64 execute(int numparams, const UINT64 *paramlist);

private:
	// internal state
	UINT16						m_minparams;
	UINT16						m_maxparams;
	symbol_table::execute_func	m_execute;
};



//**************************************************************************
//  EXPRESSION ERROR
//**************************************************************************

//-------------------------------------------------
//  code_string - return a friendly string for a
//  given expression error
//-------------------------------------------------

const char *expression_error::code_string() const
{
	switch (m_code)
	{
		case NOT_LVAL:				return "not an lvalue";
		case NOT_RVAL:				return "not an rvalue";
		case SYNTAX:				return "syntax error";
		case UNKNOWN_SYMBOL:		return "unknown symbol";
		case INVALID_NUMBER:		return "invalid number";
		case INVALID_TOKEN:			return "invalid token";
		case STACK_OVERFLOW:		return "stack overflow";
		case STACK_UNDERFLOW:		return "stack underflow";
		case UNBALANCED_PARENS:		return "unbalanced parentheses";
		case DIVIDE_BY_ZERO:		return "divide by zero";
		case OUT_OF_MEMORY:			return "out of memory";
		case INVALID_PARAM_COUNT:	return "invalid number of parameters";
		case UNBALANCED_QUOTES:		return "unbalanced quotes";
		case TOO_MANY_STRINGS:		return "too many strings";
		case INVALID_MEMORY_SIZE:	return "invalid memory size (b/w/d/q expected)";
		case NO_SUCH_MEMORY_SPACE:	return "non-existent memory space";
		case INVALID_MEMORY_SPACE:	return "invalid memory space (p/d/i/o/r/m expected)";
		case INVALID_MEMORY_NAME:	return "invalid memory name";
		case MISSING_MEMORY_NAME:	return "missing memory name";
		default:					return "unknown error";
	}
}



//**************************************************************************
//  SYMBOL ENTRY
//**************************************************************************

//-------------------------------------------------
//  symbol_entry - constructor
//-------------------------------------------------

symbol_entry::symbol_entry(symbol_table &table, symbol_type type, const char *name, void *ref)
	: m_next(NULL),
	  m_table(table),
	  m_type(type),
	  m_name(name),
	  m_ref(ref)
{
}


//-------------------------------------------------
//  ~symbol_entry - destructor
//-------------------------------------------------

symbol_entry::~symbol_entry()
{
}



//**************************************************************************
//  REGISTER SYMBOL ENTRY
//**************************************************************************

//-------------------------------------------------
//  integer_symbol_entry - constructor
//-------------------------------------------------

integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, UINT64 *ptr)
	: symbol_entry(table, SMT_INTEGER, name, (ptr == NULL) ? &m_value : ptr),
	  m_getter(internal_getter),
	  m_setter((rw == symbol_table::READ_ONLY) ? NULL : internal_setter),
	  m_value(0)
{
}


integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, UINT64 constval)
	: symbol_entry(table, SMT_INTEGER, name, &m_value),
	  m_getter(internal_getter),
	  m_setter(NULL),
	  m_value(constval)
{
}


integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, void *ref, symbol_table::getter_func getter, symbol_table::setter_func setter)
	: symbol_entry(table, SMT_INTEGER, name, ref),
	  m_getter(getter),
	  m_setter(setter),
	  m_value(0)
{
}


//-------------------------------------------------
//  is_lval - is this symbol allowable as an lval?
//-------------------------------------------------

bool integer_symbol_entry::is_lval() const
{
	return (m_setter != NULL);
}


//-------------------------------------------------
//  value - return the value of this symbol
//-------------------------------------------------

UINT64 integer_symbol_entry::value() const
{
	return (*m_getter)(m_table, m_ref);
}


//-------------------------------------------------
//  set_value - set the value of this symbol
//-------------------------------------------------

void integer_symbol_entry::set_value(UINT64 newvalue)
{
	if (m_setter != NULL)
		(*m_setter)(m_table, m_ref, newvalue);
	else
		throw emu_fatalerror("Symbol '%s' is read-only", m_name.cstr());
}


//-------------------------------------------------
//  internal_getter - internal helper for
//  returning the value of a variable
//-------------------------------------------------

UINT64 integer_symbol_entry::internal_getter(symbol_table &table, void *symref)
{
	return *(UINT64 *)symref;
}


//-------------------------------------------------
//  internal_setter - internal helper for setting
//  the value of a variable
//-------------------------------------------------

void integer_symbol_entry::internal_setter(symbol_table &table, void *symref, UINT64 value)
{
	*(UINT64 *)symref = value;
}



//**************************************************************************
//  FUNCTION SYMBOL ENTRY
//**************************************************************************

//-------------------------------------------------
//  function_symbol_entry - constructor
//-------------------------------------------------

function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, void *ref, int minparams, int maxparams, symbol_table::execute_func execute)
	: symbol_entry(table, SMT_FUNCTION, name, ref),
	  m_minparams(minparams),
	  m_maxparams(maxparams),
	  m_execute(execute)
{
}


//-------------------------------------------------
//  is_lval - is this symbol allowable as an lval?
//-------------------------------------------------

bool function_symbol_entry::is_lval() const
{
	return false;
}


//-------------------------------------------------
//  value - return the value of this symbol
//-------------------------------------------------

UINT64 function_symbol_entry::value() const
{
	throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name.cstr());
}


//-------------------------------------------------
//  set_value - set the value of this symbol
//-------------------------------------------------

void function_symbol_entry::set_value(UINT64 newvalue)
{
	throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name.cstr());
}


//-------------------------------------------------
//  execute - execute the function
//-------------------------------------------------

UINT64 function_symbol_entry::execute(int numparams, const UINT64 *paramlist)
{
	if (numparams < m_minparams)
		throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name.cstr(), m_minparams);
	if (numparams > m_maxparams)
		throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name.cstr(), m_maxparams);
	return (*m_execute)(m_table, m_ref, numparams, paramlist);
}



//**************************************************************************
//  SYMBOL TABLE
//**************************************************************************

//-------------------------------------------------
//  symbol_table - constructor
//-------------------------------------------------

symbol_table::symbol_table(void *globalref, symbol_table *parent)
	: m_parent(parent),
	  m_globalref(globalref),
	  m_memory_param(NULL),
	  m_memory_valid(NULL),
	  m_memory_read(NULL),
	  m_memory_write(NULL)
{
}


//-------------------------------------------------
//  add - add a new UINT64 pointer symbol
//-------------------------------------------------

void symbol_table::configure_memory(void *param, valid_func valid, read_func read, write_func write)
{
	m_memory_param = param;
	m_memory_valid = valid;
	m_memory_read = read;
	m_memory_write = write;
}


//-------------------------------------------------
//  add - add a new UINT64 pointer symbol
//-------------------------------------------------

void symbol_table::add(const char *name, read_write rw, UINT64 *ptr)
{
	m_symlist.remove(name);
	m_symlist.append(name, *global_alloc(integer_symbol_entry(*this, name, rw, ptr)));
}


//-------------------------------------------------
//  add - add a new value symbol
//-------------------------------------------------

void symbol_table::add(const char *name, UINT64 value)
{
	m_symlist.remove(name);
	m_symlist.append(name, *global_alloc(integer_symbol_entry(*this, name, value)));
}


//-------------------------------------------------
//  add - add a new register symbol
//-------------------------------------------------

void symbol_table::add(const char *name, void *ref, getter_func getter, setter_func setter)
{
	m_symlist.remove(name);
	m_symlist.append(name, *global_alloc(integer_symbol_entry(*this, name, ref, getter, setter)));
}


//-------------------------------------------------
//  add - add a new function symbol
//-------------------------------------------------

void symbol_table::add(const char *name, void *ref, int minparams, int maxparams, execute_func execute)
{
	m_symlist.remove(name);
	m_symlist.append(name, *global_alloc(function_symbol_entry(*this, name, ref, minparams, maxparams, execute)));
}


//-------------------------------------------------
//  find_deep - do a deep search for a symbol,
//  looking in the parent if needed
//-------------------------------------------------

symbol_entry *symbol_table::find_deep(const char *symbol)
{
	// walk up the table hierarchy to find the owner
	for (symbol_table *symtable = this; symtable != NULL; symtable = symtable->m_parent)
	{
		symbol_entry *entry = symtable->find(symbol);
		if (entry != NULL)
			return entry;
	}
	return NULL;
}


//-------------------------------------------------
//  value - return the value of a symbol
//-------------------------------------------------

UINT64 symbol_table::value(const char *symbol)
{
	symbol_entry *entry = find_deep(symbol);
	return (entry != NULL) ? entry->value() : 0;
}


//-------------------------------------------------
//  set_value - set the value of a symbol
//-------------------------------------------------

void symbol_table::set_value(const char *symbol, UINT64 value)
{
	symbol_entry *entry = find_deep(symbol);
	if (entry != NULL)
		entry->set_value(value);
}


//-------------------------------------------------
//  memory_valid - return true if the given
//  memory name/space/offset combination is valid
//-------------------------------------------------

expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space)
{
	// walk up the table hierarchy to find the owner
	for (symbol_table *symtable = this; symtable != NULL; symtable = symtable->m_parent)
		if (symtable->m_memory_valid != NULL)
		{
			expression_error::error_code err = (*symtable->m_memory_valid)(symtable->m_memory_param, name, space);
			if (err != expression_error::NO_SUCH_MEMORY_SPACE)
				return err;
		}
	return expression_error::NO_SUCH_MEMORY_SPACE;
}


//-------------------------------------------------
//  memory_value - return a value read from memory
//-------------------------------------------------

UINT64 symbol_table::memory_value(const char *name, expression_space space, UINT32 offset, int size)
{
	// walk up the table hierarchy to find the owner
	for (symbol_table *symtable = this; symtable != NULL; symtable = symtable->m_parent)
		if (symtable->m_memory_valid != NULL)
		{
			expression_error::error_code err = (*symtable->m_memory_valid)(symtable->m_memory_param, name, space);
			if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_read != NULL)
				return (*symtable->m_memory_read)(symtable->m_memory_param, name, space, offset, size);
			return 0;
		}
	return 0;
}


//-------------------------------------------------
//  set_memory_value - write a value to memory
//-------------------------------------------------

void symbol_table::set_memory_value(const char *name, expression_space space, UINT32 offset, int size, UINT64 value)
{
	// walk up the table hierarchy to find the owner
	for (symbol_table *symtable = this; symtable != NULL; symtable = symtable->m_parent)
		if (symtable->m_memory_valid != NULL)
		{
			expression_error::error_code err = (*symtable->m_memory_valid)(symtable->m_memory_param, name, space);
			if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_write != NULL)
				(*symtable->m_memory_write)(symtable->m_memory_param, name, space, offset, size, value);
			return;
		}
}



//**************************************************************************
//  PARSED EXPRESSION
//**************************************************************************

//-------------------------------------------------
//  parsed_expression - constructor
//-------------------------------------------------

parsed_expression::parsed_expression(symbol_table *symtable, const char *expression, UINT64 *result)
	: m_symtable(symtable)
{
	// if we got an expression parse it
	if (expression != NULL)
		parse(expression);

	// if we get a result pointer, execute it
	if (result != NULL)
		*result = execute();
}


//-------------------------------------------------
//  parse - parse an expression into tokens
//-------------------------------------------------

void parsed_expression::parse(const char *expression)
{
	// copy the string and reset our parsing state
	m_original_string.cpy(expression);
	m_tokenlist.reset();
	m_stringlist.reset();

	// first parse the tokens into the token array in order
	parse_string_into_tokens();

	// convert the infix order to postfix order
	infix_to_postfix();
}


//-------------------------------------------------
//  copy - copy an expression from another source
//-------------------------------------------------

void parsed_expression::copy(const parsed_expression &src)
{
	m_symtable = src.m_symtable;
	m_original_string.cpy(src.m_original_string);
	if (m_original_string)
		parse_string_into_tokens();
}


//-------------------------------------------------
//  print_tokens - debugging took to print a
//  human readable token representation
//-------------------------------------------------

void parsed_expression::print_tokens(FILE *out)
{
#if DEBUG_TOKENS
	mame_printf_debug("----\n");
	for (parse_token *token = m_tokens.first(); token != NULL; token = token->next())
	{
		switch (token->type)
		{
			default:
			case parse_token::INVALID:
				fprintf(out, "INVALID\n");
				break;

			case parse_token::END:
				fprintf(out, "END\n");
				break;

			case parse_token::NUMBER:
				fprintf(out, "NUMBER: %08X%08X\n", (UINT32)(token->value.i >> 32), (UINT32)token->value.i);
				break;

			case parse_token::STRING:
				fprintf(out, "STRING: ""%s""\n", token->string);
				break;

			case parse_token::SYMBOL:
				fprintf(out, "SYMBOL: %08X%08X\n", (UINT32)(token->value.i >> 32), (UINT32)token->value.i);
				break;

			case parse_token::OPERATOR:
				switch (token->value.i)
				{
					case TVL_LPAREN:		fprintf(out, "(\n");					break;
					case TVL_RPAREN:		fprintf(out, ")\n");					break;
					case TVL_PLUSPLUS:		fprintf(out, "++ (unspecified)\n");		break;
					case TVL_MINUSMINUS:	fprintf(out, "-- (unspecified)\n");		break;
					case TVL_PREINCREMENT:	fprintf(out, "++ (prefix)\n");			break;
					case TVL_PREDECREMENT:	fprintf(out, "-- (prefix)\n");			break;
					case TVL_POSTINCREMENT:	fprintf(out, "++ (postfix)\n");			break;
					case TVL_POSTDECREMENT:	fprintf(out, "-- (postfix)\n");			break;
					case TVL_COMPLEMENT:	fprintf(out, "!\n");					break;
					case TVL_NOT:			fprintf(out, "~\n");					break;
					case TVL_UPLUS:			fprintf(out, "+ (unary)\n");			break;
					case TVL_UMINUS:		fprintf(out, "- (unary)\n");			break;
					case TVL_MULTIPLY:		fprintf(out, "*\n");					break;
					case TVL_DIVIDE:		fprintf(out, "/\n");					break;
					case TVL_MODULO:		fprintf(out, "%%\n");					break;
					case TVL_ADD:			fprintf(out, "+\n");					break;
					case TVL_SUBTRACT:		fprintf(out, "-\n");					break;
					case TVL_LSHIFT:		fprintf(out, "<<\n");					break;
					case TVL_RSHIFT:		fprintf(out, ">>\n");					break;
					case TVL_LESS:			fprintf(out, "<\n");					break;
					case TVL_LESSOREQUAL:	fprintf(out, "<=\n");					break;
					case TVL_GREATER:		fprintf(out, ">\n");					break;
					case TVL_GREATEROREQUAL:fprintf(out, ">=\n");					break;
					case TVL_EQUAL:			fprintf(out, "==\n");					break;
					case TVL_NOTEQUAL:		fprintf(out, "!=\n");					break;
					case TVL_BAND:			fprintf(out, "&\n");					break;
					case TVL_BXOR:			fprintf(out, "^\n");					break;
					case TVL_BOR:			fprintf(out, "|\n");					break;
					case TVL_LAND:			fprintf(out, "&&\n");					break;
					case TVL_LOR:			fprintf(out, "||\n");					break;
					case TVL_ASSIGN:		fprintf(out, "=\n");					break;
					case TVL_ASSIGNMULTIPLY:fprintf(out, "*=\n");					break;
					case TVL_ASSIGNDIVIDE:	fprintf(out, "/=\n");					break;
					case TVL_ASSIGNMODULO:	fprintf(out, "%%=\n");					break;
					case TVL_ASSIGNADD:		fprintf(out, "+=\n");					break;
					case TVL_ASSIGNSUBTRACT:fprintf(out, "-=\n");					break;
					case TVL_ASSIGNLSHIFT:	fprintf(out, "<<=\n");					break;
					case TVL_ASSIGNRSHIFT:	fprintf(out, ">>=\n");					break;
					case TVL_ASSIGNBAND:	fprintf(out, "&=\n");					break;
					case TVL_ASSIGNBXOR:	fprintf(out, "^=\n");					break;
					case TVL_ASSIGNBOR:		fprintf(out, "|=\n");					break;
					case TVL_COMMA:			fprintf(out, ",\n");					break;
					case TVL_MEMORYAT:		fprintf(out, "mem@\n");					break;
					case TVL_EXECUTEFUNC:	fprintf(out, "execute\n");				break;
					default:				fprintf(out, "INVALID OPERATOR\n");		break;
				}
				break;
		}
	}
	mame_printf_debug("----\n");
#endif
}


//-------------------------------------------------
//  parse_string_into_tokens - take an expression
//  string and break it into a sequence of tokens
//-------------------------------------------------

void parsed_expression::parse_string_into_tokens()
{
	// loop until done
	const char *stringstart = m_original_string;
	const char *string = stringstart;
	while (string[0] != 0)
	{
		// ignore any whitespace
		while (string[0] != 0 && isspace((UINT8)string[0]))
			string++;
		if (string[0] == 0)
			break;

		// initialize the current token object
		parse_token &token = m_tokenlist.append(*global_alloc(parse_token(string - stringstart)));

		// switch off the first character
		switch (tolower((UINT8)string[0]))
		{
			case '(':
				string += 1, token.configure_operator(TVL_LPAREN, 0);
				break;

			case ')':
				string += 1, token.configure_operator(TVL_RPAREN, 0);
				break;

			case '~':
				string += 1, token.configure_operator(TVL_NOT, 2);
				break;

			case ',':
				string += 1, token.configure_operator(TVL_COMMA, 14);
				break;

			case '+':
				if (string[1] == '+')
					string += 2, token.configure_operator(TVL_PLUSPLUS, 1);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNADD, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_ADD, 4);
				break;

			case '-':
				if (string[1] == '-')
					string += 2, token.configure_operator(TVL_MINUSMINUS, 1);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNSUBTRACT, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_SUBTRACT, 4);
				break;

			case '*':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNMULTIPLY, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_MULTIPLY, 3);
				break;

			case '/':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNDIVIDE, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_DIVIDE, 3);
				break;

			case '%':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNMODULO, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_MODULO, 3);
				break;

			case '<':
				if (string[1] == '<' && string[2] == '=')
					string += 3, token.configure_operator(TVL_ASSIGNLSHIFT, 13).set_right_to_left();
				else if (string[1] == '<')
					string += 2, token.configure_operator(TVL_LSHIFT, 5);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_LESSOREQUAL, 6);
				else
					string += 1, token.configure_operator(TVL_LESS, 6);
				break;

			case '>':
				if (string[1] == '>' && string[2] == '=')
					string += 3, token.configure_operator(TVL_ASSIGNRSHIFT, 13).set_right_to_left();
				else if (string[1] == '>')
					string += 2, token.configure_operator(TVL_RSHIFT, 5);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_GREATEROREQUAL, 6);
				else
					string += 1, token.configure_operator(TVL_GREATER, 6);
				break;

			case '=':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_EQUAL, 7);
				else
					string += 1, token.configure_operator(TVL_ASSIGN, 13).set_right_to_left();
				break;

			case '!':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_NOTEQUAL, 7);
				else
					string += 2, token.configure_operator(TVL_COMPLEMENT, 2);
				break;

			case '&':
				if (string[1] == '&')
					string += 2, token.configure_operator(TVL_LAND, 11);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNBAND, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_BAND, 8);
				break;

			case '|':
				if (string[1] == '|')
					string += 2, token.configure_operator(TVL_LOR, 12);
				else if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNBOR, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_BOR, 10);
				break;

			case '^':
				if (string[1] == '=')
					string += 2, token.configure_operator(TVL_ASSIGNBXOR, 13).set_right_to_left();
				else
					string += 1, token.configure_operator(TVL_BXOR, 9);
				break;

			case '"':
				parse_quoted_string(token, string);
				break;

			case '\'':
				parse_quoted_char(token, string);
				break;

			default:
				parse_symbol_or_number(token, string);
				break;
		}
	}
}


//-------------------------------------------------
//  parse_symbol_or_number - parse a substring
//  into either a symbol or a number or an
//  expanded operator
//-------------------------------------------------

void parsed_expression::parse_symbol_or_number(parse_token &token, const char *&string)
{
	// accumulate a lower-case version of the symbol
	const char *stringstart = string;
	astring buffer;
	while (1)
	{
		static const char valid[] = "abcdefghijklmnopqrstuvwxyz0123456789_$#.:";
		char val = tolower((UINT8)string[0]);
		if (val == 0 || strchr(valid, val) == NULL)
			break;
		buffer.cat(&val, 1);
		string++;
	}

	// check for memory @ operators
	if (string[0] == '@')
	{
		string += 1;
		return parse_memory_operator(token, buffer);
	}

	// empty string is automatically invalid
	if (!buffer)
		throw expression_error(expression_error::INVALID_TOKEN, token.offset());

	// check for wordy variants on standard operators
	if (buffer == "bnot")
		{ token.configure_operator(TVL_NOT, 2); return; }
	if (buffer == "plus")
		{ token.configure_operator(TVL_ADD, 4); return; }
	if (buffer == "minus")
		{ token.configure_operator(TVL_SUBTRACT, 4); return; }
	if (buffer == "times" || buffer == "mul")
		{ token.configure_operator(TVL_MULTIPLY, 3); return; }
	if (buffer == "div")
		{ token.configure_operator(TVL_DIVIDE, 3); return; }
	if (buffer == "mod")
		{ token.configure_operator(TVL_MODULO, 3); return; }
	if (buffer == "lt")
		{ token.configure_operator(TVL_LESS, 6); return; }
	if (buffer == "le")
		{ token.configure_operator(TVL_LESSOREQUAL, 6); return; }
	if (buffer == "gt")
		{ token.configure_operator(TVL_GREATER, 6); return; }
	if (buffer == "ge")
		{ token.configure_operator(TVL_GREATEROREQUAL, 6); return; }
	if (buffer == "eq")
		{ token.configure_operator(TVL_EQUAL, 7); return; }
	if (buffer == "ne")
		{ token.configure_operator(TVL_NOTEQUAL, 7); return; }
	if (buffer == "not")
		{ token.configure_operator(TVL_COMPLEMENT, 2); return; }
	if (buffer == "and")
		{ token.configure_operator(TVL_LAND, 8); return; }
	if (buffer == "band")
		{ token.configure_operator(TVL_BAND, 8); return; }
	if (buffer == "or")
		{ token.configure_operator(TVL_LOR, 12); return; }
	if (buffer == "bor")
		{ token.configure_operator(TVL_BOR, 10); return; }
	if (buffer == "bxor")
		{ token.configure_operator(TVL_BXOR, 9); return; }
	if (buffer == "lshift")
		{ token.configure_operator(TVL_LSHIFT, 5); return; }
	if (buffer == "rshift")
		{ token.configure_operator(TVL_RSHIFT, 5); return; }

	// if we have an 0x prefix, we must be a hex value
	if (buffer[0] == '0' && buffer[1] == 'x')
		return parse_number(token, buffer.cstr() + 2, 16, expression_error::INVALID_NUMBER);

	// if we have a # prefix, we must be a decimal value
	if (buffer[0] == '#')
		return parse_number(token, buffer.cstr() + 1, 10, expression_error::INVALID_NUMBER);

	// if we have a $ prefix, we are a hex value
	if (buffer[0] == '$')
		return parse_number(token, buffer.cstr() + 1, 16, expression_error::INVALID_NUMBER);

	// check for a symbol match
	symbol_entry *symbol = m_symtable->find_deep(buffer);
	if (symbol != NULL)
	{
		token.configure_symbol(*symbol);

		// if this is a function symbol, synthesize an execute function operator
		if (symbol->is_function())
		{
			token = m_tokenlist.append(*global_alloc(parse_token(string - stringstart)));
			token.configure_operator(TVL_EXECUTEFUNC, 0);
		}
		return;
	}

	// attempt to parse as a number in the default base
	parse_number(token, buffer, DEFAULT_BASE, expression_error::UNKNOWN_SYMBOL);
}


//-------------------------------------------------
//  parse_number - parse a number using the
//  given base
//-------------------------------------------------

void parsed_expression::parse_number(parse_token &token, const char *string, int base, expression_error::error_code errcode)
{
	// parse the actual value
	UINT64 value = 0;
	while (*string != 0)
	{
		// look up the number's value, stopping if not valid
		static const char numbers[] = "0123456789abcdef";
		const char *ptr = strchr(numbers, tolower((UINT8)*string));
		if (ptr == NULL)
			break;

		// if outside of the base, we also stop
		int digit = ptr - numbers;
		if (digit >= base)
			break;

		// shift previous digits up and add in new digit
		value = (value * (UINT64)base) + digit;
		string++;
	}

	// if we succeeded as a number, make it so
	if (*string == 0)
		token.configure_number(value);
	else
		throw expression_error(errcode, token.offset());
}


//-------------------------------------------------
//  parse_quoted_char - parse a single-quoted
//  character constant
//-------------------------------------------------

void parsed_expression::parse_quoted_char(parse_token &token, const char *&string)
{
	// accumulate the value of the character token
	string++;
	UINT64 value = 0;
	while (string[0] != 0)
	{
		// allow '' to mean a nested single quote
		if (string[0] == '\'')
		{
			if (string[1] != '\'')
				break;
			string++;
		}
		value = (value << 8) | (UINT8)*string++;
	}

	// if we didn't find the ending quote, report an error
	if (string[0] != '\'')
		throw expression_error(expression_error::UNBALANCED_QUOTES, token.offset());
	string++;

	// make it a number token
	token.configure_number(value);
}


//-------------------------------------------------
//  parse_quoted_string - parse a double-quoted
//  string constant
//-------------------------------------------------

void parsed_expression::parse_quoted_string(parse_token &token, const char *&string)
{
	// accumulate a copy of the quoted string
	string++;
	astring buffer;
	while (string[0] != 0)
	{
		// allow "" to mean a nested double-quote
		if (string[0] == '"')
		{
			if (string[1] != '"')
				break;
			string++;
		}
		buffer.cat(string++, 1);
	}

	// if we didn't find the ending quote, report an error
	if (string[0] != '"')
		throw expression_error(expression_error::UNBALANCED_QUOTES, token.offset());
	string++;

	// make the token
	token.configure_string(m_stringlist.append(*global_alloc(expression_string(buffer))));
}


//-------------------------------------------------
//  parse_memory_operator - parse the several
//  forms of memory operators
//-------------------------------------------------

void parsed_expression::parse_memory_operator(parse_token &token, const char *string)
{
	// if there is a '.', it means we have a name
	const char *startstring = string;
	const char *namestring = NULL;
	const char *dot = strrchr(string, '.');
	if (dot != NULL)
	{
		namestring = m_stringlist.append(*global_alloc(expression_string(string, dot - string)));
		string = dot + 1;
	}

	// length 3 means logical/physical, then space, then size
	int length = (int)strlen(string);
	bool physical = false;
	int space = 'p';
	int size;
	if (length == 3)
	{
		if (string[0] != 'l' && string[0] != 'p')
			throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring));
		if (string[1] != 'p' && string[1] != 'd' && string[1] != 'i' && string[1] != '3')
			throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring));
		physical = (string[0] == 'p');
		space = string[1];
		size = string[2];
	}

	// length 2 means space then size
	else if (length == 2)
	{
		space = string[0];
		size = string[1];
	}

	// length 1 means size
	else if (length == 1)
		size = string[0];

	// anything else is invalid
	else
		throw expression_error(expression_error::INVALID_TOKEN, token.offset());

	// convert the space to flags
	expression_space memspace = EXPSPACE_INVALID;
	switch (space)
	{
		case 'p':	memspace = physical ? EXPSPACE_PROGRAM_PHYSICAL : EXPSPACE_PROGRAM_LOGICAL;	break;
		case 'd':	memspace = physical ? EXPSPACE_DATA_PHYSICAL    : EXPSPACE_DATA_LOGICAL;	break;
		case 'i':	memspace = physical ? EXPSPACE_IO_PHYSICAL      : EXPSPACE_IO_LOGICAL;		break;
		case '3':	memspace = physical ? EXPSPACE_SPACE3_PHYSICAL  : EXPSPACE_SPACE3_LOGICAL;	break;
		case 'o':	memspace = EXPSPACE_OPCODE;													break;
		case 'r':	memspace = EXPSPACE_RAMWRITE;												break;
		case 'm':	memspace = EXPSPACE_REGION;													break;
		default:	throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring));
	}

	// convert the size to flags
	int memsize;
	switch (size)
	{
		case 'b':	memsize = 0;	break;
		case 'w':	memsize = 1;	break;
		case 'd':	memsize = 2;	break;
		case 'q':	memsize = 3;	break;
		default:	throw expression_error(expression_error::INVALID_MEMORY_SIZE, token.offset() + (string - startstring) + length - 1);
	}

	// validate the name
	if (m_symtable != NULL)
	{
		expression_error::error_code err = m_symtable->memory_valid(namestring, memspace);
		if (err != expression_error::NONE)
			throw expression_error(err, token.offset() + (string - startstring));
	}

	// configure the token
	token.configure_operator(TVL_MEMORYAT, 2).set_memory_size(memsize).set_memory_space(memspace).set_memory_source(namestring);
}


//-------------------------------------------------
//  normalize_operator - resolve operator
//  ambiguities based on neighboring tokens
//-------------------------------------------------

void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &thistoken)
{
	parse_token *nexttoken = thistoken.next();
	switch (thistoken.optype())
	{
		// Determine if an open paren is part of a function or not
		case TVL_LPAREN:
			if (prevtoken != NULL && prevtoken->is_operator(TVL_EXECUTEFUNC))
				thistoken.set_function_separator();
			break;

		// Determine if ++ is a pre or post increment
		case TVL_PLUSPLUS:
			if (nexttoken != NULL && (nexttoken->is_symbol() || (nexttoken->is_operator(TVL_MEMORYAT))))
				thistoken.configure_operator(TVL_PREINCREMENT, 2);
			else if (prevtoken != NULL && (prevtoken->is_symbol() || (prevtoken->is_operator(TVL_MEMORYAT))))
				thistoken.configure_operator(TVL_POSTINCREMENT, 1);
			else
				throw expression_error(expression_error::SYNTAX, thistoken.offset());
			break;

		// Determine if -- is a pre or post decrement
		case TVL_MINUSMINUS:
			if (nexttoken != NULL && (nexttoken->is_symbol() || (nexttoken->is_operator(TVL_MEMORYAT))))
				thistoken.configure_operator(TVL_PREDECREMENT, 2);
			else if (prevtoken != NULL && (prevtoken->is_symbol() || (prevtoken->is_operator(TVL_MEMORYAT))))
				thistoken.configure_operator(TVL_POSTDECREMENT, 1);
			else
				throw expression_error(expression_error::SYNTAX, thistoken.offset());
			break;

		// Determine if +/- is a unary or binary
		case TVL_ADD:
		case TVL_SUBTRACT:
			// Assume we're unary if we are the first token, or if the previous token is not
            // a symbol, a number, or a right parenthesis
			if (prevtoken == NULL || (!prevtoken->is_symbol() && !prevtoken->is_number() && !prevtoken->is_operator(TVL_RPAREN)))
				thistoken.configure_operator(thistoken.is_operator(TVL_ADD) ? TVL_UPLUS : TVL_UMINUS, 2);
			break;

		// Determine if , refers to a function parameter
		case TVL_COMMA:
			for (int lookback = 0; lookback < MAX_STACK_DEPTH; lookback++)
			{
				parse_token *peek = peek_token(lookback);
				if (peek == NULL)
					break;

				// if we hit an execute function operator, or else a left parenthesis that is
				// already tagged, then tag us as well
				if (peek->is_operator(TVL_EXECUTEFUNC) || (peek->is_operator(TVL_LPAREN) && peek->is_function_separator()))
				{
					thistoken.set_function_separator();
					break;
				}
			}
			break;
	}
}


//-------------------------------------------------
//  infix_to_postfix - convert an infix sequence
//  of tokens to a postfix sequence for processing
//-------------------------------------------------

void parsed_expression::infix_to_postfix()
{
	simple_list<parse_token> stack;

	// loop over all the original tokens
	parse_token *prev = NULL;
	parse_token *next = NULL;
	for (parse_token *token = m_tokenlist.detach_all(); token != NULL; prev = token, token = next)
	{
		// pre-determine our next token
		next = token->next();

		// if the character is an operand, append it to the result string
		if (token->is_number() || token->is_symbol() || token->is_string())
			m_tokenlist.append(*token);

		// if this is an operator, process it
		else if (token->is_operator())
		{
			// normalize the operator based on neighbors
			normalize_operator(prev, *token);

			// if the token is an opening parenthesis, push it onto the stack.
			if (token->is_operator(TVL_LPAREN))
				stack.prepend(*token);

			// if the token is a closing parenthesis, pop all operators until we
            // reach an opening parenthesis and append them to the result string,
            // discaring the open parenthesis
			else if (token->is_operator(TVL_RPAREN))
			{
				// loop until we find our matching opener
				parse_token *popped;
				while ((popped = stack.detach_head()) != NULL)
				{
					if (popped->is_operator(TVL_LPAREN))
						break;
					m_tokenlist.append(*popped);
				}

				// if we didn't find an open paren, it's an error
				if (popped == NULL)
					throw expression_error(expression_error::UNBALANCED_PARENS, token->offset());

				// free ourself and our matching opening parenthesis
				global_free(token);
				global_free(popped);
			}

			// if the token is an operator, pop operators until we reach an opening parenthesis,
            // an operator of lower precedence, or a right associative symbol of equal precedence.
            // Push the operator onto the stack.
			else
			{
				int our_precedence = token->precedence();

				// loop until we can't peek at the stack anymore
				parse_token *peek;
				while ((peek = stack.first()) != NULL)
				{
					// break if any of the above conditions are true
					if (peek->is_operator(TVL_LPAREN))
						break;
					int stack_precedence = peek->precedence();
					if (stack_precedence > our_precedence || (stack_precedence == our_precedence && peek->right_to_left()))
						break;

					// pop this token
					m_tokenlist.append(*stack.detach_head());
				}

				// push the new operator
				stack.prepend(*token);
			}
		}
	}

	// finish popping the stack
	parse_token *popped;
	while ((popped = stack.detach_head()) != NULL)
	{
		// it is an error to have a left parenthesis still on the stack
		if (popped->is_operator(TVL_LPAREN))
			throw expression_error(expression_error::UNBALANCED_PARENS, popped->offset());

		// pop this token
		m_tokenlist.append(*popped);
	}
}


//-------------------------------------------------
//  push_token - push a token onto the stack
//-------------------------------------------------

inline void parsed_expression::push_token(parse_token &token)
{
	// check for overflow
	if (m_token_stack_ptr >= MAX_STACK_DEPTH)
		throw expression_error(expression_error::STACK_OVERFLOW, token.offset());

	// push
	m_token_stack[m_token_stack_ptr++] = token;
}


//-------------------------------------------------
//  pop_token - pop a token off the stack
//-------------------------------------------------

inline void parsed_expression::pop_token(parse_token &token)
{
	// check for underflow
	if (m_token_stack_ptr == 0)
		throw expression_error(expression_error::STACK_UNDERFLOW, token.offset());

	// pop
	token = m_token_stack[--m_token_stack_ptr];
}


//-------------------------------------------------
//  peek_token - look at a token some number of
//  entries up the stack
//-------------------------------------------------

inline parsed_expression::parse_token *parsed_expression::peek_token(int count)
{
	if (m_token_stack_ptr <= count)
		return NULL;
	return &m_token_stack[m_token_stack_ptr - count - 1];
}


//-------------------------------------------------
//  pop_token_lval - pop a token off the stack
//  and ensure that it is a proper lval
//-------------------------------------------------

inline void parsed_expression::pop_token_lval(parse_token &token)
{
	// start with normal pop
	pop_token(token);

	// if we're not an lval, throw an error
	if (!token.is_lval())
		throw expression_error(expression_error::NOT_LVAL, token.offset());
}


//-------------------------------------------------
//  pop_token_rval - pop a token off the stack
//  and ensure that it is a proper rval
//-------------------------------------------------

inline void parsed_expression::pop_token_rval(parse_token &token)
{
	// start with normal pop
	pop_token(token);

	// symbol and memory tokens get resolved down to number tokens
	if (token.is_symbol() || token.is_memory())
		token.configure_number(token.get_lval_value(m_symtable));

	// to be an rval, the final token must be a number
	if (!token.is_number())
		throw expression_error(expression_error::NOT_RVAL, token.offset());
}


//-------------------------------------------------
//  execute_tokens - execute a postfix sequence
//  of tokens
//-------------------------------------------------

UINT64 parsed_expression::execute_tokens()
{
	// reset the token stack
	m_token_stack_ptr = 0;

	// loop over the entire sequence
	parse_token t1, t2, result;
	for (parse_token *token = m_tokenlist.first(); token != NULL; token = token->next())
	{
		// symbols/numbers/strings just get pushed
		if (!token->is_operator())
		{
			push_token(*token);
			continue;
		}

		// otherwise, switch off the operator
		switch (token->optype())
		{
			case TVL_PREINCREMENT:
				pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) + 1).set_offset(t1));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_PREDECREMENT:
				pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) - 1).set_offset(t1));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_POSTINCREMENT:
				pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable)).set_offset(t1));
				t1.set_lval_value(m_symtable, result.value() + 1);
				break;

			case TVL_POSTDECREMENT:
				pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable)).set_offset(t1));
				t1.set_lval_value(m_symtable, result.value() - 1);
				break;

			case TVL_COMPLEMENT:
				pop_token_rval(t1);
				push_token(result.configure_number(!t1.value()).set_offset(t1));
				break;

			case TVL_NOT:
				pop_token_rval(t1);
				push_token(result.configure_number(~t1.value()).set_offset(t1));
				break;

			case TVL_UPLUS:
				pop_token_rval(t1);
				push_token(result.configure_number(t1.value()).set_offset(t1));
				break;

			case TVL_UMINUS:
				pop_token_rval(t1);
				push_token(result.configure_number(-t1.value()).set_offset(t1));
				break;

			case TVL_MULTIPLY:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() * t2.value()).set_offset(t1, t2));
				break;

			case TVL_DIVIDE:
				pop_token_rval(t2); pop_token_rval(t1);
				if (t2.value() == 0)
					throw expression_error(expression_error::DIVIDE_BY_ZERO, t2.offset());
				push_token(result.configure_number(t1.value() / t2.value()).set_offset(t1, t2));
				break;

			case TVL_MODULO:
				pop_token_rval(t2); pop_token_rval(t1);
				if (t2.value() == 0)
					throw expression_error(expression_error::DIVIDE_BY_ZERO, t2.offset());
				push_token(result.configure_number(t1.value() % t2.value()).set_offset(t1, t2));
				break;

			case TVL_ADD:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() + t2.value()).set_offset(t1, t2));
				break;

			case TVL_SUBTRACT:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() - t2.value()).set_offset(t1, t2));
				break;

			case TVL_LSHIFT:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() << t2.value()).set_offset(t1, t2));
				break;

			case TVL_RSHIFT:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() >> t2.value()).set_offset(t1, t2));
				break;

			case TVL_LESS:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() < t2.value()).set_offset(t1, t2));
				break;

			case TVL_LESSOREQUAL:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() <= t2.value()).set_offset(t1, t2));
				break;

			case TVL_GREATER:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() > t2.value()).set_offset(t1, t2));
				break;

			case TVL_GREATEROREQUAL:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() >= t2.value()).set_offset(t1, t2));
				break;

			case TVL_EQUAL:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() == t2.value()).set_offset(t1, t2));
				break;

			case TVL_NOTEQUAL:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() != t2.value()).set_offset(t1, t2));
				break;

			case TVL_BAND:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() & t2.value()).set_offset(t1, t2));
				break;

			case TVL_BXOR:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() ^ t2.value()).set_offset(t1, t2));
				break;

			case TVL_BOR:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() | t2.value()).set_offset(t1, t2));
				break;

			case TVL_LAND:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() && t2.value()).set_offset(t1, t2));
				break;

			case TVL_LOR:
				pop_token_rval(t2); pop_token_rval(t1);
				push_token(result.configure_number(t1.value() || t2.value()).set_offset(t1, t2));
				break;

			case TVL_ASSIGN:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t2.value()).set_offset(t2));
				t1.set_lval_value(m_symtable, t2.value());
				break;

			case TVL_ASSIGNMULTIPLY:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) * t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNDIVIDE:
				pop_token_rval(t2); pop_token_lval(t1);
				if (t2.value() == 0)
					throw expression_error(expression_error::DIVIDE_BY_ZERO, t2.offset());
				push_token(result.configure_number(t1.get_lval_value(m_symtable) / t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNMODULO:
				pop_token_rval(t2); pop_token_lval(t1);
				if (t2.value() == 0)
					throw expression_error(expression_error::DIVIDE_BY_ZERO, t2.offset());
				push_token(result.configure_number(t1.get_lval_value(m_symtable) % t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNADD:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) + t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNSUBTRACT:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) - t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNLSHIFT:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) << t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNRSHIFT:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) >> t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNBAND:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) & t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNBXOR:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) ^ t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_ASSIGNBOR:
				pop_token_rval(t2); pop_token_lval(t1);
				push_token(result.configure_number(t1.get_lval_value(m_symtable) | t2.value()).set_offset(t1, t2));
				t1.set_lval_value(m_symtable, result.value());
				break;

			case TVL_COMMA:
				if (!token->is_function_separator())
				{
					pop_token_rval(t2); pop_token_rval(t1);
					push_token(t2);
				}
				break;

			case TVL_MEMORYAT:
				pop_token_rval(t1);
				push_token(result.configure_memory(t1.value(), *token));
				break;

			case TVL_EXECUTEFUNC:
				execute_function(*token);
				break;

			default:
				throw expression_error(expression_error::SYNTAX, token->offset());
		}
	}

	// pop the final result
	pop_token_rval(result);

	// error if our stack isn't empty
	if (peek_token(0) != NULL)
		throw expression_error(expression_error::SYNTAX, 0);

	return result.value();
}



//**************************************************************************
//  PARSE TOKEN
//**************************************************************************

//-------------------------------------------------
//  parse_token - constructor
//-------------------------------------------------

parsed_expression::parse_token::parse_token(int offset)
	: m_next(NULL),
	  m_type(INVALID),
	  m_offset(offset),
	  m_value(0),
	  m_flags(0),
	  m_string(NULL),
	  m_symbol(NULL)
{
}


//-------------------------------------------------
//  get_lval_value - call the getter function
//  for a SYMBOL token
//-------------------------------------------------

UINT64 parsed_expression::parse_token::get_lval_value(symbol_table *table)
{
	// get the value of a symbol
	if (is_symbol())
		return m_symbol->value();

	// or get the value from the memory callbacks
	else if (is_memory() && table != NULL)
		return table->memory_value(m_string, memory_space(), address(), 1 << memory_size());

	return 0;
}


//-------------------------------------------------
//  set_lval_value - call the setter function
//  for a SYMBOL token
//-------------------------------------------------

inline void parsed_expression::parse_token::set_lval_value(symbol_table *table, UINT64 value)
{
	// set the value of a symbol
	if (is_symbol())
		m_symbol->set_value(value);

	// or set the value via the memory callbacks
	else if (is_memory() && table != NULL)
		table->set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value);
}


//-------------------------------------------------
//  execute_function - handle an execute function
//  operator
//-------------------------------------------------

void parsed_expression::execute_function(parse_token &token)
{
	// pop off all pushed parameters
	UINT64 funcparams[MAX_FUNCTION_PARAMS];
	symbol_entry *symbol = NULL;
	int paramcount = 0;
	while (paramcount < MAX_FUNCTION_PARAMS)
	{
		// peek at the next token on the stack
		parse_token *peek = peek_token(0);
		if (peek == NULL)
			throw expression_error(expression_error::INVALID_PARAM_COUNT, token.offset());

		// if it is a function symbol, break out of the loop
		if (peek->is_symbol())
		{
			symbol = peek->symbol();
			if (symbol->is_function())
			{
				parse_token t1;
				pop_token(t1);
				break;
			}
		}

		// otherwise, pop as a standard rval
		parse_token t1;
		pop_token_rval(t1);
		funcparams[MAX_FUNCTION_PARAMS - (++paramcount)] = t1.value();
	}

	// if we didn't find the symbol, fail
	if (paramcount == MAX_FUNCTION_PARAMS)
		throw expression_error(expression_error::INVALID_PARAM_COUNT, token.offset());

	// execute the function and push the result
	function_symbol_entry *function = downcast<function_symbol_entry *>(symbol);
	parse_token result(token.offset());
	result.configure_number(function->execute(paramcount, &funcparams[MAX_FUNCTION_PARAMS - paramcount]));
	push_token(result);
}
