mirror of
https://github.com/mist-devel/mist-board.git
synced 2026-01-20 09:35:16 +00:00
797 lines
15 KiB
C
797 lines
15 KiB
C
/* expr.c expression handling for vasm */
|
|
/* (c) in 2002-2012 by Volker Barthelmann */
|
|
|
|
#include "vasm.h"
|
|
|
|
char current_pc_char='$';
|
|
static char *s;
|
|
|
|
#ifndef EXPSKIP
|
|
#define EXPSKIP() s=skip(s)
|
|
#endif
|
|
|
|
static expr *expression();
|
|
static symbol *cpc;
|
|
static int make_tmp_lab;
|
|
|
|
|
|
expr *new_expr(void)
|
|
{
|
|
expr *new=mymalloc(sizeof(*new));
|
|
new->left=new->right=0;
|
|
return new;
|
|
}
|
|
|
|
expr *make_expr(int type,expr *left,expr *right)
|
|
{
|
|
expr *new=mymalloc(sizeof(*new));
|
|
new->left=left;
|
|
new->right=right;
|
|
new->type=type;
|
|
return new;
|
|
}
|
|
|
|
expr *copy_tree(expr *old)
|
|
{
|
|
expr *new=0;
|
|
|
|
if(old){
|
|
new=make_expr(old->type,copy_tree(old->left),copy_tree(old->right));
|
|
new->c=old->c;
|
|
}
|
|
return new;
|
|
}
|
|
|
|
expr *curpc_expr(void)
|
|
{
|
|
expr *new=new_expr();
|
|
if(!cpc){
|
|
cpc=new_import(" *current pc dummy*");
|
|
cpc->type=LABSYM;
|
|
cpc->flags|=VASMINTERN;
|
|
}
|
|
new->type=SYM;
|
|
new->c.sym=cpc;
|
|
return new;
|
|
}
|
|
|
|
static expr *primary_expr(void)
|
|
{
|
|
expr *new;
|
|
char *m,*name;
|
|
int base;
|
|
|
|
if(*s=='('){
|
|
s++;
|
|
EXPSKIP();
|
|
new=expression();
|
|
if(*s!=')')
|
|
general_error(6,')');
|
|
else
|
|
s++;
|
|
EXPSKIP();
|
|
return new;
|
|
}
|
|
if(name=get_local_label(&s)){
|
|
symbol *sym=find_symbol(name);
|
|
if(!sym)
|
|
sym=new_import(name);
|
|
if (sym->type!=EXPRESSION){
|
|
new=new_expr();
|
|
new->type=SYM;
|
|
new->c.sym=sym;
|
|
}
|
|
else
|
|
new=copy_tree(sym->expr);
|
|
myfree(name);
|
|
return new;
|
|
}
|
|
m=const_prefix(s,&base);
|
|
if(base!=0){
|
|
taddr val=0;
|
|
s=m;
|
|
if(base<=10){
|
|
while(*s>='0'&&*s<base+'0')
|
|
val=base*val+*s++-'0';
|
|
}else if(base==16){
|
|
while((*s>='0'&&*s<='9')||(*s>='a'&&*s<='f')||(*s>='A'&&*s<='F')){
|
|
if(*s>='0'&&*s<='9')
|
|
val=16*val+*s++-'0';
|
|
else if(*s>='a'&&*s<='f')
|
|
val=16*val+*s++-'a'+10;
|
|
else
|
|
val=16*val+*s++-'A'+10;
|
|
}
|
|
}else{
|
|
ierror(0);
|
|
}
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=NUM;
|
|
new->c.val=val;
|
|
return new;
|
|
}
|
|
if(*s==current_pc_char && !ISIDCHAR(*(s+1))){
|
|
s++;
|
|
EXPSKIP();
|
|
if(make_tmp_lab){
|
|
new=new_expr();
|
|
new->type=SYM;
|
|
new->c.sym=new_tmplabel(0);
|
|
add_atom(0,new_label_atom(new->c.sym));
|
|
}else new=curpc_expr();
|
|
return new;
|
|
}
|
|
if(name=parse_identifier(&s)){
|
|
symbol *sym;
|
|
EXPSKIP();
|
|
sym=find_symbol(name);
|
|
if(!sym){
|
|
#ifdef NARGSYM
|
|
if(!strcmp(name,NARGSYM)){
|
|
new=new_expr();
|
|
new->type=NUM;
|
|
new->c.val=cur_src->num_params; /*@@@ check for macro mode? */
|
|
return new;
|
|
}
|
|
#endif
|
|
sym=new_import(name);
|
|
}
|
|
if (sym->type!=EXPRESSION){
|
|
new=new_expr();
|
|
new->type=SYM;
|
|
new->c.sym=sym;
|
|
}
|
|
else
|
|
new=copy_tree(sym->expr);
|
|
myfree(name);
|
|
return new;
|
|
}
|
|
if(*s=='\''||*s=='\"'){
|
|
taddr val=0;
|
|
int shift=0,cnt=0;
|
|
char quote=*s++;
|
|
while(*s){
|
|
char c;
|
|
if(*s=='\\')
|
|
s=escape(s,&c);
|
|
else{
|
|
c=*s++;
|
|
if(c==quote){
|
|
if(*s==quote)
|
|
s++; /* allow """" to be recognized as " and '''' as ' */
|
|
else break;
|
|
}
|
|
}
|
|
if(++cnt>bytespertaddr){
|
|
general_error(21);
|
|
break;
|
|
}
|
|
if(BIGENDIAN){
|
|
val=(val<<8)+c;
|
|
}else if(LITTLEENDIAN){
|
|
val+=c<<shift;
|
|
shift+=8;
|
|
}else
|
|
ierror(0);
|
|
}
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=NUM;
|
|
new->c.val=val;
|
|
return new;
|
|
}
|
|
general_error(9);
|
|
new=new_expr();
|
|
new->type=NUM;
|
|
new->c.val=-1;
|
|
return new;
|
|
}
|
|
|
|
static expr *unary_expr()
|
|
{
|
|
expr *new;
|
|
char *m;
|
|
int len;
|
|
if(*s=='+'||*s=='-'||*s=='!'||*s=='~'){
|
|
m=s++;
|
|
EXPSKIP();
|
|
}
|
|
else if(len=EXT_UNARY_NAME(s)){
|
|
m=s;
|
|
s+=len;
|
|
EXPSKIP();
|
|
}else
|
|
return primary_expr();
|
|
if(*m=='+')
|
|
return primary_expr();
|
|
new=new_expr();
|
|
if(*m=='-')
|
|
new->type=NEG;
|
|
else if(*m=='!')
|
|
new->type=NOT;
|
|
else if(*m=='~')
|
|
new->type=CPL;
|
|
else if(EXT_UNARY_NAME(m))
|
|
new->type=EXT_UNARY_TYPE(m);
|
|
new->left=primary_expr();
|
|
return new;
|
|
}
|
|
|
|
static expr *shift_expr()
|
|
{
|
|
expr *left,*new;
|
|
char m;
|
|
left=unary_expr();
|
|
EXPSKIP();
|
|
while((*s=='<'||*s=='>')&&s[1]==*s){
|
|
m=*s;
|
|
s+=2;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
if(m=='<')
|
|
new->type=LSH;
|
|
else
|
|
new->type=RSH;
|
|
new->left=left;
|
|
new->right=unary_expr();
|
|
left=new;
|
|
EXPSKIP();
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *multiplicative_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
char m;
|
|
left=shift_expr();
|
|
EXPSKIP();
|
|
while(*s=='*'||*s=='/'||*s=='%'){
|
|
m=*s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
if(m=='/'){
|
|
if(*s=='/'){
|
|
s++;
|
|
new->type=MOD;
|
|
}
|
|
else
|
|
new->type=DIV;
|
|
}
|
|
else if(m=='*')
|
|
new->type=MUL;
|
|
else
|
|
new->type=MOD;
|
|
new->left=left;
|
|
new->right=shift_expr();
|
|
left=new;
|
|
EXPSKIP();
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *and_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
left=multiplicative_expr();
|
|
EXPSKIP();
|
|
while(*s=='&'&&s[1]!='&'){
|
|
s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=BAND;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=multiplicative_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *exclusive_or_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
left=and_expr();
|
|
EXPSKIP();
|
|
while(*s=='^'||*s=='~'){
|
|
s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=XOR;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=and_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *inclusive_or_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
left=exclusive_or_expr();
|
|
EXPSKIP();
|
|
while((*s=='|'&&s[1]!='|')||(*s=='!'&&s[1]!='=')){
|
|
s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=BOR;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=exclusive_or_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *additive_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
char m;
|
|
left=inclusive_or_expr();
|
|
EXPSKIP();
|
|
while(*s=='+'||*s=='-'){
|
|
m=*s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
if(m=='+')
|
|
new->type=ADD;
|
|
else
|
|
new->type=SUB;
|
|
new->left=left;
|
|
new->right=inclusive_or_expr();
|
|
left=new;
|
|
EXPSKIP();
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *relational_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
char m1,m2=0;
|
|
left=additive_expr();
|
|
EXPSKIP();
|
|
while(((*s=='<'&&s[1]!='>')||*s=='>')&&s[1]!=*s){
|
|
m1=*s++;
|
|
if(*s=='=')
|
|
m2=*s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
if(m1=='<'){
|
|
if(m2)
|
|
new->type=LEQ;
|
|
else
|
|
new->type=LT;
|
|
}else{
|
|
if(m2)
|
|
new->type=GEQ;
|
|
else
|
|
new->type=GT;
|
|
}
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=additive_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *equality_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
char m;
|
|
left=relational_expr();
|
|
EXPSKIP();
|
|
while(*s=='='||(*s=='!'&&s[1]=='=')||(*s=='<'&&s[1]=='>')){
|
|
m=*s++;
|
|
if(m==*s||m!='=')
|
|
s++;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
if(m=='=')
|
|
new->type=EQ;
|
|
else
|
|
new->type=NEQ;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=relational_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *logical_and_expr(void)
|
|
{
|
|
expr *left,*new;
|
|
left=equality_expr();
|
|
EXPSKIP();
|
|
while(*s=='&'&&s[1]=='&'){
|
|
s+=2;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=LAND;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=equality_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
static expr *expression()
|
|
{
|
|
expr *left,*new;
|
|
left=logical_and_expr();
|
|
EXPSKIP();
|
|
while(*s=='|'&&s[1]=='|'){
|
|
s+=2;
|
|
EXPSKIP();
|
|
new=new_expr();
|
|
new->type=LOR;
|
|
EXPSKIP();
|
|
new->left=left;
|
|
new->right=logical_and_expr();
|
|
left=new;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
/* Tries to parse the string as a constant value. Sets pp to the
|
|
end of the parse. Already defined absolute symbols are
|
|
recognized. */
|
|
expr *parse_expr(char **pp)
|
|
{
|
|
expr *tree;
|
|
s=*pp;
|
|
make_tmp_lab=0;
|
|
tree=expression();
|
|
simplify_expr(tree);
|
|
*pp=s;
|
|
return tree;
|
|
}
|
|
|
|
expr *parse_expr_tmplab(char **pp)
|
|
{
|
|
expr *tree;
|
|
s=*pp;
|
|
make_tmp_lab=1;
|
|
tree=expression();
|
|
simplify_expr(tree);
|
|
*pp=s;
|
|
return tree;
|
|
}
|
|
|
|
void free_expr(expr *tree)
|
|
{
|
|
if(!tree)
|
|
return;
|
|
free_expr(tree->left);
|
|
free_expr(tree->right);
|
|
myfree(tree);
|
|
}
|
|
|
|
/* Try to evaluate expression as far as possible. Subexpressions
|
|
only containing constants or absolute symbols are simplified. */
|
|
void simplify_expr(expr *tree)
|
|
{
|
|
taddr val;
|
|
if(!tree)
|
|
return;
|
|
simplify_expr(tree->left);
|
|
simplify_expr(tree->right);
|
|
if((tree->left&&tree->left->type!=NUM)||
|
|
(tree->right&&tree->right->type!=NUM))
|
|
return;
|
|
switch(tree->type){
|
|
case ADD:
|
|
val=(tree->left->c.val+tree->right->c.val);
|
|
break;
|
|
case SUB:
|
|
val=(tree->left->c.val-tree->right->c.val);
|
|
break;
|
|
case MUL:
|
|
val=(tree->left->c.val*tree->right->c.val);
|
|
break;
|
|
case DIV:
|
|
if(tree->right->c.val==0){
|
|
general_error(41);
|
|
val=0;
|
|
}else
|
|
val=(tree->left->c.val/tree->right->c.val);
|
|
break;
|
|
case MOD:
|
|
if(tree->right->c.val==0){
|
|
general_error(41);
|
|
val=0;
|
|
}else
|
|
val=(tree->left->c.val%tree->right->c.val);
|
|
break;
|
|
case NEG:
|
|
val=(-tree->left->c.val);
|
|
break;
|
|
case CPL:
|
|
val=(~tree->left->c.val);
|
|
break;
|
|
case LAND:
|
|
val=-(tree->left->c.val&&tree->right->c.val);
|
|
break;
|
|
case LOR:
|
|
val=-(tree->left->c.val||tree->right->c.val);
|
|
break;
|
|
case BAND:
|
|
val=(tree->left->c.val&tree->right->c.val);
|
|
break;
|
|
case BOR:
|
|
val=(tree->left->c.val|tree->right->c.val);
|
|
break;
|
|
case XOR:
|
|
val=(tree->left->c.val^tree->right->c.val);
|
|
break;
|
|
case NOT:
|
|
val=(!tree->left->c.val);
|
|
break;
|
|
case LSH:
|
|
val=(tree->left->c.val<<tree->right->c.val);
|
|
break;
|
|
case RSH:
|
|
val=(tree->left->c.val>>tree->right->c.val);
|
|
break;
|
|
case LT:
|
|
val=-(tree->left->c.val<tree->right->c.val);
|
|
break;
|
|
case GT:
|
|
val=-(tree->left->c.val>tree->right->c.val);
|
|
break;
|
|
case LEQ:
|
|
val=-(tree->left->c.val<=tree->right->c.val);
|
|
break;
|
|
case GEQ:
|
|
val=-(tree->left->c.val>=tree->right->c.val);
|
|
break;
|
|
case NEQ:
|
|
val=-(tree->left->c.val!=tree->right->c.val);
|
|
break;
|
|
case EQ:
|
|
val=-(tree->left->c.val==tree->right->c.val);
|
|
break;
|
|
case SYM:
|
|
if(tree->c.sym->type==EXPRESSION&&tree->c.sym->expr->type==NUM){
|
|
val=tree->c.sym->expr->c.val;
|
|
break;
|
|
}else
|
|
return;
|
|
default:
|
|
#ifdef EXT_UNARY_EVAL
|
|
if (tree->left && EXT_UNARY_EVAL(tree->type,tree->left->c.val,&val,1))
|
|
break;
|
|
#endif
|
|
return;
|
|
}
|
|
free_expr(tree->left);
|
|
free_expr(tree->right);
|
|
tree->type=NUM;
|
|
tree->left=tree->right=0;
|
|
tree->c.val=val;
|
|
}
|
|
|
|
/* Evaluate an expression using current values of all symbols.
|
|
Result is written to *result. The return value specifies
|
|
whether the result is constant (i.e. only depending on
|
|
constants or absolute symbols). */
|
|
int eval_expr(expr *tree,taddr *result,section *sec,taddr pc)
|
|
{
|
|
taddr val,lval,rval;
|
|
symbol *lsym,*rsym;
|
|
int cnst=1;
|
|
|
|
if(!tree)
|
|
ierror(0);
|
|
if(tree->left&&!eval_expr(tree->left,&lval,sec,pc))
|
|
cnst=0;
|
|
if(tree->right&&!eval_expr(tree->right,&rval,sec,pc))
|
|
cnst=0;
|
|
|
|
switch(tree->type){
|
|
case ADD:
|
|
val=(lval+rval);
|
|
break;
|
|
case SUB:
|
|
find_base(tree->left,&lsym,sec,pc);
|
|
find_base(tree->right,&rsym,sec,pc);
|
|
/* l2-l1 is constant when both have a valid symbol-base, and both
|
|
symbols are LABSYMs from the same section, e.g. (sym1+x)-(sym2-y) */
|
|
if(cnst==0&&lsym!=NULL&&rsym!=NULL)
|
|
cnst=lsym->type==LABSYM&&rsym->type==LABSYM&&lsym->sec==rsym->sec;
|
|
/* Difference between symbols from different section or between an
|
|
external symbol and a symbol from the current section can be
|
|
represented by a REL_PC, so we calculate the addend. */
|
|
if(lsym!=NULL&&rsym!=NULL&&rsym->type==LABSYM&&
|
|
rsym->sec==sec&&lsym->sec!=NULL&&
|
|
((lsym->type==LABSYM&&lsym->sec!=rsym->sec)||lsym->type==IMPORT))
|
|
val=(pc-rval+lval-lsym->sec->org);
|
|
else
|
|
val=(lval-rval);
|
|
break;
|
|
case MUL:
|
|
val=(lval*rval);
|
|
break;
|
|
case DIV:
|
|
if(rval==0){
|
|
general_error(41);
|
|
val=0;
|
|
}else
|
|
val=(lval/rval);
|
|
break;
|
|
case MOD:
|
|
if(rval==0){
|
|
general_error(41);
|
|
val=0;
|
|
}else
|
|
val=(lval%rval);
|
|
break;
|
|
case NEG:
|
|
val=(-lval);
|
|
break;
|
|
case CPL:
|
|
val=(~lval);
|
|
break;
|
|
case LAND:
|
|
val=-(lval&&rval);
|
|
break;
|
|
case LOR:
|
|
val=-(lval||rval);
|
|
break;
|
|
case BAND:
|
|
val=(lval&rval);
|
|
break;
|
|
case BOR:
|
|
val=(lval|rval);
|
|
break;
|
|
case XOR:
|
|
val=(lval^rval);
|
|
break;
|
|
case NOT:
|
|
val=(!lval);
|
|
break;
|
|
case LSH:
|
|
val=(lval<<rval);
|
|
break;
|
|
case RSH:
|
|
val=(lval>>rval);
|
|
break;
|
|
case LT:
|
|
val=-(lval<rval);
|
|
break;
|
|
case GT:
|
|
val=-(lval>rval);
|
|
break;
|
|
case LEQ:
|
|
val=-(lval<=rval);
|
|
break;
|
|
case GEQ:
|
|
val=-(lval>=rval);
|
|
break;
|
|
case NEQ:
|
|
val=-(lval!=rval);
|
|
break;
|
|
case EQ:
|
|
val=-(lval==rval);
|
|
break;
|
|
case SYM:
|
|
if(tree->c.sym->type==EXPRESSION){
|
|
if(tree->c.sym->flags&INEVAL)
|
|
general_error(18,tree->c.sym->name);
|
|
tree->c.sym->flags|=INEVAL;
|
|
cnst=eval_expr(tree->c.sym->expr,&val,sec,pc);
|
|
tree->c.sym->flags&=~INEVAL;
|
|
}else if(tree->c.sym->type==LABSYM){
|
|
if(tree->c.sym==cpc&&sec!=0){
|
|
cpc->sec=sec;
|
|
cpc->pc=pc;
|
|
}
|
|
val=tree->c.sym->pc;
|
|
cnst=sec==NULL?0:(sec->flags&UNALLOCATED)!=0;
|
|
}else{
|
|
/* IMPORT */
|
|
cnst=0;
|
|
val=0;
|
|
}
|
|
break;
|
|
case NUM:
|
|
val=tree->c.val;
|
|
break;
|
|
default:
|
|
#ifdef EXT_UNARY_EVAL
|
|
if (EXT_UNARY_EVAL(tree->type,lval,&val,cnst))
|
|
break;
|
|
#endif
|
|
ierror(0);
|
|
}
|
|
*result=val;
|
|
return cnst;
|
|
}
|
|
|
|
void print_expr(FILE *f,expr *p)
|
|
{
|
|
simplify_expr(p);
|
|
if(p->type==NUM)
|
|
fprintf(f,"%lu",(unsigned long)p->c.val);
|
|
else
|
|
fprintf(f,"complex expression");
|
|
}
|
|
|
|
/* Tests, if an expression is based only on one non-absolute
|
|
symbol plus constants. Returns that symbol or zero.
|
|
Note: Does not find all possible solutions. */
|
|
int find_base(expr *p,symbol **base,section *sec,taddr pc)
|
|
{
|
|
if(base)
|
|
*base=NULL;
|
|
if(p->type==SYM){
|
|
if(p->c.sym==cpc&&sec!=NULL){
|
|
cpc->sec=sec;
|
|
cpc->pc=pc;
|
|
}
|
|
if(p->c.sym->type==EXPRESSION)
|
|
return find_base(p->c.sym->expr,base,sec,pc);
|
|
else{
|
|
if(base)
|
|
*base=p->c.sym;
|
|
return BASE_OK;
|
|
}
|
|
}
|
|
if(p->type==ADD){
|
|
taddr val;
|
|
if(eval_expr(p->left,&val,sec,pc)&&
|
|
find_base(p->right,base,sec,pc)==BASE_OK)
|
|
return BASE_OK;
|
|
if(eval_expr(p->right,&val,sec,pc)&&
|
|
find_base(p->left,base,sec,pc)==BASE_OK)
|
|
return BASE_OK;
|
|
}
|
|
if(p->type==SUB){
|
|
taddr val;
|
|
symbol *pcsym;
|
|
if(eval_expr(p->right,&val,sec,pc)&&
|
|
find_base(p->left,base,sec,pc)==BASE_OK)
|
|
return BASE_OK;
|
|
if(find_base(p->left,base,sec,pc)==BASE_OK&&
|
|
find_base(p->right,&pcsym,sec,pc)==BASE_OK) {
|
|
if(pcsym->type==LABSYM&&pcsym->sec==sec&&
|
|
((*base)->type==LABSYM||(*base)->type==IMPORT))
|
|
return BASE_PCREL;
|
|
}
|
|
}
|
|
#ifdef EXT_FIND_BASE
|
|
return EXT_FIND_BASE(base,p,sec,pc);
|
|
#else
|
|
return BASE_ILLEGAL;
|
|
#endif
|
|
}
|
|
|
|
expr *number_expr(taddr val)
|
|
{
|
|
expr *new=new_expr();
|
|
new->type=NUM;
|
|
new->c.val=val;
|
|
return new;
|
|
}
|
|
|
|
taddr parse_constexpr(char **s)
|
|
{
|
|
expr *tree;
|
|
taddr val = 0;
|
|
|
|
if (tree = parse_expr(s)) {
|
|
simplify_expr(tree);
|
|
if (tree->type == NUM)
|
|
val = tree->c.val;
|
|
else
|
|
general_error(30); /* expression must be a constant */
|
|
free_expr(tree);
|
|
}
|
|
return val;
|
|
}
|