/* * picasm -- expr.c * * Copyright 1995-2004 Timo Rossi, * See the file LICENSE for license terms. * * expression parser * */ #include #include #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; }