/*
* picasm -- expr.c
*
* Copyright 1995-2004 Timo Rossi, <trossi@iki.fi>
* See the file LICENSE for license terms.
*
* expression parser
*
*/
#include <stdio.h>
#include <string.h>
#include "picasm.h"
#include "token.h"
#include "symtab.h"
/*forward*/
static long expr_main(void);
static int expr_error; /* expression error flag */
/*
* expression parser, bottom level
*/
static long
expr_element(void)
{
long val, tval;
struct symbol *sym;
static char strbuf[256];
int symtype;
int tt;
if(expr_error)
return 0;
switch(token_type)
{
case TOK_LEFTBRAK:
get_token();
tval = 0;
while(!expr_error && token_type != TOK_RIGHTBRAK)
{
val = expr_main();
if(!expr_error && (val < 0 || val >= EXPR_NBITS))
{
error(1, "bit number out of range");
expr_error = 1;
}
tval |= (1 << val);
}
if(!expr_error)
get_token();
return tval;
case TOK_LEFTPAR:
get_token();
val = expr_main();
if(!expr_error && token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
}
if(!expr_error)
get_token();
return val;
case KW_HIBYTE:
get_token();
if(token_type != TOK_LEFTPAR)
{
error(1, "'(' expected");
expr_error = 1;
return EXPR_FALSE;
}
get_token();
val = expr_main();
if(!expr_error)
{
if(token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
return val;
}
get_token();
}
return (val >> 8) & 0xff;
case KW_DEFINED:
get_token();
if(token_type != TOK_LEFTPAR)
{
error(1, "'(' expected");
expr_error = 1;
return EXPR_FALSE;
}
get_token();
if(token_type != TOK_IDENTIFIER && token_type != TOK_LOCAL_ID)
{
error(1, "Symbol expected");
expr_error = 1;
return EXPR_FALSE;
}
symtype =
(token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL);
if((sym = lookup_symbol(token_string, symtype)) == NULL)
val = EXPR_FALSE;
else if(sym->type == SYM_DEFINED || sym->type == SYM_SET)
val = EXPR_TRUE;
else
val = EXPR_FALSE;
get_token();
if(token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
return val;
}
get_token();
return val;
case KW_STREQ:
case KW_STREQCASE:
/*
* streq(arg1, arg2), return TRUE if strings are identical
*
* streqcase(arg1, arg2), return TRUE if strings are identical,
* ignoring case
*/
tt = token_type;
get_token();
if(token_type != TOK_LEFTPAR)
{
error(1, "'(' expected");
expr_error = 1;
return EXPR_FALSE;
}
get_token();
if(token_type != TOK_STRCONST)
{
error(1, "Quoted string expected");
expr_error = 1;
return EXPR_FALSE;
}
strcpy(strbuf, token_string);
get_token();
if(token_type != TOK_COMMA)
{
error(1, "',' expected");
expr_error = 1;
return EXPR_FALSE;
}
get_token();
if(token_type != TOK_STRCONST)
{
error(1, "Quoted string expected");
expr_error = 1;
return EXPR_FALSE;
}
switch(tt)
{
case KW_STREQ:
default:
val = (strcmp(token_string, strbuf) == 0 ? EXPR_TRUE : EXPR_FALSE);
break;
case KW_STREQCASE:
val = (strcasecmp(token_string, strbuf) == 0 ? EXPR_TRUE : EXPR_FALSE);
break;
}
get_token();
if(token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
return val;
}
get_token();
return val;
case KW_ISSTR:
/* isstr(arg), return TRUE if argument is a quoted string */
get_token();
if(token_type != TOK_LEFTPAR)
{
error(1, "'(' expected");
expr_error = 1;
return EXPR_FALSE;
}
get_token();
if(token_type == TOK_STRCONST)
{
val = EXPR_TRUE;
get_token();
}
else if(token_type == TOK_RIGHTPAR)
{
get_token(); /* empty parameter list */
return EXPR_FALSE;
}
else
{
val = EXPR_FALSE;
do
{
get_token();
} while(token_type != TOK_EOF && token_type != TOK_NEWLINE &&
token_type != TOK_COMMA && token_type != TOK_RIGHTPAR);
}
if(token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
return val;
}
get_token();
return val;
case KW_CHRVAL:
/* chrval(string, pos), return ascii code of character in string */
get_token();
if(token_type != TOK_LEFTPAR)
{
error(1, "'(' expected");
expr_error = 1;
return -1;
}
get_token();
if(token_type != TOK_STRCONST)
{
error(1, "Quoted string expected");
expr_error = 1;
return -1;
}
strcpy(strbuf, token_string);
get_token();
if(token_type != TOK_COMMA)
{
error(1, "',' expected");
expr_error = 1;
return -1;
}
get_token();
val = expr_main();
if(val < 0 || val >= (long)strlen(strbuf))
val = -1;
else
val = (unsigned char)(strbuf[val]);
if(token_type != TOK_RIGHTPAR)
{
error(1, "')' expected");
expr_error = 1;
return val;
}
get_token();
return val;
case TOK_DOLLAR: /* current location */
case TOK_PERIOD:
switch(O_Mode)
{
case O_PROGRAM:
val = prog_location;
break;
case O_REGFILE:
val = reg_location;
break;
case O_EDATA:
val = edata_location;
break;
case O_NONE:
default:
val = org_val;
break;
}
if(val < 0)
{
error(1, "ORG value not set");
expr_error = 1;
}
get_token();
return val;
case TOK_INTCONST:
val = token_int_val;
get_token();
return val;
case TOK_IDENTIFIER:
case TOK_LOCAL_ID:
symtype =
(token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL);
if(symtype == SYMTAB_LOCAL && local_level == 0)
{
error(1, "Local symbol outside a LOCAL block");
expr_error = 1;
return 0;
}
if((sym = lookup_symbol(token_string, symtype)) == NULL)
{
error(1, "Undefined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
token_string);
expr_error = 1;
}
else
{
if(sym->type == SYM_MACRO)
{
error(1, "Invalid usage of macro name '%s'", token_string);
expr_error = 1;
}
else if(sym->type != SYM_DEFINED && sym->type != SYM_SET)
{
error(1, "Undefined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
token_string);
expr_error = 1;
}
get_token();
return sym->v.value;
}
break;
default:
expr_error = 1;
error(1, "Expression syntax error");
break;
}
return 0;
}
/* expression parser, unary minus and bit-not */
static long
expr_unary1(void)
{
int op;
long val;
if(!expr_error &&
(token_type == TOK_MINUS || token_type == TOK_BITNOT))
{
op = token_type;
get_token();
val = expr_element();
if(op == TOK_MINUS)
return -val;
else
return ~val;
} else
{
return expr_element();
}
}
/* expression, multiplication, division, bit-and */
static long
expr_mul_div(void)
{
int op;
long val1, val2;
val1 = expr_unary1();
while(!expr_error &&
((op = token_type) == TOK_ASTERISK || op == TOK_SLASH ||
op == TOK_PERCENT || op == TOK_BITAND))
{
get_token();
val2 = expr_unary1();
switch(op)
{
case TOK_ASTERISK:
val1 *= val2;
break;
case TOK_SLASH:
case TOK_PERCENT:
if(val2 == 0)
{
error(1, "Division by zero");
expr_error = 1;
}
else
{
if(op == TOK_SLASH)
val1 /= val2;
else
val1 %= val2;
}
break;
case TOK_BITAND:
val1 &= val2;
break;
}
}
return val1;
}
/* expression. add, subtract, bit-or */
static long
expr_add_sub(void)
{
int op;
long val1, val2;
val1 = expr_mul_div();
while(!expr_error && ((op = token_type) == TOK_PLUS || op == TOK_MINUS ||
op == TOK_BITOR || op == TOK_BITXOR ||
op == TOK_LSHIFT || op == TOK_RSHIFT))
{
get_token();
val2 = expr_mul_div();
switch(op)
{
case TOK_PLUS:
val1 += val2;
break;
case TOK_MINUS:
val1 -= val2;
break;
case TOK_BITOR:
val1 |= val2;
break;
case TOK_BITXOR:
val1 ^= val2;
break;
case TOK_LSHIFT:
val1 <<= val2;
break;
case TOK_RSHIFT:
val1 >>= val2;
break;
}
}
return val1;
}
/*
* the main expression parser entry point
*
* handles only compare operators directly
* (note: '=' (TOK_EQUAL) cannot be used as a comparison operator
* as it would be confused with local labels. '==' (TOK_EQ)
* must be used instead)
*/
long
expr_main(void)
{
int op;
long val1, val2;
val1 = expr_add_sub();
while(!expr_error && ((op = token_type) == TOK_EQ ||
op == TOK_NOT_EQ ||
op == TOK_LESS || op == TOK_GREATER ||
op == TOK_LESS_EQ || op == TOK_GT_EQ))
{
get_token();
val2 = expr_add_sub();
switch(op)
{
case TOK_EQ:
val1 = -(val1 == val2);
break;
case TOK_NOT_EQ:
val1 = -(val1 != val2);
break;
case TOK_LESS:
val1 = -(val1 < val2);
break;
case TOK_LESS_EQ:
val1 = -(val1 <= val2);
break;
case TOK_GREATER:
val1 = -(val1 > val2);
break;
case TOK_GT_EQ:
val1 = -(val1 >= val2);
break;
}
}
return val1;
}
int
get_expression(long *val_p)
{
long val;
expr_error = 0;
val = expr_main();
if(expr_error)
return FAIL;
*val_p = val;
return OK;
}
syntax highlighted by Code2HTML, v. 0.9.1