summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/debug/express.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug/express.c')
-rw-r--r--src/emu/debug/express.c1953
1 files changed, 1953 insertions, 0 deletions
diff --git a/src/emu/debug/express.c b/src/emu/debug/express.c
new file mode 100644
index 00000000000..22aa49d9782
--- /dev/null
+++ b/src/emu/debug/express.c
@@ -0,0 +1,1953 @@
+/***************************************************************************
+
+ express.c
+ Generic expressions engine.
+ Written by Aaron Giles
+ Copyright (c) 2005, Aaron Giles
+
+***************************************************************************/
+
+#include "express.h"
+#include <ctype.h>
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define DEBUG_TOKENS 0
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#ifndef DEFAULT_BASE
+#define DEFAULT_BASE 16 /* hex unless otherwise specified */
+#endif
+
+#define MAX_TOKENS 128
+#define MAX_STACK_DEPTH 128
+#define MAX_STRING_LENGTH 256
+#define MAX_EXPRESSION_STRINGS 64
+#define MAX_SYMBOL_LENGTH 64
+
+#define SYM_TABLE_HASH_SIZE 97
+
+/* token.type values */
+enum
+{
+ TOK_INVALID = 0,
+ TOK_END,
+ TOK_NUMBER,
+ TOK_STRING,
+ TOK_MEMORY,
+ TOK_SYMBOL,
+ TOK_OPERATOR
+};
+
+
+/* token.info values */
+enum
+{
+ TIN_PRECEDENCE_MASK = 0x001f,
+ TIN_PRECEDENCE_0 = 0x0000, /* ( ) */
+ TIN_PRECEDENCE_1 = 0x0001, /* ++ (postfix), -- (postfix) */
+ TIN_PRECEDENCE_2 = 0x0002, /* ++ (prefix), -- (prefix), ~, !, - (unary), + (unary), b@, w@, d@, q@ */
+ TIN_PRECEDENCE_3 = 0x0003, /* *, /, % */
+ TIN_PRECEDENCE_4 = 0x0004, /* + - */
+ TIN_PRECEDENCE_5 = 0x0005, /* << >> */
+ TIN_PRECEDENCE_6 = 0x0006, /* < <= > >= */
+ TIN_PRECEDENCE_7 = 0x0007, /* == != */
+ TIN_PRECEDENCE_8 = 0x0008, /* & */
+ TIN_PRECEDENCE_9 = 0x0009, /* ^ */
+ TIN_PRECEDENCE_10 = 0x000a, /* | */
+ TIN_PRECEDENCE_11 = 0x000b, /* && */
+ TIN_PRECEDENCE_12 = 0x000c, /* || */
+ TIN_PRECEDENCE_13 = 0x000d, /* = *= /= %= += -= <<= >>= &= |= ^= */
+ TIN_PRECEDENCE_14 = 0x000e, /* , */
+ TIN_PRECEDENCE_15 = 0x000f, /* func() */
+
+ TIN_RIGHT_TO_LEFT = 0x0040,
+ TIN_FUNCTION = 0x0080,
+
+ TIN_MEMORY_SIZE_SHIFT = 8,
+ TIN_MEMORY_SIZE_MASK = (3 << TIN_MEMORY_SIZE_SHIFT),
+ TIN_MEMORY_BYTE = (0 << TIN_MEMORY_SIZE_SHIFT),
+ TIN_MEMORY_WORD = (1 << TIN_MEMORY_SIZE_SHIFT),
+ TIN_MEMORY_DWORD = (2 << TIN_MEMORY_SIZE_SHIFT),
+ TIN_MEMORY_QWORD = (3 << TIN_MEMORY_SIZE_SHIFT),
+
+ TIN_MEMORY_SPACE_SHIFT = 10,
+ TIN_MEMORY_SPACE_MASK = (3 << TIN_MEMORY_SPACE_SHIFT),
+ TIN_MEMORY_PROGRAM = (EXPSPACE_PROGRAM << TIN_MEMORY_SPACE_SHIFT),
+ TIN_MEMORY_DATA = (EXPSPACE_DATA << TIN_MEMORY_SPACE_SHIFT),
+ TIN_MEMORY_IO = (EXPSPACE_IO << TIN_MEMORY_SPACE_SHIFT),
+};
+
+
+/* token.value values if token.type == TOK_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
+***************************************************************************/
+
+typedef union _int_ptr int_ptr;
+union _int_ptr
+{
+ void * p; /* pointer value */
+ UINT64 i; /* integer value */
+};
+
+
+typedef struct _parse_token parse_token;
+struct _parse_token
+{
+ UINT16 type; /* type of token */
+ UINT16 info; /* info for token */
+ UINT32 offset; /* offset within the string */
+ int_ptr value; /* value of token */
+};
+
+
+typedef struct _internal_symbol_entry internal_symbol_entry;
+struct _internal_symbol_entry
+{
+ internal_symbol_entry *next; /* pointer to the next entry */
+ const char * name; /* name of the symbol */
+ symbol_entry entry; /* actual entry data */
+};
+
+
+/* typedef struct _symbol_table symbol_table -- defined in express.h */
+struct _symbol_table
+{
+ symbol_table *parent; /* pointer to the parent symbol table */
+ internal_symbol_entry *hash[SYM_TABLE_HASH_SIZE]; /* hash table */
+};
+
+
+/* typedef struct _parsed_expression parsed_expression -- defined in express.h */
+struct _parsed_expression
+{
+ const symbol_table *table; /* symbol table */
+ char * original_string;/* original string (prior to parsing) */
+ char * string[MAX_EXPRESSION_STRINGS]; /* string table */
+ parse_token token[MAX_TOKENS];/* array of tokens */
+ int token_stack_ptr;/* stack poointer */
+ parse_token token_stack[MAX_STACK_DEPTH];/* token stack */
+};
+
+
+
+/***************************************************************************
+ STACK MANIPULATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ init_token_stack - reset the token stack
+-------------------------------------------------*/
+
+INLINE void init_token_stack(parsed_expression *expr)
+{
+ expr->token_stack_ptr = 0;
+}
+
+
+/*-------------------------------------------------
+ push_token - push a token onto the stack
+-------------------------------------------------*/
+
+INLINE EXPRERR push_token(parsed_expression *expr, parse_token *token)
+{
+ /* check for overflow */
+ if (expr->token_stack_ptr >= MAX_STACK_DEPTH)
+ return MAKE_EXPRERR_STACK_OVERFLOW(token->offset);
+
+ /* push */
+ expr->token_stack[expr->token_stack_ptr++] = *token;
+ return EXPRERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ pop_token - pop a token off the stack
+-------------------------------------------------*/
+
+INLINE EXPRERR pop_token(parsed_expression *expr, parse_token *token)
+{
+ /* check for underflow */
+ if (expr->token_stack_ptr == 0)
+ return MAKE_EXPRERR_STACK_UNDERFLOW(token->offset);
+
+ /* push */
+ *token = expr->token_stack[--expr->token_stack_ptr];
+ return EXPRERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ peek_token - look at a token some number of
+ entries up the stack
+-------------------------------------------------*/
+
+INLINE parse_token *peek_token(parsed_expression *expr, int count)
+{
+ if (expr->token_stack_ptr <= count)
+ return NULL;
+ return &expr->token_stack[expr->token_stack_ptr - count - 1];
+}
+
+
+
+/***************************************************************************
+ LVAL/RVAL EVALUATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ pop_token_lval - pop a token off the stack
+ and ensure that it is a proper lval
+-------------------------------------------------*/
+
+INLINE EXPRERR pop_token_lval(parsed_expression *expr, parse_token *token, const symbol_table *table)
+{
+ /* check for underflow */
+ if (expr->token_stack_ptr == 0)
+ return MAKE_EXPRERR_STACK_UNDERFLOW(token->offset);
+
+ /* pop */
+ *token = expr->token_stack[--expr->token_stack_ptr];
+
+ /* to be an lval, the token must be a valid read/write symbol or a memory token */
+ if (token->type == TOK_SYMBOL)
+ {
+ symbol_entry *symbol = token->value.p;
+ if (symbol == NULL || symbol->type != SMT_REGISTER || symbol->info.reg.setter == NULL)
+ return MAKE_EXPRERR_NOT_LVAL(token->offset);
+ }
+ else if (token->type != TOK_MEMORY)
+ return MAKE_EXPRERR_NOT_LVAL(token->offset);
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ pop_token_rval - pop a token off the stack
+ and ensure that it is a proper rval
+-------------------------------------------------*/
+
+INLINE EXPRERR pop_token_rval(parsed_expression *expr, parse_token *token, const symbol_table *table)
+{
+ /* check for underflow */
+ if (expr->token_stack_ptr == 0)
+ return MAKE_EXPRERR_STACK_UNDERFLOW(token->offset);
+
+ /* pop */
+ *token = expr->token_stack[--expr->token_stack_ptr];
+
+ /* symbol tokens get resolved down to number tokens */
+ if (token->type == TOK_SYMBOL)
+ {
+ symbol_entry *symbol = token->value.p;
+ if (symbol == NULL || (symbol->type != SMT_REGISTER && symbol->type != SMT_VALUE))
+ return MAKE_EXPRERR_NOT_RVAL(token->offset);
+ token->type = TOK_NUMBER;
+ if (symbol->type == SMT_REGISTER)
+ token->value.i = (*symbol->info.reg.getter)(symbol->ref);
+ else
+ token->value.i = symbol->info.gen.value;
+ }
+
+ /* memory tokens get resolved down to number tokens */
+ else if (token->type == TOK_MEMORY)
+ {
+ int space = (token->info & TIN_MEMORY_SPACE_MASK) >> TIN_MEMORY_SPACE_SHIFT;
+ int size = (token->info & TIN_MEMORY_SIZE_MASK) >> TIN_MEMORY_SIZE_SHIFT;
+ token->type = TOK_NUMBER;
+ token->value.i = external_read_memory(space, token->value.i, 1 << size);
+ }
+
+ /* to be an rval, the token must be a number */
+ if (token->type != TOK_NUMBER)
+ return MAKE_EXPRERR_NOT_RVAL(token->offset);
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ get_lval_value - call the getter function
+ for a SYMBOL token
+-------------------------------------------------*/
+
+INLINE UINT64 get_lval_value(parse_token *token, const symbol_table *table)
+{
+ if (token->type == TOK_SYMBOL)
+ {
+ symbol_entry *symbol = token->value.p;
+ if (symbol != NULL && symbol->type == SMT_REGISTER)
+ return (*symbol->info.reg.getter)(symbol->ref);
+ }
+ else if (token->type == TOK_MEMORY)
+ {
+ int space = (token->info & TIN_MEMORY_SPACE_MASK) >> TIN_MEMORY_SPACE_SHIFT;
+ int size = (token->info & TIN_MEMORY_SIZE_MASK) >> TIN_MEMORY_SIZE_SHIFT;
+ return external_read_memory(space, token->value.i, 1 << size);
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ set_lval_value - call the setter function
+ for a SYMBOL token
+-------------------------------------------------*/
+
+INLINE void set_lval_value(parse_token *token, const symbol_table *table, UINT64 value)
+{
+ if (token->type == TOK_SYMBOL)
+ {
+ symbol_entry *symbol = token->value.p;
+ if (symbol != NULL && symbol->type == SMT_REGISTER && symbol->info.reg.setter)
+ (*symbol->info.reg.setter)(symbol->ref, value);
+ }
+ else if (token->type == TOK_MEMORY)
+ {
+ int space = (token->info & TIN_MEMORY_SPACE_MASK) >> TIN_MEMORY_SPACE_SHIFT;
+ int size = (token->info & TIN_MEMORY_SIZE_MASK) >> TIN_MEMORY_SIZE_SHIFT;
+ external_write_memory(space, token->value.i, 1 << size, value);
+ }
+}
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+/*-------------------------------------------------
+ print_token - debugging took to print a
+ human readable token representation
+-------------------------------------------------*/
+
+static void print_tokens(FILE *out, parsed_expression *expr)
+{
+#if DEBUG_TOKENS
+ parse_token *token = expr->token;
+
+ mame_printf_debug("----\n");
+ while (token->type != TOK_END)
+ {
+ switch (token->type)
+ {
+ default:
+ case TOK_INVALID:
+ fprintf(out, "INVALID\n");
+ break;
+
+ case TOK_END:
+ fprintf(out, "END\n");
+ break;
+
+ case TOK_NUMBER:
+ fprintf(out, "NUMBER: %08X%08X\n", (UINT32)(token->value.i >> 32), (UINT32)token->value.i);
+ break;
+
+ case TOK_STRING:
+ fprintf(out, "STRING: ""%s""\n", (char *)token->value.p);
+ break;
+
+ case TOK_SYMBOL:
+ fprintf(out, "SYMBOL: %08X%08X\n", (UINT32)(token->value.i >> 32), (UINT32)token->value.i);
+ break;
+
+ case TOK_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;
+ }
+ token++;
+ }
+ mame_printf_debug("----\n");
+#endif
+}
+
+
+
+/***************************************************************************
+ TOKENIZING
+***************************************************************************/
+
+/*-------------------------------------------------
+ parse_memory_operator - parse the several
+ forms of memory operators
+-------------------------------------------------*/
+
+static int parse_memory_operator(const char *buffer, UINT16 *flags)
+{
+ int length = (int)strlen(buffer);
+ int space = 'p', size;
+
+ *flags = 0;
+
+ /* length 2 means space, then size */
+ if (length == 2)
+ {
+ space = buffer[0];
+ size = buffer[1];
+ }
+
+ /* length 1 means size */
+ else if (length == 1)
+ size = buffer[0];
+
+ /* anything else is invalid */
+ else
+ return 0;
+
+ /* convert the space to flags */
+ switch (space)
+ {
+ case 'p': *flags |= TIN_MEMORY_PROGRAM; break;
+ case 'd': *flags |= TIN_MEMORY_DATA; break;
+ case 'i': *flags |= TIN_MEMORY_IO; break;
+ default: return 0;
+ }
+
+ /* convert the size to flags */
+ switch (size)
+ {
+ case 'b': *flags |= TIN_MEMORY_BYTE; break;
+ case 'w': *flags |= TIN_MEMORY_WORD; break;
+ case 'd': *flags |= TIN_MEMORY_DWORD; break;
+ case 'q': *flags |= TIN_MEMORY_QWORD; break;
+ default: return 0;
+ }
+ return 1;
+}
+
+
+/*-------------------------------------------------
+ parse_string_into_tokens - take an expression
+ string and break it into a sequence of tokens
+-------------------------------------------------*/
+
+static EXPRERR parse_string_into_tokens(const char *stringstart, parsed_expression *expr, const symbol_table *table)
+{
+ #define SET_TOKEN_INFO(_length, _type, _value, _info) \
+ do { \
+ token->type = _type; \
+ token->value.i = _value; \
+ token->info = _info; \
+ string += _length; \
+ } while (0)
+
+ parse_token *token = expr->token;
+ const char *string = stringstart;
+
+ /* zap expression object */
+ memset(expr, 0, sizeof(*expr));
+
+ /* stash the symbol table pointer */
+ expr->table = table;
+
+ /* make a copy of the original string */
+ expr->original_string = malloc(strlen(stringstart) + 1);
+ if (!expr->original_string)
+ return MAKE_EXPRERR_OUT_OF_MEMORY(0);
+ strcpy(expr->original_string, stringstart);
+
+ /* loop until done */
+ while (string[0] != 0 && token - expr->token < MAX_TOKENS)
+ {
+ /* ignore any whitespace */
+ while (string[0] != 0 && isspace(string[0]))
+ string++;
+ if (string[0] == 0)
+ break;
+
+ /* initialize the current token object */
+ SET_TOKEN_INFO(0, TOK_INVALID, 0, 0);
+ token->offset = string - stringstart;
+
+ /* switch off the first character */
+ switch (tolower(string[0]))
+ {
+ case '(':
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_LPAREN, TIN_PRECEDENCE_0);
+ break;
+
+ case ')':
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_RPAREN, TIN_PRECEDENCE_0);
+ break;
+
+ case '~':
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_NOT, TIN_PRECEDENCE_2);
+ break;
+
+ case ',':
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_COMMA, TIN_PRECEDENCE_14);
+ break;
+
+ case '+':
+ if (string[1] == '+')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_PLUSPLUS, TIN_PRECEDENCE_1);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNADD, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_ADD, TIN_PRECEDENCE_4);
+ break;
+
+ case '-':
+ if (string[1] == '-')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_MINUSMINUS, TIN_PRECEDENCE_1);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNSUBTRACT, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_SUBTRACT, TIN_PRECEDENCE_4);
+ break;
+
+ case '*':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNMULTIPLY, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_MULTIPLY, TIN_PRECEDENCE_3);
+ break;
+
+ case '/':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNDIVIDE, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_DIVIDE, TIN_PRECEDENCE_3);
+ break;
+
+ case '%':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNMODULO, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_MODULO, TIN_PRECEDENCE_3);
+ break;
+
+ case '<':
+ if (string[1] == '<' && string[2] == '=')
+ SET_TOKEN_INFO(3, TOK_OPERATOR, TVL_ASSIGNLSHIFT, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else if (string[1] == '<')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_LSHIFT, TIN_PRECEDENCE_5);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_LESSOREQUAL, TIN_PRECEDENCE_6);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_LESS, TIN_PRECEDENCE_6);
+ break;
+
+ case '>':
+ if (string[1] == '>' && string[2] == '=')
+ SET_TOKEN_INFO(3, TOK_OPERATOR, TVL_ASSIGNRSHIFT, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else if (string[1] == '>')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_RSHIFT, TIN_PRECEDENCE_5);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_GREATEROREQUAL, TIN_PRECEDENCE_6);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_GREATER, TIN_PRECEDENCE_6);
+ break;
+
+ case '=':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_EQUAL, TIN_PRECEDENCE_7);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_ASSIGN, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ break;
+
+ case '!':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_NOTEQUAL, TIN_PRECEDENCE_7);
+ else
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_COMPLEMENT, TIN_PRECEDENCE_2);
+ break;
+
+ case '&':
+ if (string[1] == '&')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_LAND, TIN_PRECEDENCE_11);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNBAND, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_BAND, TIN_PRECEDENCE_8);
+ break;
+
+ case '|':
+ if (string[1] == '|')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_LOR, TIN_PRECEDENCE_12);
+ else if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNBOR, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_BOR, TIN_PRECEDENCE_10);
+ break;
+
+ case '^':
+ if (string[1] == '=')
+ SET_TOKEN_INFO(2, TOK_OPERATOR, TVL_ASSIGNBXOR, TIN_PRECEDENCE_13 | TIN_RIGHT_TO_LEFT);
+ else
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_BXOR, TIN_PRECEDENCE_9);
+ break;
+
+ case '"':
+ {
+ char buffer[MAX_STRING_LENGTH];
+ int bufindex = 0, strindex;
+
+ /* find a free string entry */
+ for (strindex = 0; strindex < MAX_EXPRESSION_STRINGS; strindex++)
+ if (expr->string[strindex] == NULL)
+ break;
+ if (strindex == MAX_EXPRESSION_STRINGS)
+ return MAKE_EXPRERR_TOO_MANY_STRINGS(token->offset);
+
+ /* accumulate a copy of the string */
+ string++;
+ while (string[0] != 0 && bufindex < MAX_STRING_LENGTH)
+ {
+ /* allow "" to mean a nested double-quote */
+ if (string[0] == '"')
+ {
+ if (string[1] != '"')
+ break;
+ string++;
+ }
+ buffer[bufindex++] = *string++;
+ }
+
+ /* if we didn't find the ending quote, report an error */
+ if (string[0] != '"')
+ return MAKE_EXPRERR_UNBALANCED_QUOTES(token->offset);
+ string++;
+
+ /* terminate the string and allocate memory */
+ buffer[bufindex++] = 0;
+ expr->string[strindex] = malloc(bufindex);
+ if (!expr->string[strindex])
+ return MAKE_EXPRERR_OUT_OF_MEMORY(token->offset);
+
+ /* copy the string in and make the token */
+ strcpy(expr->string[strindex], buffer);
+ token->type = TOK_STRING;
+ token->value.p = expr->string[strindex];
+ break;
+ }
+
+ case '\'':
+ {
+ UINT64 value = 0;
+
+ /* accumulate the value of the character token */
+ string++;
+ 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] != '\'')
+ return MAKE_EXPRERR_UNBALANCED_QUOTES(token->offset);
+ string++;
+
+ /* make it a number token */
+ token->type = TOK_NUMBER;
+ token->value.i = value;
+ break;
+ }
+
+ default:
+ {
+ static const char valid[] = "abcdefghijklmnopqrstuvwxyz0123456789_$#.";
+ static const char numbers[] = "0123456789abcdef";
+ int bufindex = 0, must_be_number = 0, numbase = DEFAULT_BASE;
+ char buffer[MAX_SYMBOL_LENGTH];
+ UINT64 value;
+
+ /* accumulate a lower-case version of the symbol */
+ while (1)
+ {
+ char val = tolower(string[0]);
+ if (val == 0 || strchr(valid, val) == NULL)
+ break;
+ buffer[bufindex++] = val;
+ string++;
+ }
+ buffer[bufindex] = 0;
+
+ /* check for memory @ operators */
+ if (string[0] == '@')
+ {
+ UINT16 info;
+ if (parse_memory_operator(buffer, &info))
+ {
+ SET_TOKEN_INFO(1, TOK_OPERATOR, TVL_MEMORYAT, TIN_PRECEDENCE_2 | info);
+ break;
+ }
+ }
+
+ /* empty string is automatically invalid */
+ if (buffer[0] == 0)
+ return MAKE_EXPRERR_INVALID_TOKEN(token->offset);
+ else
+ {
+ /* if we have a number prefix, assume it is a number */
+ bufindex = 0;
+ if (buffer[0] == '$') { numbase = 16; bufindex++; must_be_number = 1; }
+ else if (buffer[0] == '0' && buffer[1] == 'x') { numbase = 16; bufindex += 2; must_be_number = 1; }
+ else if (buffer[0] == '#') { numbase = 10; bufindex++; must_be_number = 1; }
+
+ /* if we're not forced to be a number, check for a symbol match first */
+ if (!must_be_number)
+ {
+ const symbol_entry *symbol = symtable_find(expr->table, buffer);
+ if (symbol != NULL)
+ {
+ token->type = TOK_SYMBOL;
+ token->value.p = (void *)symbol;
+
+ /* if this is a function symbol, synthesize an execute function operator */
+ if (symbol->type == SMT_FUNCTION)
+ {
+ token++;
+ SET_TOKEN_INFO(0, TOK_OPERATOR, TVL_EXECUTEFUNC, TIN_PRECEDENCE_0);
+ }
+ }
+ }
+
+ /* see if we parse as a number */
+ if (token->type == TOK_INVALID)
+ {
+ value = 0;
+ while (buffer[bufindex] != 0)
+ {
+ const char *ptr = strchr(numbers, tolower(buffer[bufindex]));
+ int digit;
+
+ if (ptr == NULL)
+ break;
+ digit = ptr - numbers;
+ if (digit >= numbase)
+ break;
+ value = (value * (UINT64)numbase) + digit;
+ bufindex++;
+ }
+
+ /* if we succeeded as a number, make it so */
+ if (buffer[bufindex] == 0)
+ {
+ token->type = TOK_NUMBER;
+ token->value.i = value;
+ }
+ }
+
+ /* report errors */
+ if (token->type == TOK_INVALID)
+ {
+ if (must_be_number)
+ return MAKE_EXPRERR_INVALID_NUMBER(token->offset);
+ else
+ return MAKE_EXPRERR_UNKNOWN_SYMBOL(token->offset);
+ }
+ }
+ break;
+ }
+ }
+
+ /* advance to the next token */
+ token++;
+ }
+
+ /* add an ending token */
+ token->type = TOK_END;
+ token->value.i = 0;
+ return EXPRERR_NONE;
+}
+
+
+
+/***************************************************************************
+ INFIX TO POSTFIX CONVERSION
+***************************************************************************/
+
+/*-------------------------------------------------
+ normalize_operator - resolve operator
+ ambiguities based on neighboring tokens
+-------------------------------------------------*/
+
+static EXPRERR normalize_operator(parsed_expression *expr, int tokindex)
+{
+ parse_token *thistoken = &expr->token[tokindex];
+ parse_token *nexttoken = thistoken + 1;
+ parse_token *prevtoken = (tokindex == 0) ? NULL : (thistoken - 1);
+
+ switch (thistoken->value.i)
+ {
+ /* Determine if an open paren is part of a function or not */
+ case TVL_LPAREN:
+ if (prevtoken != NULL && prevtoken->type == TOK_OPERATOR && prevtoken->value.i == TVL_EXECUTEFUNC)
+ thistoken->info |= TIN_FUNCTION;
+ break;
+
+ /* Determine if ++ is a pre or post increment */
+ case TVL_PLUSPLUS:
+ if (nexttoken->type == TOK_SYMBOL || (nexttoken->type == TOK_OPERATOR && nexttoken->value.i == TVL_MEMORYAT))
+ {
+ thistoken->value.i = TVL_PREINCREMENT;
+ thistoken->info = TIN_PRECEDENCE_2;
+ }
+ else if (prevtoken != NULL && (prevtoken->type == TOK_SYMBOL || (prevtoken->type == TOK_OPERATOR && prevtoken->value.i == TVL_MEMORYAT)))
+ {
+ thistoken->value.i = TVL_POSTINCREMENT;
+ thistoken->info = TIN_PRECEDENCE_1;
+ }
+ else
+ return MAKE_EXPRERR_SYNTAX(thistoken->offset);
+ break;
+
+ /* Determine if -- is a pre or post decrement */
+ case TVL_MINUSMINUS:
+ if (nexttoken->type == TOK_SYMBOL || (nexttoken->type == TOK_OPERATOR && nexttoken->value.i == TVL_MEMORYAT))
+ {
+ thistoken->value.i = TVL_PREDECREMENT;
+ thistoken->info = TIN_PRECEDENCE_2;
+ }
+ else if (prevtoken != NULL && (prevtoken->type == TOK_SYMBOL || (prevtoken->type == TOK_OPERATOR && prevtoken->value.i == TVL_MEMORYAT)))
+ {
+ thistoken->value.i = TVL_POSTDECREMENT;
+ thistoken->info = TIN_PRECEDENCE_1;
+ }
+ else
+ return MAKE_EXPRERR_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->type != TOK_SYMBOL && prevtoken->type != TOK_NUMBER &&
+ (prevtoken->type != TOK_OPERATOR || prevtoken->value.i != TVL_RPAREN)))
+ {
+ thistoken->value.i = (thistoken->value.i == TVL_ADD) ? TVL_UPLUS : TVL_UMINUS;
+ thistoken->info = TIN_PRECEDENCE_2;
+ }
+ break;
+
+ /* Determine if , refers to a function parameter */
+ case TVL_COMMA:
+ {
+ int lookback;
+
+ for (lookback = 0; lookback < MAX_STACK_DEPTH; lookback++)
+ {
+ parse_token *peek = peek_token(expr, lookback);
+ if (!peek)
+ break;
+ if (peek->value.i == TVL_LPAREN)
+ {
+ if (peek->info & TIN_FUNCTION)
+ thistoken->info |= TIN_FUNCTION;
+ break;
+ }
+ if (peek->value.i == TVL_EXECUTEFUNC)
+ {
+ thistoken->info |= TIN_FUNCTION;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return EXPRERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ infix_to_postfix - convert an infix sequence
+ of tokens to a postfix sequence for processing
+-------------------------------------------------*/
+
+static EXPRERR infix_to_postfix(parsed_expression *expr)
+{
+ parse_token *dest = expr->token;
+ parse_token dummy;
+ parse_token *peek;
+ int tokindex = 0;
+ EXPRERR exprerr;
+
+ /* start with an empty stack */
+ init_token_stack(expr);
+
+ /* loop over all the original tokens */
+ for ( ; expr->token[tokindex].type != TOK_END; tokindex++)
+ {
+ parse_token *token = &expr->token[tokindex];
+
+ /* If the character is an operand, append it to the result string */
+ if (token->type == TOK_NUMBER || token->type == TOK_SYMBOL || token->type == TOK_STRING)
+ *dest++ = *token;
+
+ /* If this is an operator, process it */
+ else if (token->type == TOK_OPERATOR)
+ {
+ int exprerr = normalize_operator(expr, tokindex);
+ if (exprerr != 0)
+ return exprerr;
+
+ /* If the token is an opening parenthesis, push it onto the stack. */
+ if (token->value.i == TVL_LPAREN)
+ {
+ exprerr = push_token(expr, token);
+ if (exprerr != 0)
+ return exprerr;
+ }
+
+ /* If the token is a closing parenthesis, pop all operators until we
+ reach an opening parenthesis and append them to the result string. */
+ else if (token->value.i == TVL_RPAREN)
+ {
+ /* loop until we can't peek at the stack anymore */
+ while ((peek = peek_token(expr, 0)) != NULL)
+ {
+ if (peek->value.i == TVL_LPAREN)
+ break;
+ exprerr = pop_token(expr, dest++);
+ if (exprerr != 0)
+ return exprerr;
+ }
+
+ /* if we didn't find an open paren, it's an error */
+ if (peek == NULL)
+ return MAKE_EXPRERR_UNBALANCED_PARENS(token->offset);
+
+ /* pop the open paren off the stack */
+ exprerr = pop_token(expr, &dummy);
+ if (exprerr != 0)
+ return exprerr;
+ }
+
+ /* 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->info & TIN_PRECEDENCE_MASK;
+
+ /* loop until we can't peek at the stack anymore */
+ while ((peek = peek_token(expr, 0)) != NULL)
+ {
+ int stack_precedence = peek->info & TIN_PRECEDENCE_MASK;
+
+ /* break if any of the above conditions are true */
+ if (peek->value.i == TVL_LPAREN)
+ break;
+ if (stack_precedence > our_precedence)
+ break;
+ if (stack_precedence == our_precedence && (peek->info & TIN_RIGHT_TO_LEFT))
+ break;
+
+ /* pop this token */
+ exprerr = pop_token(expr, dest++);
+ if (exprerr != 0)
+ return exprerr;
+ }
+
+ /* push the new operator */
+ exprerr = push_token(expr, token);
+ if (exprerr != 0)
+ return exprerr;
+ }
+ }
+ }
+
+ /* finish popping the stack */
+ while ((peek = peek_token(expr, 0)) != NULL)
+ {
+ /* it is an error to have a left parenthesis still on the stack */
+ if (peek->value.i == TVL_LPAREN)
+ return MAKE_EXPRERR_UNBALANCED_PARENS(peek->offset);
+
+ /* pop this token */
+ exprerr = pop_token(expr, dest++);
+ if (exprerr != 0)
+ return exprerr;
+ }
+
+ /* copy the end token to the final stream */
+ *dest++ = expr->token[tokindex];
+ return EXPRERR_NONE;
+}
+
+
+
+/***************************************************************************
+ EXPRESSION EVALUATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ execute_function - handle an execute function
+ operator
+-------------------------------------------------*/
+
+static EXPRERR execute_function(parsed_expression *expr, parse_token *token)
+{
+ UINT64 funcparams[MAX_FUNCTION_PARAMS];
+ symbol_entry *symbol = NULL;
+ int paramcount = 0;
+ parse_token t1;
+ EXPRERR exprerr;
+
+ /* pop off all pushed parameters */
+ while (paramcount < MAX_FUNCTION_PARAMS)
+ {
+ /* peek at the next token on the stack */
+ parse_token *peek = peek_token(expr, 0);
+ if (!peek)
+ return MAKE_EXPRERR_INVALID_PARAM_COUNT(token->offset);
+
+ /* if it is a function symbol, break out of the loop */
+ if (peek->type == TOK_SYMBOL)
+ {
+ symbol = peek->value.p;
+ if (symbol != NULL && symbol->type == SMT_FUNCTION)
+ {
+ pop_token(expr, &t1);
+ break;
+ }
+ }
+
+ /* otherwise, pop as a standard rval */
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ funcparams[MAX_FUNCTION_PARAMS - (++paramcount)] = t1.value.i;
+ }
+
+ /* if we didn't find the symbol, fail */
+ if (paramcount == MAX_FUNCTION_PARAMS)
+ return MAKE_EXPRERR_INVALID_PARAM_COUNT(token->offset);
+
+ /* validate the number of parameters */
+ if (paramcount < symbol->info.func.minparams || paramcount > symbol->info.func.maxparams)
+ return MAKE_EXPRERR_INVALID_PARAM_COUNT(token->offset);
+
+ /* execute the function and push the result */
+ t1.type = TOK_NUMBER;
+ t1.offset = token->offset;
+ t1.value.i = (*symbol->info.func.execute)(symbol->ref, paramcount, &funcparams[MAX_FUNCTION_PARAMS - paramcount]);
+ push_token(expr, &t1);
+
+ return EXPRERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ execute_tokens - execute a postfix sequence
+ of tokens
+-------------------------------------------------*/
+
+static EXPRERR execute_tokens(parsed_expression *expr, UINT64 *result)
+{
+ parse_token t1, t2, tempnum, tempmem;
+ EXPRERR exprerr;
+ int tokindex;
+
+ /* reset the token stack */
+ init_token_stack(expr);
+
+ /* create a temporary token for holding intermediate number and memory values */
+ tempnum.type = TOK_NUMBER;
+ tempnum.offset = 0;
+ tempnum.info = 0;
+ tempmem.type = TOK_MEMORY;
+ tempmem.offset = 0;
+ tempmem.info = 0;
+
+ /* loop over the entire sequence */
+ for (tokindex = 0; expr->token[tokindex].type != TOK_END; tokindex++)
+ {
+ parse_token *token = &expr->token[tokindex];
+
+ switch (token->type)
+ {
+ default:
+ case TOK_INVALID:
+ return MAKE_EXPRERR_INVALID_TOKEN(token->offset);
+
+ case TOK_SYMBOL:
+ case TOK_NUMBER:
+ case TOK_STRING:
+ exprerr = push_token(expr, token);
+ if (exprerr != 0)
+ return exprerr;
+ break;
+
+ case TOK_OPERATOR:
+ switch (token->value.i)
+ {
+ case TVL_PREINCREMENT:
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table) + 1;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_PREDECREMENT:
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table) - 1;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_POSTINCREMENT:
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i + 1);
+ break;
+
+ case TVL_POSTDECREMENT:
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i - 1);
+ break;
+
+ case TVL_COMPLEMENT:
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = !t1.value.i;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_NOT:
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = ~t1.value.i;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_UPLUS:
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_UMINUS:
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = -t1.value.i;
+ tempnum.offset = t1.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_MULTIPLY:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i * t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_DIVIDE:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ if (t2.value.i == 0) return MAKE_EXPRERR_DIVIDE_BY_ZERO(t2.offset);
+ tempnum.value.i = t1.value.i / t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_MODULO:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ if (t2.value.i == 0) return MAKE_EXPRERR_DIVIDE_BY_ZERO(t2.offset);
+ tempnum.value.i = t1.value.i % t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_ADD:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i + t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_SUBTRACT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i - t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_LSHIFT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i << t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_RSHIFT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i >> t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_LESS:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i < t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_LESSOREQUAL:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i <= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_GREATER:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i > t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_GREATEROREQUAL:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i >= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_EQUAL:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i == t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_NOTEQUAL:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i != t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_BAND:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i & t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_BXOR:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i ^ t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_BOR:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i | t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_LAND:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i && t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_LOR:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = t1.value.i || t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_ASSIGN:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ push_token(expr, &t2);
+ set_lval_value(&t1, expr->table, t2.value.i);
+ break;
+
+ case TVL_ASSIGNMULTIPLY:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i *= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNDIVIDE:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ if (t2.value.i == 0) return MAKE_EXPRERR_DIVIDE_BY_ZERO(t2.offset);
+ tempnum.value.i /= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNMODULO:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ if (t2.value.i == 0) return MAKE_EXPRERR_DIVIDE_BY_ZERO(t2.offset);
+ tempnum.value.i %= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNADD:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i += t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNSUBTRACT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i -= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNLSHIFT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i <<= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNRSHIFT:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i >>= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNBAND:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i &= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNBXOR:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i ^= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_ASSIGNBOR:
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_lval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempnum.value.i = get_lval_value(&t1, expr->table);
+ tempnum.value.i |= t2.value.i;
+ tempnum.offset = (t1.offset < t2.offset) ? t1.offset : t2.offset;
+ exprerr = push_token(expr, &tempnum); if (exprerr != 0) return exprerr;
+ set_lval_value(&t1, expr->table, tempnum.value.i);
+ break;
+
+ case TVL_COMMA:
+ if (!(token->info & TIN_FUNCTION))
+ {
+ exprerr = pop_token_rval(expr, &t2, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ exprerr = push_token(expr, &t2); if (exprerr != 0) return exprerr;
+ }
+ break;
+
+ case TVL_MEMORYAT:
+ exprerr = pop_token_rval(expr, &t1, expr->table); if (exprerr != 0) return exprerr;
+ tempmem.value.i = t1.value.i;
+ tempmem.info = token->info;
+ exprerr = push_token(expr, &tempmem); if (exprerr != 0) return exprerr;
+ break;
+
+ case TVL_EXECUTEFUNC:
+ exprerr = execute_function(expr, token); if (exprerr != 0) return exprerr;
+ break;
+
+ default:
+ return MAKE_EXPRERR_SYNTAX(token->offset);
+ }
+ break;
+ }
+ }
+
+ /* pop the final result */
+ exprerr = pop_token_rval(expr, &tempnum, expr->table);
+ if (exprerr != 0)
+ return exprerr;
+
+ /* error if our stack isn't empty */
+ if (peek_token(expr, 0) != NULL)
+ return MAKE_EXPRERR_SYNTAX(0);
+
+ *result = tempnum.value.i;
+ return EXPRERR_NONE;
+}
+
+
+
+/***************************************************************************
+ MISC HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ free_expression_strings - free all strings
+ allocated to an expression
+-------------------------------------------------*/
+
+static void free_expression_strings(parsed_expression *expr)
+{
+ int strindex;
+
+ /* free the original expression */
+ if (expr->original_string != NULL)
+ free(expr->original_string);
+ expr->original_string = NULL;
+
+ /* free all strings */
+ for (strindex = 0; strindex < MAX_EXPRESSION_STRINGS; strindex++)
+ if (expr->string[strindex] != NULL)
+ {
+ free(expr->string[strindex]);
+ expr->string[strindex] = NULL;
+ }
+}
+
+
+
+/***************************************************************************
+ CORE WRAPPER FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ expression_evaluate - evaluate a string
+ expression using the passed symbol table
+-------------------------------------------------*/
+
+EXPRERR expression_evaluate(const char *expression, const symbol_table *table, UINT64 *result)
+{
+ parsed_expression temp_expression;
+ EXPRERR exprerr;
+
+ /* first parse the tokens into the token array in order */
+ exprerr = parse_string_into_tokens(expression, &temp_expression, table);
+ if (exprerr != EXPRERR_NONE)
+ goto cleanup;
+
+ /* debugging */
+ print_tokens(stdout, &temp_expression);
+
+ /* convert the infix order to postfix order */
+ exprerr = infix_to_postfix(&temp_expression);
+ if (exprerr != EXPRERR_NONE)
+ goto cleanup;
+
+ /* debugging */
+ print_tokens(stdout, &temp_expression);
+
+ /* execute the expression to get the result */
+ exprerr = execute_tokens(&temp_expression, result);
+
+cleanup:
+ free_expression_strings(&temp_expression);
+ return exprerr;
+}
+
+
+/*-------------------------------------------------
+ expression_parse - parse an expression and
+ return an allocated token array
+-------------------------------------------------*/
+
+EXPRERR expression_parse(const char *expression, const symbol_table *table, parsed_expression **result)
+{
+ parsed_expression temp_expression;
+ EXPRERR exprerr;
+
+ /* first parse the tokens into the token array in order */
+ exprerr = parse_string_into_tokens(expression, &temp_expression, table);
+ if (exprerr != EXPRERR_NONE)
+ goto cleanup;
+
+ /* convert the infix order to postfix order */
+ exprerr = infix_to_postfix(&temp_expression);
+ if (exprerr != EXPRERR_NONE)
+ goto cleanup;
+
+ /* allocate memory for the result */
+ *result = malloc(sizeof(temp_expression));
+ if (!*result)
+ {
+ exprerr = MAKE_EXPRERR_OUT_OF_MEMORY(0);
+ goto cleanup;
+ }
+
+ /* copy the final expression and return */
+ **result = temp_expression;
+ return EXPRERR_NONE;
+
+cleanup:
+ free_expression_strings(&temp_expression);
+ return exprerr;
+}
+
+
+/*-------------------------------------------------
+ expression_execute - execute a
+ previously-parsed expression
+-------------------------------------------------*/
+
+EXPRERR expression_execute(parsed_expression *expr, UINT64 *result)
+{
+ /* execute the expression to get the result */
+ return execute_tokens(expr, result);
+}
+
+
+/*-------------------------------------------------
+ expression_free - free a previously
+ allocated parsed expression
+-------------------------------------------------*/
+
+void expression_free(parsed_expression *expr)
+{
+ if (expr)
+ {
+ free_expression_strings(expr);
+ free(expr);
+ }
+}
+
+
+/*-------------------------------------------------
+ expression_original_string - return a
+ pointer to the original expression string
+-------------------------------------------------*/
+
+const char *expression_original_string(parsed_expression *expr)
+{
+ return expr->original_string;
+}
+
+
+
+/***************************************************************************
+ ERROR HANDLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ exprerr_to_string - return a friendly
+ string for a given expression error
+-------------------------------------------------*/
+
+const char *exprerr_to_string(EXPRERR error)
+{
+ switch (EXPRERR_ERROR_CLASS(error))
+ {
+ case EXPRERR_NOT_LVAL: return "not an lvalue";
+ case EXPRERR_NOT_RVAL: return "not an rvalue";
+ case EXPRERR_SYNTAX: return "syntax error";
+ case EXPRERR_UNKNOWN_SYMBOL: return "unknown symbol";
+ case EXPRERR_INVALID_NUMBER: return "invalid number";
+ case EXPRERR_INVALID_TOKEN: return "invalid token";
+ case EXPRERR_STACK_OVERFLOW: return "stack overflow";
+ case EXPRERR_STACK_UNDERFLOW: return "stack underflow";
+ case EXPRERR_UNBALANCED_PARENS: return "unbalanced parentheses";
+ case EXPRERR_DIVIDE_BY_ZERO: return "divide by zero";
+ case EXPRERR_OUT_OF_MEMORY: return "out of memory";
+ case EXPRERR_INVALID_PARAM_COUNT: return "invalid number of parameters";
+ case EXPRERR_UNBALANCED_QUOTES: return "unbalanced quotes";
+ case EXPRERR_TOO_MANY_STRINGS: return "too many strings";
+ default: return "unknown error";
+ }
+}
+
+
+
+/***************************************************************************
+ SYMBOL TABLES
+***************************************************************************/
+
+/*-------------------------------------------------
+ hash_string - simple string hash
+-------------------------------------------------*/
+
+INLINE UINT32 hash_string(const char *string)
+{
+ UINT32 hash = 0;
+ while (*string)
+ hash = (hash * 31) + *string++;
+ return hash;
+}
+
+
+/*-------------------------------------------------
+ symtable_alloc - allocate a symbol table
+-------------------------------------------------*/
+
+symbol_table *symtable_alloc(symbol_table *parent)
+{
+ symbol_table *table;
+
+ /* allocate memory for the table */
+ table = malloc(sizeof(*table));
+ if (!table)
+ return NULL;
+
+ /* initialize the data */
+ memset(table, 0, sizeof(*table));
+ table->parent = parent;
+ return table;
+}
+
+
+/*-------------------------------------------------
+ symtable_add - add a new symbol to a
+ symbol table
+-------------------------------------------------*/
+
+int symtable_add(symbol_table *table, const char *name, const symbol_entry *entry)
+{
+ internal_symbol_entry *symbol;
+ symbol_entry *oldentry;
+ char *newstring;
+ UINT32 hash_index;
+ int strindex;
+ int all_digits, i;
+
+ /* we cannot add numeric symbols */
+ all_digits = TRUE;
+ for (i = 0; name[i]; i++)
+ {
+ if (!isdigit(name[i]))
+ {
+ all_digits = FALSE;
+ break;
+ }
+ }
+ assert_always(!all_digits, "All-digit symbols are not allowed");
+
+ /* see if we already have an entry and just overwrite it if we do */
+ oldentry = (symbol_entry *)symtable_find(table, name);
+ if (oldentry)
+ {
+ *oldentry = *entry;
+ return 1;
+ }
+
+ /* otherwise, allocate a new entry */
+ symbol = malloc(sizeof(*symbol));
+ if (!symbol)
+ return 0;
+ memset(symbol, 0, sizeof(*symbol));
+
+ /* allocate space for a copy of the string */
+ newstring = malloc(strlen(name) + 1);
+ if (!newstring)
+ {
+ free(symbol);
+ return 0;
+ }
+
+ /* copy the string, converting to lowercase */
+ for (strindex = 0; name[strindex] != 0; strindex++)
+ newstring[strindex] = tolower(name[strindex]);
+ newstring[strindex] = 0;
+
+ /* fill in the details */
+ symbol->name = newstring;
+ symbol->entry = *entry;
+
+ /* add the entry to the hash table */
+ hash_index = hash_string(newstring) % SYM_TABLE_HASH_SIZE;
+ symbol->next = table->hash[hash_index];
+ table->hash[hash_index] = symbol;
+ return 1;
+}
+
+
+/*-------------------------------------------------
+ symtable_add_register - add a new
+ register symbol to a symbol table
+-------------------------------------------------*/
+
+int symtable_add_register(symbol_table *table, const char *name, UINT32 ref, UINT64 (*getter)(UINT32), void (*setter)(UINT32, UINT64))
+{
+ symbol_entry symbol;
+
+ symbol.ref = ref;
+ symbol.type = SMT_REGISTER;
+ symbol.info.reg.getter = getter;
+ symbol.info.reg.setter = setter;
+ return symtable_add(table, name, &symbol);
+}
+
+
+/*-------------------------------------------------
+ symtable_add_function - add a new
+ function symbol to a symbol table
+-------------------------------------------------*/
+
+int symtable_add_function(symbol_table *table, const char *name, UINT32 ref, UINT16 minparams, UINT16 maxparams, UINT64 (*execute)(UINT32, UINT32, UINT64 *))
+{
+ symbol_entry symbol;
+
+ symbol.ref = ref;
+ symbol.type = SMT_FUNCTION;
+ symbol.info.func.minparams = minparams;
+ symbol.info.func.maxparams = maxparams;
+ symbol.info.func.execute = execute;
+ return symtable_add(table, name, &symbol);
+}
+
+
+/*-------------------------------------------------
+ symtable_add_value - add a new
+ value symbol to a symbol table
+-------------------------------------------------*/
+
+int symtable_add_value(symbol_table *table, const char *name, UINT64 value)
+{
+ symbol_entry symbol;
+
+ symbol.ref = 0;
+ symbol.type = SMT_VALUE;
+ symbol.info.gen.value = value;
+ return symtable_add(table, name, &symbol);
+}
+
+
+/*-------------------------------------------------
+ symtable_find - find a symbol in a symbol
+ table
+-------------------------------------------------*/
+
+const symbol_entry *symtable_find(const symbol_table *table, const char *name)
+{
+ UINT32 hash_index = hash_string(name) % SYM_TABLE_HASH_SIZE;
+ const internal_symbol_entry *symbol;
+
+ /* loop until we run out of tables */
+ while (table)
+ {
+ /* search linearly within this hash entry */
+ for (symbol = table->hash[hash_index]; symbol; symbol = symbol->next)
+ if (!strcmp(symbol->name, name))
+ return &symbol->entry;
+
+ /* look in the parent */
+ table = table->parent;
+ }
+
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ symtable_find_indexed - find an indexed symbol
+ in a symbol table
+-------------------------------------------------*/
+
+const char *symtable_find_indexed(const symbol_table *table, int index, const symbol_entry **entry)
+{
+ const internal_symbol_entry *symbol;
+ int hash_index;
+
+ /* loop over hash entries, then over entries within each bucket */
+ for (hash_index = 0; hash_index < SYM_TABLE_HASH_SIZE; hash_index++)
+ for (symbol = table->hash[hash_index]; symbol; symbol = symbol->next)
+ if (index-- == 0)
+ {
+ if (entry)
+ *entry = &symbol->entry;
+ return symbol->name;
+ }
+
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ symtable_free - free a symbol table
+-------------------------------------------------*/
+
+void symtable_free(symbol_table *table)
+{
+ internal_symbol_entry *entry, *next;
+ int hash_index;
+
+ /* free all the entries in the hash table */
+ for (hash_index = 0; hash_index < SYM_TABLE_HASH_SIZE; hash_index++)
+ for (entry = table->hash[hash_index]; entry; entry = next)
+ {
+ /* free the allocated name */
+ if (entry->name)
+ free((void *)entry->name);
+
+ /* remove from this list and put on the free list */
+ next = entry->next;
+ free(entry);
+ }
+
+ /* free the structure */
+ free(table);
+}