From aecf8f0175aa2286cf0a4a32e71d655a2617de50 Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Sat, 10 Apr 2010 12:25:01 +1200 Subject: [PATCH] Address expressions are now fully algebraic. --- mspdebug.man | 12 +- util.c | 308 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 256 insertions(+), 64 deletions(-) diff --git a/mspdebug.man b/mspdebug.man index 2f1d622..f305b21 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -296,17 +296,21 @@ renamed. .SH ADDRESS EXPRESSIONS Any command which accepts a memory address, length or register value as an argument may be given an address expression. An address -expression consists of a sequence of one or more address values -separated by the operators \fB+\fR or \fB\-\fR. +expression consists of an algebraic combination of values. An address value may be either a decimal value, a hexadecimal value preceeded by the prefix \fB0x\fR, or a symbol name. +The operators recognised are the usual algebraic operators: \fB+\fR, \fB-\fR, +\fB*\fR, \fB/\fR, \fB%\fR, \fB(\fR and \fB)\fR. Operator precedence is the +same as in C-like languages, and the \fB-\fR operator may be used as a +unary negation operator. + The following are all valid examples of address expressions: -.B 64 +.B 2+2 .br -.B 0xffe0 +.B table_start + (elem_size + elem_pad)*4 .br .B main+0x3f .br diff --git a/util.c b/util.c index a5cb7bd..1feaeaf 100644 --- a/util.c +++ b/util.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef USE_READLINE #include @@ -665,82 +666,269 @@ int ctrlc_check(void) return ctrlc_flag; } -static char token_buf[64]; -static int token_len; -static int token_mult; -static int token_sum; - static token_func_t token_func; -static int token_add(void) -{ - int i; - int value; - - if (!token_len) - return 0; - - token_buf[token_len] = 0; - token_len = 0; - - /* Is it a decimal? */ - i = 0; - while (token_buf[i] && isdigit(token_buf[i])) - i++; - if (!token_buf[i]) { - token_sum += token_mult * atoi(token_buf); - return 0; - } - - /* Is it hex? */ - if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') { - token_sum += token_mult * strtol(token_buf + 2, NULL, 16); - return 0; - } - - /* Look up the name in the symbol table */ - if (token_func && !token_func(token_buf, &value)) { - token_sum += token_mult * value; - return 0; - } - - fprintf(stderr, "unknown token: %s\n", token_buf); - return -1; -} - void set_token_func(token_func_t func) { token_func = func; } -int addr_exp(const char *text, int *addr) -{ - token_len = 0; - token_mult = 1; - token_sum = 0; +struct addr_exp_state { + int last_operator; + int data_stack[32]; + int data_stack_size; + int op_stack[32]; + int op_stack_size; +}; - while (*text) { - if (isalnum(*text) || *text == '_' || *text == '$' || - *text == '.' || *text == ':') { - if (token_len + 1 < sizeof(token_buf)) - token_buf[token_len++] = *text; - } else { - if (token_add() < 0) +static int addr_exp_data(struct addr_exp_state *s, const char *text) +{ + int value; + + if (!s->last_operator || s->last_operator == ')') { + fprintf(stderr, "syntax error at token %s\n", text); + return -1; + } + + /* Hex value */ + if (*text == '0' && text[1] == 'x') + value = strtoul(text + 2, NULL, 16); + else if (isdigit(*text)) + value = atoi(text); + else if (!token_func || token_func(text, &value) < 0) { + fprintf(stderr, "can't parse token: %s\n", text); + return -1; + } + + if (s->data_stack_size + 1 > ARRAY_LEN(s->data_stack)) { + fprintf(stderr, "data stack overflow at token %s\n", text); + return -1; + } + + s->data_stack[s->data_stack_size++] = value; + s->last_operator = 0; + return 0; +} + +static int addr_exp_pop(struct addr_exp_state *s) +{ + char op = s->op_stack[--s->op_stack_size]; + int data1 = s->data_stack[--s->data_stack_size]; + int data2 = 0; + + int result = 0; + + if (op != 'N') + data2 = s->data_stack[--s->data_stack_size]; + + assert (s->op_stack_size >= 0); + assert (s->data_stack_size >= 0); + + switch (op) { + case '+': + result = data2 + data1; + break; + + case '-': + result = data2 - data1; + break; + + case '*': + result = data2 * data1; + break; + + case '/': + if (!data1) + goto divzero; + result = data2 / data1; + break; + + case '%': + if (!data1) + goto divzero; + result = data2 % data1; + break; + + case 'N': + result = -data1; + break; + } + + s->data_stack[s->data_stack_size++] = result; + return 0; + + divzero: + fprintf(stderr, "divide by zero\n"); + return -1; +} + +static int can_push(struct addr_exp_state *s, char op) +{ + char top; + + if (!s->op_stack_size || op == '(') + return 1; + + top = s->op_stack[s->op_stack_size - 1]; + + if (top == '(') + return 1; + + switch (op) { + case 'N': + return 1; + + case '*': + case '%': + case '/': + return top == '+' || top == '-'; + + default: + break; + } + + return 0; +} + +static int addr_exp_op(struct addr_exp_state *s, char op) +{ + if (op == '(') { + if (!s->last_operator || s->last_operator == ')') + goto syntax_error; + } else if (op == '-') { + if (s->last_operator && s->last_operator != ')') + op = 'N'; + } else { + if (s->last_operator && s->last_operator != ')') + goto syntax_error; + } + + if (op == ')') { + /* ) collapses the stack to the last matching ( */ + while (s->op_stack_size && + s->op_stack[s->op_stack_size - 1] != '(') + if (addr_exp_pop(s) < 0) return -1; - if (*text == '+') - token_mult = 1; - if (*text == '-') - token_mult = -1; + + if (!s->op_stack_size) { + fprintf(stderr, "parenthesis mismatch: )\n"); + return -1; } + s->op_stack_size--; + } else { + while (!can_push(s, op)) + if (addr_exp_pop(s) < 0) + return -1; + + if (s->op_stack_size + 1 > ARRAY_LEN(s->op_stack)) { + fprintf(stderr, "operator stack overflow: %c\n", op); + return -1; + } + + s->op_stack[s->op_stack_size++] = op; + } + + s->last_operator = op; + return 0; + + syntax_error: + fprintf(stderr, "syntax error at operator %c\n", op); + return -1; +} + +static int addr_exp_finish(struct addr_exp_state *s, int *ret) +{ + if (s->last_operator && s->last_operator != ')') { + fprintf(stderr, "syntax error at end of expression\n"); + return -1; + } + + while (s->op_stack_size) { + if (s->op_stack[s->op_stack_size - 1] == '(') { + fprintf(stderr, "parenthesis mismatch: (\n"); + return -1; + } + + if (addr_exp_pop(s) < 0) + return -1; + } + + if (s->data_stack_size != 1) { + fprintf(stderr, "no data: stack size is %d\n", + s->data_stack_size); + return -1; + } + + if (ret) + *ret = s->data_stack[0]; + + return 0; +} + +int addr_exp(const char *text, int *addr) +{ + const char *text_save = text; + int last_cc = 1; + char token_buf[64]; + int token_len = 0; + struct addr_exp_state s = {0}; + + s.last_operator = '('; + + for (;;) { + int cc; + + /* Figure out what class this character is */ + if (*text == '+' || *text == '-' || + *text == '*' || *text == '/' || + *text == '%' || *text == '(' || + *text == ')') + cc = 1; + else if (!*text || isspace(*text)) + cc = 2; + else if (isalnum(*text) || *text == '.' || *text == '_' || + *text == '$' || *text == ':') + cc = 3; + else { + fprintf(stderr, "illegal character in expression: %c\n", + *text); + return -1; + } + + /* Accumulate and process token text */ + if (cc == 3) { + if (token_len + 1 < sizeof(token_buf)) + token_buf[token_len++] = *text; + } else if (token_len) { + token_buf[token_len] = 0; + token_len = 0; + + if (addr_exp_data(&s, token_buf) < 0) + goto fail; + } + + /* Process operators */ + if (cc == 1) { + if (addr_exp_op(&s, *text) < 0) + goto fail; + } + + if (!*text) + break; + + last_cc = cc; text++; } - if (token_add() < 0) - return -1; + if (addr_exp_finish(&s, addr) < 0) + goto fail; - *addr = token_sum & 0xffff; return 0; + + fail: + fprintf(stderr, "bad address expression: %s\n", text_save); + return -1; } static int modify_flags;