// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    express.c

    Generic expressions engine.

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

    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),
	m_token_stack_ptr(0)
{
	// 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
	osd_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;
		}
	}
	osd_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())
		{
			parse_token &newtoken = m_tokenlist.append(*global_alloc(parse_token(string - stringstart)));
			newtoken.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);
}
