/*
 *      eval.c	-	evaluate expression
 *
 *	From Motorola BBS.
 *	See ../doc/ChangeLog to examine and/or add change descriptions.
 *
 *      an expression is constructed like this:
 *
 *      expr ::=  expr + term |
 *                expr - term |
 *                expr * term |
 *                expr / term |
 *                expr | term |
 *                expr & term |
 *                expr % term |
 *                expr ^ term ;
 *
 *      term ::=  symbol |
 *                * |         <-- program counter at start of current line
 *                constant ;
 *
 *      symbol ::=  string of alphanumerics with initial alpha char,
 *			underscore, or period ;
 *
 *      constant ::= hex constant |
 *                   binary constant |
 *                   octal constant |
 *                   decimal constant |
 *                   ascii constant ;
 *
 *      hex constant ::= $<hex digits> ;
 *
 *      octal constant ::= @<octal digits> ;
 *
 *      binary constant ::= %<binary digits> ;
 *
 *      decimal constant ::= <decimal digits> ;
 *
 *      ascii constant ::= '<any printing char> ;
 *
 */
int
eval(void)
{
        int     left,right;     /* left and right terms for expression */
        char    o;              /* operator character */

#ifdef DEBUG
	if (DebugMess)
		printf("Evaluating %s\n",Optr);
#endif
        Force_byte = NO;
	Force_word = NO;
	Result = 0;
        if(*Optr=='<'){
                Force_byte++;
                Optr++;
                }
        else if(*Optr=='>'){
                Force_word++;
                Optr++;
                }
	left = get_term();         /* pickup first part of expression */
	if( Term_err ) return(NO);

	while( is_op(*Optr) ) {
                o = *Optr++; /* pickup connector and skip */
		right = get_term();     /* pickup current rightmost side */
		if( Term_err ) {
			Result = 0;
			return(NO);
			}
                switch(o){
                        case '+': left += right; break;
                        case '-': left -= right; break;
                        case '*': left *= right; break;
                        case '/': left /= right; break;
                        case '|': left |= right; break;
                        case '&': left &= right; break;
                        case '%': left %= right; break;
                        case '^': left = left^right; break;
                        }
		}

	Result= left;
#ifdef DEBUG
	if (DebugMess)
	{
		printf("Result=%x\n",Result);
		printf("Force_byte=%d  Force_word=%d\n",Force_byte,Force_word);
	}
#endif
	if( !any( *Optr, " \t\n,;" ) && *Optr!=EOS ) {
		error( "Improper character in expression" );
		return(NO);
		}
        return(YES);
}

/*
 *      is_op --- is character an expression operator?
 */
int
is_op(char c)
{
        if( any(c,"+-*/&%|^"))
                return(YES);
        return(NO);
}


/*
 *      get_term --- evaluate a single item in an expression
 */
int
get_term(void)
{
        char    hold[MAXBUF];
        char    *tmp;
        int     val = 0;        /* local value being built */
        int     minus;          /* unary minus flag */
        struct nlist *lookup(char *name),*pointer;
	struct link *pnt,*bpnt;

	Term_err = NO;

        if( *Optr == '-' ){
                Optr++;
                minus =YES;
                }
        else
                minus = NO;

	while( *Optr == '#' ) Optr++;

	if( *Optr == '-' ) {    /* allow - on either side of # */
	   if( minus == NO ) {
		Optr++;
		minus = YES;
		}
	   else {               /* don't allow - on both sides of # */
		error("Minus signs on both sides of #");
		Term_err = YES;
		return(val);
		}
	   }

        /* look at rest of expression */

        if(*Optr=='%'){ /* binary constant */
                Optr++;
                while( any(*Optr,"01"))
                        val = (val * 2) + ( (*Optr++)-'0');
                }
        else if(*Optr=='@'){ /* octal constant */
                Optr++;
                while( any(*Optr,"01234567"))
                        val = (val * 8) + ((*Optr++)-'0');
                }
        else if(*Optr=='$'){ /* hex constant */
                Optr++;
		while( any(*Optr,"0123456789abcdefABCDEF")) {
                        if( *Optr > '9' )
                                val = (val * 16) + 10 + (mapdn(*Optr++)-'a');
                        else
				val = (val * 16) + ((*Optr++)-'0');
			}
                }
        else if( any(*Optr,"0123456789")){ /* decimal constant */
                while(*Optr >= '0' && *Optr <= '9')
                        val = (val * 10) + ( (*Optr++)-'0');
                }
        else if(*Optr=='*'){    /* current location counter */
                Optr++;
                val = Old_pc;
                }
        else if(*Optr=='\''){   /* quoted character */
                Optr++;		/* skip quote */
		if (*Optr=='\\')	/* ohno!, backslash */
		{
			if ( (*(Optr+1) == '\\') ||
				( (*(Optr+1) == '\'') && (*(Optr+2) == '\'') ) )	
					Optr++; /* skip past backslash */
			/* else quoted backslash '\' */
		}
		/* pick up quoted char */
		if(*Optr == EOS) {
			error("NULL character constant not allowed");
			val = 0;
			Term_err = YES;
			}
		else {
			val = *Optr++;
			if(*Optr=='\'') Optr++; /* allow closing quote */
			}
                }
        else if( alpha(*Optr) ){ /* a symbol */
                tmp = hold;     /* collect symbol name */
                while(alphan(*Optr))
                        *tmp++ = *Optr++;
                *tmp = EOS;
                pointer = lookup(hold);
                  if (pointer != NULL)
                   {
                   if (Pass == 2)
                    {
                       pnt = pointer->L_list;
                        bpnt = NULL;
                        while (pnt != NULL)
                         {
                           bpnt = pnt;
                           pnt = pnt->next;
                         }
                        pnt = (struct link *) alloc(sizeof(struct link));
                      if (bpnt == NULL)
                       pointer->L_list = pnt;
                      else bpnt->next = pnt;
                     pnt->L_num = Line_num;
                    pnt->next = NULL;
                    }
                       val = Last_sym;
                   }
                else{
                        if(Pass==1){    /* forward ref here */
                                fwdmark();
                                if( !Force_byte )
                                        Force_word++;
                                val = 0;
                                }
                        }
                if(Pass==2 && Line_num==F_ref && Cfn==Ffn){
                        if( !Force_byte  )
                                Force_word++;
                        fwdnext();
                        }
                }
	else {  /* none of the above */
		if( white(*Optr) ) error("Whitespace not allowed in expression");
		else error("Missing or unidentifiable operand ");
		val = 0;
		Term_err = YES;
		}

        if(minus)
                return(-val);
        else
                return(val);
}
