2837 lines
		
	
	
		
			103 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2837 lines
		
	
	
		
			103 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*  Pawn compiler - Recursive descend expresion parser
 | |
|  *
 | |
|  *  Copyright (c) ITB CompuPhase, 1997-2005
 | |
|  *
 | |
|  *  This software is provided "as-is", without any express or implied warranty.
 | |
|  *  In no event will the authors be held liable for any damages arising from
 | |
|  *  the use of this software.
 | |
|  *
 | |
|  *  Permission is granted to anyone to use this software for any purpose,
 | |
|  *  including commercial applications, and to alter it and redistribute it
 | |
|  *  freely, subject to the following restrictions:
 | |
|  *
 | |
|  *  1.  The origin of this software must not be misrepresented; you must not
 | |
|  *      claim that you wrote the original software. If you use this software in
 | |
|  *      a product, an acknowledgment in the product documentation would be
 | |
|  *      appreciated but is not reeq;quired.
 | |
|  *  2.  Altered source versions must be plainly marked as such, and must not be
 | |
|  *      misrepresented as being the original software.
 | |
|  *  3.  This notice may not be removed or altered from any source distribution.
 | |
|  *
 | |
|  *  Version: $Id$
 | |
|  */
 | |
| #include <assert.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>     /* for _MAX_PATH */
 | |
| #include <string.h>
 | |
| #if defined FORTIFY
 | |
|   #include <alloc/fortify.h>
 | |
| #endif
 | |
| #include "sc.h"
 | |
| #include "sctracker.h"
 | |
| 
 | |
| static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval,
 | |
|                 int (*hier)(value*),value *lval);
 | |
| static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval);
 | |
| static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval,
 | |
|                  char *forcetag,int chkbitwise);
 | |
| static int plnge1(int (*hier)(value *lval),value *lval);
 | |
| static void plnge2(void (*oper)(void),
 | |
|                    int (*hier)(value *lval),
 | |
|                    value *lval1,value *lval2);
 | |
| static cell calc(cell left,void (*oper)(),cell right,char *boolresult);
 | |
| static int hier14(value *lval);
 | |
| static int hier13(value *lval);
 | |
| static int hier12(value *lval);
 | |
| static int hier11(value *lval);
 | |
| static int hier10(value *lval);
 | |
| static int hier9(value *lval);
 | |
| static int hier8(value *lval);
 | |
| static int hier7(value *lval);
 | |
| static int hier6(value *lval);
 | |
| static int hier5(value *lval);
 | |
| static int hier4(value *lval);
 | |
| static int hier3(value *lval);
 | |
| static int hier2(value *lval);
 | |
| static int hier1(value *lval1);
 | |
| static int primary(value *lval);
 | |
| static void clear_value(value *lval);
 | |
| static void callfunction(symbol *sym,value *lval_result,int matchparanthesis);
 | |
| static int dbltest(void (*oper)(),value *lval1,value *lval2);
 | |
| static int commutative(void (*oper)());
 | |
| static int constant(value *lval);
 | |
| 
 | |
| static char lastsymbol[sNAMEMAX+1]; /* name of last function/variable */
 | |
| static int bitwise_opercount;   /* count of bitwise operators in an expression */
 | |
| 
 | |
| /* Function addresses of binary operators for signed operations */
 | |
| static void (*op1[17])(void) = {
 | |
|   os_mult,os_div,os_mod,        /* hier3, index 0 */
 | |
|   ob_add,ob_sub,                /* hier4, index 3 */
 | |
|   ob_sal,os_sar,ou_sar,         /* hier5, index 5 */
 | |
|   ob_and,                       /* hier6, index 8 */
 | |
|   ob_xor,                       /* hier7, index 9 */
 | |
|   ob_or,                        /* hier8, index 10 */
 | |
|   os_le,os_ge,os_lt,os_gt,      /* hier9, index 11 */
 | |
|   ob_eq,ob_ne,                  /* hier10, index 15 */
 | |
| };
 | |
| /* These two functions are defined because the functions inc() and dec() in
 | |
|  * SC4.C have a different prototype than the other code generation functions.
 | |
|  * The arrays for user-defined functions use the function pointers for
 | |
|  * identifying what kind of operation is requested; these functions must all
 | |
|  * have the same prototype. As inc() and dec() are special cases already, it
 | |
|  * is simplest to add two "do-nothing" functions.
 | |
|  */
 | |
| static void user_inc(void) {}
 | |
| static void user_dec(void) {}
 | |
| 
 | |
| /*
 | |
|  *  Searches for a binary operator a list of operators. The list is stored in
 | |
|  *  the array "list". The last entry in the list should be set to 0.
 | |
|  *
 | |
|  *  The index of an operator in "list" (if found) is returned in "opidx". If
 | |
|  *  no operator is found, nextop() returns 0.
 | |
|  *
 | |
|  *  If an operator is found in the expression, it cannot be used in a function
 | |
|  *  call with omitted parantheses. Mark this...
 | |
|  *
 | |
|  *  Global references: sc_allowproccall   (modified)
 | |
|  */
 | |
| static int nextop(int *opidx,int *list)
 | |
| {
 | |
|   *opidx=0;
 | |
|   while (*list){
 | |
|     if (matchtoken(*list)){
 | |
|       sc_allowproccall=FALSE;
 | |
|       return TRUE;      /* found! */
 | |
|     } else {
 | |
|       list+=1;
 | |
|       *opidx+=1;
 | |
|     } /* if */
 | |
|   } /* while */
 | |
|   return FALSE;         /* entire list scanned, nothing found */
 | |
| }
 | |
| 
 | |
| SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam,
 | |
|                          value *lval,int *resulttag)
 | |
| {
 | |
| static char *binoperstr[] = { "*", "/", "%", "+", "-", "", "", "",
 | |
|                               "", "", "", "<=", ">=", "<", ">", "==", "!=" };
 | |
| static int binoper_savepri[] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
 | |
|                                  FALSE, FALSE, FALSE, FALSE, FALSE,
 | |
|                                  TRUE, TRUE, TRUE, TRUE, FALSE, FALSE };
 | |
| static char *unoperstr[] = { "!", "-", "++", "--" };
 | |
| static void (*unopers[])(void) = { lneg, neg, user_inc, user_dec };
 | |
|   char opername[4] = "", symbolname[sNAMEMAX+1];
 | |
|   int i,swapparams,savepri,savealt;
 | |
|   int paramspassed;
 | |
|   symbol *sym;
 | |
| 
 | |
|   /* since user-defined operators on untagged operands are forbidden, we have
 | |
|    * a quick exit.
 | |
|    */
 | |
|   assert(numparam==1 || numparam==2);
 | |
|   if (tag1==0 && (numparam==1 || tag2==0))
 | |
|     return FALSE;
 | |
| 
 | |
|   savepri=savealt=FALSE;
 | |
|   /* find the name with the operator */
 | |
|   if (numparam==2) {
 | |
|     if (oper==NULL) {
 | |
|       /* assignment operator: a special case */
 | |
|       strcpy(opername,"=");
 | |
|       if (lval!=NULL && (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR))
 | |
|         savealt=TRUE;
 | |
|     } else {
 | |
|       assert( (sizeof binoperstr / sizeof binoperstr[0]) == (sizeof op1 / sizeof op1[0]) );
 | |
|       for (i=0; i<sizeof op1 / sizeof op1[0]; i++) {
 | |
|         if (oper==op1[i]) {
 | |
|           strcpy(opername,binoperstr[i]);
 | |
|           savepri=binoper_savepri[i];
 | |
|           break;
 | |
|         } /* if */
 | |
|       } /* for */
 | |
|     } /* if */
 | |
|   } else {
 | |
|     assert(oper!=NULL);
 | |
|     assert(numparam==1);
 | |
|     /* try a select group of unary operators */
 | |
|     assert( (sizeof unoperstr / sizeof unoperstr[0]) == (sizeof unopers / sizeof unopers[0]) );
 | |
|     if (opername[0]=='\0') {
 | |
|       for (i=0; i<sizeof unopers / sizeof unopers[0]; i++) {
 | |
|         if (oper==unopers[i]) {
 | |
|           strcpy(opername,unoperstr[i]);
 | |
|           break;
 | |
|         } /* if */
 | |
|       } /* for */
 | |
|     } /* if */
 | |
|   } /* if */
 | |
|   /* if not found, quit */
 | |
|   if (opername[0]=='\0')
 | |
|     return FALSE;
 | |
| 
 | |
|   /* create a symbol name from the tags and the operator name */
 | |
|   assert(numparam==1 || numparam==2);
 | |
|   operator_symname(symbolname,opername,tag1,tag2,numparam,tag2);
 | |
|   swapparams=FALSE;
 | |
|   sym=findglb(symbolname,sGLOBAL);
 | |
|   if (sym==NULL /*|| (sym->usage & uDEFINE)==0*/) {  /* ??? should not check uDEFINE; first pass clears these bits */
 | |
|     /* check for commutative operators */
 | |
|     if (tag1==tag2 || oper==NULL || !commutative(oper))
 | |
|       return FALSE;             /* not commutative, cannot swap operands */
 | |
|     /* if arrived here, the operator is commutative and the tags are different,
 | |
|      * swap tags and try again
 | |
|      */
 | |
|     assert(numparam==2);        /* commutative operator must be a binary operator */
 | |
|     operator_symname(symbolname,opername,tag2,tag1,numparam,tag1);
 | |
|     swapparams=TRUE;
 | |
|     sym=findglb(symbolname,sGLOBAL);
 | |
|     if (sym==NULL /*|| (sym->usage & uDEFINE)==0*/)
 | |
|       return FALSE;
 | |
|   } /* if */
 | |
| 
 | |
|   /* check existance and the proper declaration of this function */
 | |
|   if ((sym->usage & uMISSING)!=0 || (sym->usage & uPROTOTYPED)==0) {
 | |
|     char symname[2*sNAMEMAX+16];  /* allow space for user defined operators */
 | |
|     funcdisplayname(symname,sym->name);
 | |
|     if ((sym->usage & uMISSING)!=0)
 | |
|       error(4,symname);           /* function not defined */
 | |
|     if ((sym->usage & uPROTOTYPED)==0)
 | |
|       error(71,symname);          /* operator must be declared before use */
 | |
|   } /* if */
 | |
| 
 | |
|   /* we don't want to use the redefined operator in the function that
 | |
|    * redefines the operator itself, otherwise the snippet below gives
 | |
|    * an unexpected recursion:
 | |
|    *    fixed:operator+(fixed:a, fixed:b)
 | |
|    *        return a + b
 | |
|    */
 | |
|   if (sym==curfunc)
 | |
|     return FALSE;
 | |
| 
 | |
|   /* for increment and decrement operators, the symbol must first be loaded
 | |
|    * (and stored back afterwards)
 | |
|    */
 | |
|   if (oper==user_inc || oper==user_dec) {
 | |
|     assert(!savepri);
 | |
|     assert(lval!=NULL);
 | |
|     if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR)
 | |
|       pushreg(sPRI);            /* save current address in PRI */
 | |
|     rvalue(lval);               /* get the symbol's value in PRI */
 | |
|   } /* if */
 | |
| 
 | |
|   assert(!savepri || !savealt); /* either one MAY be set, but not both */
 | |
|   if (savepri) {
 | |
|     /* the chained comparison operators require that the ALT register is
 | |
|      * unmodified, so we save it here; actually, we save PRI because the normal
 | |
|      * instruction sequence (without user operator) swaps PRI and ALT
 | |
|      */
 | |
|     pushreg(sPRI);              /* right-hand operand is in PRI */
 | |
|   } else if (savealt) {
 | |
|     /* for the assignment operator, ALT may contain an address at which the
 | |
|      * result must be stored; this address must be preserved accross the
 | |
|      * call
 | |
|      */
 | |
|     assert(lval!=NULL);         /* this was checked earlier */
 | |
|     assert(lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); /* checked earlier */
 | |
|     pushreg(sALT);
 | |
|   } /* if */
 | |
| 
 | |
|   /* push parameters, call the function */
 | |
|   paramspassed= (oper==NULL) ? 1 : numparam;
 | |
|   switch (paramspassed) {
 | |
|   case 1:
 | |
|     pushreg(sPRI);
 | |
|     break;
 | |
|   case 2:
 | |
|     /* note that 1) a function expects that the parameters are pushed
 | |
|      * in reversed order, and 2) the left operand is in the secondary register
 | |
|      * and the right operand is in the primary register */
 | |
|     if (swapparams) {
 | |
|       pushreg(sALT);
 | |
|       pushreg(sPRI);
 | |
|     } else {
 | |
|       pushreg(sPRI);
 | |
|       pushreg(sALT);
 | |
|     } /* if */
 | |
|     break;
 | |
|   default:
 | |
|     assert(0);
 | |
|   } /* switch */
 | |
|   markexpr(sPARM,NULL,0);       /* mark the end of a sub-expression */
 | |
|   pushval((cell)paramspassed /* *sizeof(cell)*/ );
 | |
|   assert(sym->ident==iFUNCTN);
 | |
|   ffcall(sym,NULL,paramspassed);
 | |
|   if (sc_status!=statSKIP)
 | |
|     markusage(sym,uREAD);       /* do not mark as "used" when this call itself is skipped */
 | |
|   if ((sym->usage & uNATIVE)!=0 && sym->x.lib!=NULL)
 | |
|     sym->x.lib->value += 1;     /* increment "usage count" of the library */
 | |
|   sideeffect=TRUE;              /* assume functions carry out a side-effect */
 | |
|   assert(resulttag!=NULL);
 | |
|   *resulttag=sym->tag;          /* save tag of the called function */
 | |
| 
 | |
|   if (savepri || savealt)
 | |
|     popreg(sALT);               /* restore the saved PRI/ALT that into ALT */
 | |
|   if (oper==user_inc || oper==user_dec) {
 | |
|     assert(lval!=NULL);
 | |
|     if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR)
 | |
|       popreg(sALT);             /* restore address (in ALT) */
 | |
|     store(lval);                /* store PRI in the symbol */
 | |
|     moveto1();                  /* make sure PRI is restored on exit */
 | |
|   } /* if */
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| SC_FUNC int checktags_string(int tags[], int numtags, value *sym1)
 | |
| {
 | |
|   int i;
 | |
|   if (sym1->ident == iARRAY || sym1->ident == iREFARRAY)
 | |
|   {
 | |
|     return FALSE;
 | |
|   }
 | |
|   for (i=0; i<numtags; i++) {
 | |
|     if ((sym1->tag == pc_tag_string && tags[i] == 0) ||
 | |
| 		(sym1->tag == 0 && tags[i] == pc_tag_string))
 | |
|       return TRUE;
 | |
|   }
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| SC_FUNC int checktag_string(value *sym1, value *sym2)
 | |
| {
 | |
|   if (sym1->ident == iARRAY || sym2->ident == iARRAY
 | |
| 	  || sym1->ident == iREFARRAY || sym2->ident == iREFARRAY)
 | |
|   {
 | |
|     return FALSE;
 | |
|   }
 | |
|   if ((sym1->tag == pc_tag_string && sym2->tag == 0)
 | |
| 	  || (sym1->tag == 0 && sym2->tag == pc_tag_string))
 | |
|   {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| SC_FUNC int matchtag_string(int ident, int tag)
 | |
| {
 | |
|   if (ident == iARRAY || ident == iREFARRAY)
 | |
|     return FALSE;
 | |
|   return (tag == pc_tag_string) ? TRUE : FALSE;
 | |
| }
 | |
| 
 | |
| SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce)
 | |
| {
 | |
|   if (formaltag != actualtag) {
 | |
|     /* if the formal tag is zero and the actual tag is not "fixed", the actual
 | |
|      * tag is "coerced" to zero
 | |
|      */
 | |
|     if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) {
 | |
| 		if (formaltag == pc_anytag || actualtag == pc_anytag)
 | |
| 		{
 | |
| 			return TRUE;
 | |
| 		}
 | |
| 
 | |
| 		if (formaltag & FUNCTAG)
 | |
| 		{
 | |
| 			if (actualtag == pc_functag || (formaltag == pc_functag && actualtag & FUNCTAG))
 | |
| 			{
 | |
| 				return TRUE;
 | |
| 			} else if (actualtag & FUNCTAG) {
 | |
| 				constvalue *v = find_tag_byval(actualtag);
 | |
| 				int index;
 | |
| 				short usage = uPUBLIC;
 | |
| 				symbol *sym, *found = NULL;
 | |
| 				funcenum_t *e;
 | |
| 				functag_t *t;
 | |
| 
 | |
| 				if (strncmp(v->name, "$Func", 5) != 0)
 | |
| 				{
 | |
| 					return FALSE;
 | |
| 				}
 | |
| 
 | |
| 				/* Now we have to go about looking up each function in this enum.  WHICH IS IT. */
 | |
| 				e = funcenums_find_byval(formaltag);
 | |
| 				if (!e)
 | |
| 				{
 | |
| 					return FALSE;
 | |
| 				}
 | |
| 
 | |
| 				assert(v->name[5] == '@' || v->name[5] == '!');
 | |
| 
 | |
| 				/* Deduce which function type this is */
 | |
| 				if (v->name[5] == '@')
 | |
| 				{
 | |
| 					usage = uPUBLIC;
 | |
| 				} else if (v->name[5] = '!') {
 | |
| 					usage = uSTOCK;
 | |
| 				}
 | |
| 
 | |
| 				index = atoi(&v->name[6]);
 | |
| 
 | |
| 				assert(index >= 0);
 | |
| 
 | |
| 				/* Find the function, either by public idx or code addr */
 | |
| 				if (usage == uPUBLIC)
 | |
| 				{
 | |
| 					for (sym=glbtab.next; sym!=NULL; sym=sym->next) {
 | |
| 						if (sym->ident==iFUNCTN && (sym->usage & uPUBLIC)!=0 && (sym->vclass == sGLOBAL))
 | |
| 						{
 | |
| 							if (index-- == 0)
 | |
| 							{
 | |
| 								found = sym;
 | |
| 								break;
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				} else if (usage == uSTOCK) {
 | |
| 					for (sym=glbtab.next; sym!=NULL; sym=sym->next) {
 | |
| 						if (sym->ident==iFUNCTN && (sym->vclass == sGLOBAL))
 | |
| 						{
 | |
| 							if (sym->codeaddr == index)
 | |
| 							{
 | |
| 								found = sym;
 | |
| 								break;
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (!found)
 | |
| 				{
 | |
| 					assert(found);
 | |
| 					return FALSE;
 | |
| 				}
 | |
| 
 | |
| 				/* Wow, we now have:
 | |
| 				 * 1) The functional enum deduced from formaltag
 | |
| 				 * 2) The function trying to be shoved in deduced from actualtag
 | |
| 				 * Now we have to check if it matches any one of the functags inside the enum.
 | |
| 				 */
 | |
| 				t = e->first;
 | |
| 				while (t)
 | |
| 				{
 | |
| 					int curarg,skip=0,i;
 | |
| 					arginfo *func_arg;
 | |
| 					funcarg_t *enum_arg;
 | |
| 					/* Check return type first. */
 | |
| 					if (t->ret_tag != sym->tag)
 | |
| 					{
 | |
| 						t = t->next;
 | |
| 						continue;
 | |
| 					}
 | |
| 					/* Check usage */
 | |
| 					if (t->type != usage)
 | |
| 					{
 | |
| 						t = t->next;
 | |
| 						continue;
 | |
| 					}
 | |
| 					/* Begin iterating arguments */
 | |
| 					for (curarg=0; curarg<t->argcount; curarg++)
 | |
| 					{
 | |
| 						enum_arg = &t->args[curarg];
 | |
| 						/* Check whether we've exhausted our arguments */
 | |
| 						if (sym->dim.arglist[curarg].ident == 0)
 | |
| 						{
 | |
| 							/* Can we bail out early? */
 | |
| 							if (!enum_arg->ommittable)
 | |
| 							{
 | |
| 								/* No! */
 | |
| 								skip = 1;
 | |
| 							}
 | |
| 							break;
 | |
| 						}
 | |
| 						func_arg = &sym->dim.arglist[curarg];
 | |
| 						/* First check the ident type */
 | |
| 						if (enum_arg->ident != func_arg->ident)
 | |
| 						{
 | |
| 							skip = 1;
 | |
| 							break;
 | |
| 						}
 | |
| 						/* Next check arrayness */
 | |
| 						if (enum_arg->dimcount != func_arg->numdim)
 | |
| 						{
 | |
| 							skip = 1;
 | |
| 							break;
 | |
| 						}
 | |
| 						if (enum_arg->dimcount > 0)
 | |
| 						{
 | |
| 							for (i=0; i<enum_arg->dimcount; i++)
 | |
| 							{
 | |
| 								if (enum_arg->dims[i] != func_arg->dim[i])
 | |
| 								{
 | |
| 									skip = 1;
 | |
| 									break;
 | |
| 								}
 | |
| 							}
 | |
| 							if (skip)
 | |
| 							{
 | |
| 								break;
 | |
| 							}
 | |
| 						}
 | |
| 						/* Lastly, check the tags */
 | |
| 						if (enum_arg->tagcount != func_arg->numtags)
 | |
| 						{
 | |
| 							skip = 1;
 | |
| 							break;
 | |
| 						}
 | |
| 						/* They should all be in the same order just for clarity... */
 | |
| 						for (i=0; i<enum_arg->tagcount; i++)
 | |
| 						{
 | |
| 							if (enum_arg->tags[i] != func_arg->tags[i])
 | |
| 							{
 | |
| 								skip = 1;
 | |
| 								break;
 | |
| 							}
 | |
| 						}
 | |
| 						if (skip)
 | |
| 						{
 | |
| 							break;
 | |
| 						}
 | |
| 					}
 | |
| 					if (!skip)
 | |
| 					{
 | |
| 						/* Make sure there are no trailing arguments */
 | |
| 						if (sym->dim.arglist[curarg].ident == 0)
 | |
| 						{
 | |
| 							return TRUE;
 | |
| 						}
 | |
| 					}
 | |
| 					t = t->next;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return FALSE;
 | |
| 	}
 | |
|   } /* if */
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  The AMX pseudo-processor has no direct support for logical (boolean)
 | |
|  *  operations. These have to be done via comparing and jumping. Since we are
 | |
|  *  already jumping through the code, we might as well implement an "early
 | |
|  *  drop-out" evaluation (also called "short-circuit"). This conforms to
 | |
|  *  standard C:
 | |
|  *
 | |
|  *  expr1 || expr2           expr2 will only be evaluated if expr1 is false.
 | |
|  *  expr1 && expr2           expr2 will only be evaluated if expr1 is true.
 | |
|  *
 | |
|  *  expr1 || expr2 && expr3  expr2 will only be evaluated if expr1 is false
 | |
|  *                           and expr3 will only be evaluated if expr1 is
 | |
|  *                           false and expr2 is true.
 | |
|  *
 | |
|  *  Code generation for the last example proceeds thus:
 | |
|  *
 | |
|  *      evaluate expr1
 | |
|  *      operator || found
 | |
|  *      jump to "l1" if result of expr1 not equal to 0
 | |
|  *      evaluate expr2
 | |
|  *      ->  operator && found; skip to higher level in hierarchy diagram
 | |
|  *          jump to "l2" if result of expr2 equal to 0
 | |
|  *          evaluate expr3
 | |
|  *          jump to "l2" if result of expr3 equal to 0
 | |
|  *          set expression result to 1 (true)
 | |
|  *          jump to "l3"
 | |
|  *      l2: set expression result to 0 (false)
 | |
|  *      l3:
 | |
|  *      <-  drop back to previous hierarchy level
 | |
|  *      jump to "l1" if result of expr2 && expr3 not equal to 0
 | |
|  *      set expression result to 0 (false)
 | |
|  *      jump to "l4"
 | |
|  *  l1: set expression result to 1 (true)
 | |
|  *  l4:
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*  Skim over terms adjoining || and && operators
 | |
|  *  dropval   The value of the expression after "dropping out". An "or" drops
 | |
|  *            out when the left hand is TRUE, so dropval must be 1 on "or"
 | |
|  *            expressions.
 | |
|  *  endval    The value of the expression when no expression drops out. In an
 | |
|  *            "or" expression, this happens when both the left hand and the
 | |
|  *            right hand are FALSE, so endval must be 0 for "or" expressions.
 | |
|  */
 | |
| static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval,
 | |
|                 int (*hier)(value*),value *lval)
 | |
| {
 | |
|   int lvalue,hits,droplab,endlab,opidx;
 | |
|   int allconst,foundop;
 | |
|   cell constval;
 | |
|   int index;
 | |
|   cell cidx;
 | |
| 
 | |
|   stgget(&index,&cidx);         /* mark position in code generator */
 | |
|   hits=FALSE;                   /* no logical operators "hit" yet */
 | |
|   allconst=TRUE;                /* assume all values "const" */
 | |
|   constval=0;
 | |
|   droplab=0;                    /* to avoid a compiler warning */
 | |
|   for ( ;; ) {
 | |
|     lvalue=plnge1(hier,lval);   /* evaluate left expression */
 | |
| 
 | |
|     allconst= allconst && (lval->ident==iCONSTEXPR);
 | |
|     if (allconst) {
 | |
|       if (hits) {
 | |
|         /* one operator was already found */
 | |
|         if (testfunc==jmp_ne0)
 | |
|           lval->constval= lval->constval || constval;
 | |
|         else
 | |
|           lval->constval= lval->constval && constval;
 | |
|       } /* if */
 | |
|       constval=lval->constval;  /* save result accumulated so far */
 | |
|     } /* if */
 | |
| 
 | |
|     foundop=nextop(&opidx,opstr);
 | |
|     if ((foundop || hits) && (lval->ident==iARRAY || lval->ident==iREFARRAY))
 | |
|       error(33, lval->sym ? (lval->sym->name ? lval->sym->name : "-unknown") : "-unknown-");  /* array was not indexed in an expression */
 | |
|     if (foundop) {
 | |
|       if (!hits) {
 | |
|         /* this is the first operator in the list */
 | |
|         hits=TRUE;
 | |
|         droplab=getlabel();
 | |
|       } /* if */
 | |
|       dropout(lvalue,testfunc,droplab,lval);
 | |
|     } else if (hits) {                       /* no (more) identical operators */
 | |
|       dropout(lvalue,testfunc,droplab,lval); /* found at least one operator! */
 | |
|       ldconst(endval,sPRI);
 | |
|       jumplabel(endlab=getlabel());
 | |
|       setlabel(droplab);
 | |
|       ldconst(dropval,sPRI);
 | |
|       setlabel(endlab);
 | |
|       lval->sym=NULL;
 | |
|       lval->tag=pc_addtag("bool");  /* force tag to be "bool" */
 | |
|       if (allconst) {
 | |
|         lval->ident=iCONSTEXPR;
 | |
|         lval->constval=constval;
 | |
|         stgdel(index,cidx);         /* scratch generated code and calculate */
 | |
|       } else {
 | |
|         lval->ident=iEXPRESSION;
 | |
|         lval->constval=0;
 | |
|       } /* if */
 | |
|       return FALSE;
 | |
|     } else {
 | |
|       return lvalue;            /* none of the operators in "opstr" were found */
 | |
|     } /* if */
 | |
| 
 | |
|   } /* while */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  Reads into the primary register the variable pointed to by lval if
 | |
|  *  plunging through the hierarchy levels detected an lvalue. Otherwise
 | |
|  *  if a constant was detected, it is loaded. If there is no constant and
 | |
|  *  no lvalue, the primary register must already contain the expression
 | |
|  *  result.
 | |
|  *
 | |
|  *  After that, the compare routines "jmp_ne0" or "jmp_eq0" are called, which
 | |
|  *  compare the primary register against 0, and jump to the "early drop-out"
 | |
|  *  label "exit1" if the condition is true.
 | |
|  */
 | |
| static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval)
 | |
| {
 | |
|   if (lvalue)
 | |
|     rvalue(lval);
 | |
|   else if (lval->ident==iCONSTEXPR)
 | |
|     ldconst(lval->constval,sPRI);
 | |
|   (*testfunc)(exit1);
 | |
| }
 | |
| 
 | |
| static void checkfunction(value *lval)
 | |
| {
 | |
|   symbol *sym=lval->sym;
 | |
| 
 | |
|   if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC))
 | |
|     return;             /* no known symbol, or not a function result */
 | |
| 
 | |
|   if ((sym->usage & uDEFINE)!=0) {
 | |
|     /* function is defined, can now check the return value (but make an
 | |
|      * exception for directly recursive functions)
 | |
|      */
 | |
|     if (sym!=curfunc && (sym->usage & uRETVALUE)==0) {
 | |
|       char symname[2*sNAMEMAX+16];  /* allow space for user defined operators */
 | |
|       funcdisplayname(symname,sym->name);
 | |
|       error(209,symname);       /* function should return a value */
 | |
|     } /* if */
 | |
|   } else {
 | |
|     /* function not yet defined, set */
 | |
|     sym->usage|=uRETVALUE;      /* make sure that a future implementation of
 | |
|                                  * the function uses "return <value>" */
 | |
|   } /* if */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  Plunge to a lower level
 | |
|  */
 | |
| static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval,
 | |
|                  char *forcetag,int chkbitwise)
 | |
| {
 | |
|   int lvalue,opidx;
 | |
|   int count;
 | |
|   value lval2 = {0};
 | |
| 
 | |
|   lvalue=plnge1(hier,lval);
 | |
|   if (nextop(&opidx,opstr)==0)
 | |
|     return lvalue;              /* no operator in "opstr" found */
 | |
|   if (lvalue)
 | |
|     rvalue(lval);
 | |
|   count=0;
 | |
|   do {
 | |
|     if (chkbitwise && count++>0 && bitwise_opercount!=0)
 | |
|       error(212);
 | |
|     opidx+=opoff;               /* add offset to index returned by nextop() */
 | |
|     plnge2(op1[opidx],hier,lval,&lval2);
 | |
|     if (op1[opidx]==ob_and || op1[opidx]==ob_or)
 | |
|       bitwise_opercount++;
 | |
|     if (forcetag!=NULL)
 | |
|       lval->tag=pc_addtag(forcetag);
 | |
|   } while (nextop(&opidx,opstr)); /* do */
 | |
|   return FALSE;         /* result of expression is not an lvalue */
 | |
| }
 | |
| 
 | |
| /*  plnge_rel
 | |
|  *
 | |
|  *  Binary plunge to lower level; this is very simular to plnge, but
 | |
|  *  it has special code generation sequences for chained operations.
 | |
|  */
 | |
| static int plnge_rel(int *opstr,int opoff,int (*hier)(value *lval),value *lval)
 | |
| {
 | |
|   int lvalue,opidx;
 | |
|   value lval2={0};
 | |
|   int count;
 | |
|   char boolresult;
 | |
| 
 | |
|   /* this function should only be called for relational operators */
 | |
|   assert(op1[opoff]==os_le);
 | |
|   lvalue=plnge1(hier,lval);
 | |
|   if (nextop(&opidx,opstr)==0)
 | |
|     return lvalue;              /* no operator in "opstr" found */
 | |
|   if (lvalue)
 | |
|     rvalue(lval);
 | |
|   count=0;
 | |
|   lval->boolresult=TRUE;
 | |
|   do {
 | |
|     /* same check as in plnge(), but "chkbitwise" is always TRUE */
 | |
|     if (count>0 && bitwise_opercount!=0)
 | |
|       error(212);
 | |
|     if (count>0) {
 | |
|       relop_prefix();
 | |
|       boolresult=lval->boolresult;
 | |
|       *lval=lval2;      /* copy right hand expression of the previous iteration */
 | |
|       lval->boolresult=boolresult;
 | |
|     } /* if */
 | |
|     opidx+=opoff;
 | |
|     plnge2(op1[opidx],hier,lval,&lval2);
 | |
|     if (count++>0)
 | |
|       relop_suffix();
 | |
|   } while (nextop(&opidx,opstr)); /* enddo */
 | |
|   lval->constval=lval->boolresult;
 | |
|   lval->tag=pc_addtag("bool");    /* force tag to be "bool" */
 | |
|   return FALSE;         /* result of expression is not an lvalue */
 | |
| }
 | |
| 
 | |
| /*  plnge1
 | |
|  *
 | |
|  *  Unary plunge to lower level
 | |
|  *  Called by: skim(), plnge(), plnge2(), plnge_rel(), hier14() and hier13()
 | |
|  */
 | |
| static int plnge1(int (*hier)(value *lval),value *lval)
 | |
| {
 | |
|   int lvalue,index;
 | |
|   cell cidx;
 | |
| 
 | |
|   stgget(&index,&cidx); /* mark position in code generator */
 | |
|   lvalue=(*hier)(lval);
 | |
|   if (lval->ident==iCONSTEXPR)
 | |
|     stgdel(index,cidx); /* load constant later */
 | |
|   return lvalue;
 | |
| }
 | |
| 
 | |
| /*  plnge2
 | |
|  *
 | |
|  *  Binary plunge to lower level
 | |
|  *  Called by: plnge(), plnge_rel(), hier14() and hier1()
 | |
|  */
 | |
| static void plnge2(void (*oper)(void),
 | |
|                    int (*hier)(value *lval),
 | |
|                    value *lval1,value *lval2)
 | |
| {
 | |
|   int index;
 | |
|   cell cidx;
 | |
| 
 | |
|   stgget(&index,&cidx);             /* mark position in code generator */
 | |
|   if (lval1->ident==iCONSTEXPR) {   /* constant on left side; it is not yet loaded */
 | |
|     if (plnge1(hier,lval2))
 | |
|       rvalue(lval2);                /* load lvalue now */
 | |
|     else if (lval2->ident==iCONSTEXPR)
 | |
|       ldconst(lval2->constval<<dbltest(oper,lval2,lval1),sPRI);
 | |
|     ldconst(lval1->constval<<dbltest(oper,lval2,lval1),sALT);
 | |
|                    /* ^ doubling of constants operating on integer addresses */
 | |
|                    /*   is restricted to "add" and "subtract" operators */
 | |
|   } else {                          /* non-constant on left side */
 | |
|     pushreg(sPRI);
 | |
|     if (plnge1(hier,lval2))
 | |
|       rvalue(lval2);
 | |
|     if (lval2->ident==iCONSTEXPR) { /* constant on right side */
 | |
|       if (commutative(oper)) {      /* test for commutative operators */
 | |
|         value lvaltmp = {0};
 | |
|         stgdel(index,cidx);         /* scratch pushreg() and constant fetch (then
 | |
|                                      * fetch the constant again */
 | |
|         ldconst(lval2->constval<<dbltest(oper,lval1,lval2),sALT);
 | |
|         /* now, the primary register has the left operand and the secondary
 | |
|          * register the right operand; swap the "lval" variables so that lval1
 | |
|          * is associated with the secondary register and lval2 with the
 | |
|          * primary register, as is the "normal" case.
 | |
|          */
 | |
|         lvaltmp=*lval1;
 | |
|         *lval1=*lval2;
 | |
|         *lval2=lvaltmp;
 | |
|       } else {
 | |
|         ldconst(lval2->constval<<dbltest(oper,lval1,lval2),sPRI);
 | |
|         popreg(sALT);   /* pop result of left operand into secondary register */
 | |
|       } /* if */
 | |
|     } else {            /* non-constants on both sides */
 | |
|       popreg(sALT);
 | |
|       if (dbltest(oper,lval1,lval2))
 | |
|         cell2addr();                    /* double primary register */
 | |
|       if (dbltest(oper,lval2,lval1))
 | |
|         cell2addr_alt();                /* double secondary register */
 | |
|     } /* if */
 | |
|   } /* if */
 | |
|   if (oper) {
 | |
|     /* If used in an expression, a function should return a value.
 | |
|      * If the function has been defined, we can check this. If the
 | |
|      * function was not defined, we can set this requirement (so that
 | |
|      * a future function definition can check this bit.
 | |
|      */
 | |
|     checkfunction(lval1);
 | |
|     checkfunction(lval2);
 | |
|     if (lval1->ident==iARRAY || lval1->ident==iREFARRAY) {
 | |
|       char *ptr=(lval1->sym!=NULL) ? lval1->sym->name : "-unknown-";
 | |
|       error(33,ptr);                    /* array must be indexed */
 | |
|     } else if (lval2->ident==iARRAY || lval2->ident==iREFARRAY) {
 | |
|       char *ptr=(lval2->sym!=NULL) ? lval2->sym->name : "-unknown-";
 | |
|       error(33,ptr);                    /* array must be indexed */
 | |
|     } /* if */
 | |
|     /* ??? ^^^ should do same kind of error checking with functions */
 | |
| 
 | |
|     /* check whether an "operator" function is defined for the tag names
 | |
|      * (a constant expression cannot be optimized in that case)
 | |
|      */
 | |
|     if (check_userop(oper,lval1->tag,lval2->tag,2,NULL,&lval1->tag)) {
 | |
|       lval1->ident=iEXPRESSION;
 | |
|       lval1->constval=0;
 | |
|     } else if (lval1->ident==iCONSTEXPR && lval2->ident==iCONSTEXPR) {
 | |
|       /* only constant expression if both constant */
 | |
|       stgdel(index,cidx);       /* scratch generated code and calculate */
 | |
|       if (!matchtag(lval1->tag,lval2->tag,FALSE))
 | |
|         error(213);             /* tagname mismatch */
 | |
|       lval1->constval=calc(lval1->constval,oper,lval2->constval,&lval1->boolresult);
 | |
|     } else {
 | |
|       if (!checktag_string(lval1, lval2) && !matchtag(lval1->tag,lval2->tag,FALSE))
 | |
|         error(213);             /* tagname mismatch */
 | |
|       (*oper)();                /* do the (signed) operation */
 | |
|       lval1->ident=iEXPRESSION;
 | |
|     } /* if */
 | |
|   } /* if */
 | |
| }
 | |
| 
 | |
| static cell flooreddiv(cell a,cell b,int return_remainder)
 | |
| {
 | |
|   cell q,r;
 | |
| 
 | |
|   if (b==0) {
 | |
|     error(29);
 | |
|     return 0;
 | |
|   } /* if */
 | |
|   /* first implement truncated division in a portable way */
 | |
|   #define IABS(a)       ((a)>=0 ? (a) : (-a))
 | |
|   q=IABS(a)/IABS(b);
 | |
|   if ((cell)(a ^ b)<0)
 | |
|     q=-q;               /* swap sign if either "a" or "b" is negative (but not both) */
 | |
|   r=a-q*b;              /* calculate the matching remainder */
 | |
|   /* now "fiddle" with the values to get floored division */
 | |
|   if (r!=0 && (cell)(r ^ b)<0) {
 | |
|     q--;
 | |
|     r+=b;
 | |
|   } /* if */
 | |
|   return return_remainder ? r : q;
 | |
| }
 | |
| 
 | |
| static cell calc(cell left,void (*oper)(),cell right,char *boolresult)
 | |
| {
 | |
|   if (oper==ob_or)
 | |
|     return (left | right);
 | |
|   else if (oper==ob_xor)
 | |
|     return (left ^ right);
 | |
|   else if (oper==ob_and)
 | |
|     return (left & right);
 | |
|   else if (oper==ob_eq)
 | |
|     return (left == right);
 | |
|   else if (oper==ob_ne)
 | |
|     return (left != right);
 | |
|   else if (oper==os_le)
 | |
|     return *boolresult &= (char)(left <= right), right;
 | |
|   else if (oper==os_ge)
 | |
|     return *boolresult &= (char)(left >= right), right;
 | |
|   else if (oper==os_lt)
 | |
|     return *boolresult &= (char)(left < right), right;
 | |
|   else if (oper==os_gt)
 | |
|     return *boolresult &= (char)(left > right), right;
 | |
|   else if (oper==os_sar)
 | |
|     return (left >> (int)right);
 | |
|   else if (oper==ou_sar)
 | |
|     return ((ucell)left >> (ucell)right);
 | |
|   else if (oper==ob_sal)
 | |
|     return ((ucell)left << (int)right);
 | |
|   else if (oper==ob_add)
 | |
|     return (left + right);
 | |
|   else if (oper==ob_sub)
 | |
|     return (left - right);
 | |
|   else if (oper==os_mult)
 | |
|     return (left * right);
 | |
|   else if (oper==os_div)
 | |
|     return flooreddiv(left,right,0);
 | |
|   else if (oper==os_mod)
 | |
|     return flooreddiv(left,right,1);
 | |
|   else
 | |
|     error(29);  /* invalid expression, assumed 0 (this should never occur) */
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult,value *_lval)
 | |
| {
 | |
|   value lval={0};
 | |
|   pushheaplist();
 | |
| 
 | |
|   if (hier14(&lval))
 | |
|     rvalue(&lval);
 | |
|   /* scrap any arrays left on the heap */
 | |
|   popheaplist();
 | |
| 
 | |
|   if (lval.ident==iCONSTEXPR && val!=NULL)    /* constant expression */
 | |
|     *val=lval.constval;
 | |
|   if (tag!=NULL)
 | |
|     *tag=lval.tag;
 | |
|   if (symptr!=NULL)
 | |
|     *symptr=lval.sym;
 | |
|   if (chkfuncresult)
 | |
|     checkfunction(&lval);
 | |
|   if (_lval)
 | |
|     *_lval=lval;
 | |
|   return lval.ident;
 | |
| }
 | |
| 
 | |
| SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state)
 | |
| {
 | |
|   char name[sNAMEMAX+1];
 | |
|   cell val;
 | |
|   char *str;
 | |
|   int fsa,islabel;
 | |
| 
 | |
|   assert(automaton!=NULL);
 | |
|   assert(state!=NULL);
 | |
|   if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
 | |
|     return 0;
 | |
| 
 | |
|   tokeninfo(&val,&str);
 | |
|   assert(strlen(str)<sizeof name);
 | |
|   strcpy(name,str);
 | |
|   if (islabel || matchtoken(':')) {
 | |
|     /* token is an automaton name, add the name and get a new token */
 | |
|     *automaton=automaton_find(name);
 | |
|     /* read in the state name before checking the automaton, to keep the parser
 | |
|      * going (an "unknown automaton" error may occur when the "state" instruction
 | |
|      * precedes any state definition)
 | |
|      */
 | |
|     if (!needtoken(tSYMBOL))
 | |
|       return 0;
 | |
|     tokeninfo(&val,&str);        /* do not copy the name yet, must check automaton first */
 | |
|     if (*automaton==NULL) {
 | |
|       error(86,name);            /* unknown automaton */
 | |
|       return 0;
 | |
|     } /* if */
 | |
|     assert((*automaton)->index>0);
 | |
|     assert(strlen(str)<sizeof name);
 | |
|     strcpy(name,str);
 | |
|   } else {
 | |
|     *automaton=automaton_find("");
 | |
|     assert(*automaton!=NULL);
 | |
|     assert((*automaton)->index==0);
 | |
|   } /* if */
 | |
|   assert(*automaton!=NULL);
 | |
|   fsa=(*automaton)->index;
 | |
| 
 | |
|   assert(*automaton!=NULL);
 | |
|   *state=state_find(name,fsa);
 | |
|   if (*state==NULL) {
 | |
|     char *fsaname=(*automaton)->name;
 | |
|     if (*fsaname=='\0')
 | |
|       fsaname="<main>";
 | |
|     error(87,name,fsaname);   /* unknown state for automaton */
 | |
|     return 0;
 | |
|   } /* if */
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| SC_FUNC cell array_totalsize(symbol *sym)
 | |
| {
 | |
|   cell length;
 | |
| 
 | |
|   assert(sym!=NULL);
 | |
|   assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
 | |
|   length=sym->dim.array.length;
 | |
|   if (sym->dim.array.level > 0) {
 | |
|     cell sublength=array_totalsize(finddepend(sym));
 | |
|     if (sublength>0)
 | |
|       length=length+length*sublength;
 | |
|     else
 | |
|       length=0;
 | |
|   } /* if */
 | |
|   return length;
 | |
| }
 | |
| 
 | |
| static cell array_levelsize(symbol *sym,int level)
 | |
| {
 | |
|   assert(sym!=NULL);
 | |
|   assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
 | |
|   assert(level <= sym->dim.array.level);
 | |
|   while (level-- > 0) {
 | |
|     sym=finddepend(sym);
 | |
|     assert(sym!=NULL);
 | |
|   } /* if */
 | |
|   return (sym->dim.array.slength ? sym->dim.array.slength : sym->dim.array.length);
 | |
| }
 | |
| 
 | |
| /*  hier14
 | |
|  *
 | |
|  *  Lowest hierarchy level (except for the , operator).
 | |
|  *
 | |
|  *  Global references: sc_intest        (reffered to only)
 | |
|  *                     sc_allowproccall (modified)
 | |
|  */
 | |
| static int hier14(value *lval1)
 | |
| {
 | |
|   int lvalue;
 | |
|   value lval2={0},lval3={0};
 | |
|   void (*oper)(void);
 | |
|   int tok,level,i;
 | |
|   cell val;
 | |
|   char *st;
 | |
|   int bwcount,leftarray;
 | |
|   cell arrayidx1[sDIMEN_MAX],arrayidx2[sDIMEN_MAX];  /* last used array indices */
 | |
|   cell *org_arrayidx;
 | |
| 
 | |
|   bwcount=bitwise_opercount;
 | |
|   bitwise_opercount=0;
 | |
|   /* initialize the index arrays with unlikely constant indices; note that
 | |
|    * these indices will only be changed when the array is indexed with a
 | |
|    * constant, and that negative array indices are invalid (so actually, any
 | |
|    * negative value would do).
 | |
|    */
 | |
|   for (i=0; i<sDIMEN_MAX; i++)
 | |
|     arrayidx1[i]=arrayidx2[i]=(cell)(-1L << (sizeof(cell)*8-1));
 | |
|   org_arrayidx=lval1->arrayidx; /* save current pointer, to reset later */
 | |
|   if (lval1->arrayidx==NULL)
 | |
|     lval1->arrayidx=arrayidx1;
 | |
|   lvalue=plnge1(hier13,lval1);
 | |
|   if (lval1->ident!=iARRAYCELL && lval1->ident!=iARRAYCHAR)
 | |
|     lval1->arrayidx=NULL;
 | |
|   if (lval1->ident==iCONSTEXPR) /* load constant here */
 | |
|     ldconst(lval1->constval,sPRI);
 | |
|   tok=lex(&val,&st);
 | |
|   switch (tok) {
 | |
|     case taOR:
 | |
|       oper=ob_or;
 | |
|       break;
 | |
|     case taXOR:
 | |
|       oper=ob_xor;
 | |
|       break;
 | |
|     case taAND:
 | |
|       oper=ob_and;
 | |
|       break;
 | |
|     case taADD:
 | |
|       oper=ob_add;
 | |
|       break;
 | |
|     case taSUB:
 | |
|       oper=ob_sub;
 | |
|       break;
 | |
|     case taMULT:
 | |
|       oper=os_mult;
 | |
|       break;
 | |
|     case taDIV:
 | |
|       oper=os_div;
 | |
|       break;
 | |
|     case taMOD:
 | |
|       oper=os_mod;
 | |
|       break;
 | |
|     case taSHRU:
 | |
|       oper=ou_sar;
 | |
|       break;
 | |
|     case taSHR:
 | |
|       oper=os_sar;
 | |
|       break;
 | |
|     case taSHL:
 | |
|       oper=ob_sal;
 | |
|       break;
 | |
|     case '=':           /* simple assignment */
 | |
|       oper=NULL;
 | |
|       if (sc_intest)
 | |
|         error(211);     /* possibly unintended assignment */
 | |
|       break;
 | |
|     default:
 | |
|       lexpush();
 | |
|       bitwise_opercount=bwcount;
 | |
|       lval1->arrayidx=org_arrayidx; /* restore array index pointer */
 | |
|       return lvalue;
 | |
|   } /* switch */
 | |
| 
 | |
|   /* if we get here, it was an assignment; first check a few special cases
 | |
|    * and then the general */
 | |
|   if (lval1->ident==iARRAYCHAR) {
 | |
|     /* special case, assignment to packed character in a cell is permitted */
 | |
|     lvalue=TRUE;
 | |
|   } else if (lval1->ident==iARRAY || lval1->ident==iREFARRAY) {
 | |
|     /* array assignment is permitted too (with restrictions) */
 | |
|     if (oper)
 | |
|       return error(23); /* array assignment must be simple assigment */
 | |
|     assert(lval1->sym!=NULL);
 | |
|     if (array_totalsize(lval1->sym)==0)
 | |
|       return error(46,lval1->sym->name);        /* unknown array size */
 | |
|     lvalue=TRUE;
 | |
|   } /* if */
 | |
| 
 | |
|   /* operand on left side of assignment must be lvalue */
 | |
|   if (!lvalue)
 | |
|     return error(22);                   /* must be lvalue */
 | |
|   /* may not change "constant" parameters */
 | |
|   assert(lval1->sym!=NULL);
 | |
|   if ((lval1->sym->usage & uCONST)!=0)
 | |
|     return error(22);           /* assignment to const argument */
 | |
|   sc_allowproccall=FALSE;       /* may no longer use "procedure call" syntax */
 | |
| 
 | |
|   lval3=*lval1;         /* save symbol to enable storage of expresion result */
 | |
|   lval1->arrayidx=org_arrayidx; /* restore array index pointer */
 | |
|   if (lval1->ident==iARRAYCELL || lval1->ident==iARRAYCHAR
 | |
|       || lval1->ident==iARRAY || lval1->ident==iREFARRAY)
 | |
|   {
 | |
|     /* if indirect fetch: save PRI (cell address) */
 | |
|     if (oper) {
 | |
|       pushreg(sPRI);
 | |
|       rvalue(lval1);
 | |
|     } /* if */
 | |
|     lval2.arrayidx=arrayidx2;
 | |
|     plnge2(oper,hier14,lval1,&lval2);
 | |
|     if (lval2.ident!=iARRAYCELL && lval2.ident!=iARRAYCHAR)
 | |
|       lval2.arrayidx=NULL;
 | |
|     if (oper)
 | |
|       popreg(sALT);
 | |
|     if (!oper && lval3.arrayidx!=NULL && lval2.arrayidx!=NULL
 | |
|         && lval3.ident==lval2.ident && lval3.sym==lval2.sym)
 | |
|     {
 | |
|       int same=TRUE;
 | |
|       assert(lval2.arrayidx==arrayidx2);
 | |
|       for (i=0; i<sDIMEN_MAX; i++)
 | |
|         same=same && (lval3.arrayidx[i]==lval2.arrayidx[i]);
 | |
|         if (same)
 | |
|           error(226,lval3.sym->name);   /* self-assignment */
 | |
|     } /* if */
 | |
|   } else {
 | |
|     if (oper){
 | |
|       rvalue(lval1);
 | |
|       plnge2(oper,hier14,lval1,&lval2);
 | |
|     } else {
 | |
|       /* if direct fetch and simple assignment: no "push"
 | |
|        * and "pop" needed -> call hier14() directly, */
 | |
|       if (hier14(&lval2))
 | |
|         rvalue(&lval2);         /* instead of plnge2(). */
 | |
|       else if (lval2.ident==iVARIABLE)
 | |
|         lval2.ident=iEXPRESSION;/* mark as "rvalue" if it is not an "lvalue" */
 | |
|       checkfunction(&lval2);
 | |
|       /* check whether lval2 and lval3 (old lval1) refer to the same variable */
 | |
|       if (lval2.ident==iVARIABLE && lval3.ident==lval2.ident && lval3.sym==lval2.sym) {
 | |
|         assert(lval3.sym!=NULL);
 | |
|         error(226,lval3.sym->name);     /* self-assignment */
 | |
|       } /* if */
 | |
|     } /* if */
 | |
|   } /* if */
 | |
|   /* Array elements are sometimes considered as sub-arrays --when the
 | |
|    * array index is an enumeration field and the enumeration size is greater
 | |
|    * than 1. If the expression on the right side of the assignment is a cell,
 | |
|    * or if an operation is in effect, this does not apply.
 | |
|    */
 | |
|   leftarray= lval3.ident==iARRAY || lval3.ident==iREFARRAY
 | |
|              || ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR)
 | |
|                  && lval3.constval>1 && lval3.sym->dim.array.level==0
 | |
|                  && !oper && (lval2.ident==iARRAY || lval2.ident==iREFARRAY));
 | |
|   if (leftarray) {
 | |
|     /* Left operand is an array, right operand should be an array variable
 | |
|      * of the same size and the same dimension, an array literal (of the
 | |
|      * same size) or a literal string. For single-dimensional arrays without
 | |
|      * tag for the index, it is permitted to assign a smaller array into a
 | |
|      * larger one (without warning). This is to make it easier to work with
 | |
|      * strings.
 | |
|      */
 | |
|     int exactmatch=TRUE;
 | |
|     int idxtag=0;
 | |
|     int ltlength=(int)lval3.sym->dim.array.length;
 | |
|     if ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR)
 | |
|         && lval3.constval>0 && lval3.sym->dim.array.level==0)
 | |
|     {
 | |
|       ltlength=(int)lval3.constval;
 | |
|     } /* if */
 | |
|     if (lval2.ident!=iARRAY && lval2.ident!=iREFARRAY
 | |
|         && (lval2.sym==NULL || lval2.constval<=0))
 | |
|       error(33,lval3.sym->name);        /* array must be indexed */
 | |
|     if (lval2.sym!=NULL) {
 | |
|       if (lval2.constval==0) {
 | |
|         val=lval2.sym->dim.array.length;/* array variable */
 | |
|       } else {
 | |
|         val=lval2.constval;
 | |
|         if (lval2.sym->dim.array.level!=0)
 | |
|           error(28,lval2.sym->name);
 | |
|       } /* if */
 | |
|       level=lval2.sym->dim.array.level;
 | |
|       idxtag=lval2.sym->x.tags.index;
 | |
|       if (level==0 && idxtag==0 && lval3.sym->x.tags.index==0)
 | |
|         exactmatch=FALSE;
 | |
|     } else {
 | |
|       val=lval2.constval;               /* literal array */
 | |
|       level=0;
 | |
|       /* If val is negative, it means that lval2 is a literal string.
 | |
|        * The string array size may be smaller than the destination
 | |
|        * array, provided that the destination array does not have an
 | |
|        * index tag.
 | |
|        */
 | |
|       if (val<0) {
 | |
|         val=-val;
 | |
|         if (lval3.sym->x.tags.index==0)
 | |
|           exactmatch=FALSE;
 | |
|       } /* if */
 | |
|     } /* if */
 | |
|     if (lval3.sym->dim.array.level!=level)
 | |
|       return error(47); /* array dimensions must match */
 | |
|     else if (ltlength<val || exactmatch && ltlength>val || val==0)
 | |
|       return error(47); /* array sizes must match */
 | |
|     else if (lval3.ident!=iARRAYCELL && !matchtag(lval3.sym->x.tags.index,idxtag,TRUE))
 | |
|       error(229,(lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name); /* index tag mismatch */
 | |
|     if (level>0) {
 | |
|       /* check the sizes of all sublevels too */
 | |
|       symbol *sym1 = lval3.sym;
 | |
|       symbol *sym2 = lval2.sym;
 | |
|       int i;
 | |
|       error(23);
 | |
|       assert(sym1!=NULL && sym2!=NULL);
 | |
|       /* ^^^ sym2 must be valid, because only variables can be
 | |
|        *     multi-dimensional (there are no multi-dimensional literals),
 | |
|        *     sym1 must be valid because it must be an lvalue
 | |
|        */
 | |
|       assert(exactmatch);
 | |
|       for (i=0; i<level; i++) {
 | |
|         sym1=finddepend(sym1);
 | |
|         sym2=finddepend(sym2);
 | |
|         assert(sym1!=NULL && sym2!=NULL);
 | |
|         /* ^^^ both arrays have the same dimensions (this was checked
 | |
|          *     earlier) so the dependend should always be found
 | |
|          */
 | |
|         if (sym1->dim.array.length!=sym2->dim.array.length)
 | |
|           error(47);    /* array sizes must match */
 | |
|         else if (!matchtag(sym1->x.tags.index,sym2->x.tags.index,TRUE))
 | |
|           error(229,sym2->name);  /* index tag mismatch */
 | |
|       } /* for */
 | |
|       /* get the total size in cells of the multi-dimensional array */
 | |
|       val=array_totalsize(lval3.sym);
 | |
|       assert(val>0);    /* already checked */
 | |
|     } /* if */
 | |
|   } else {
 | |
|     /* left operand is not an array, right operand should then not be either */
 | |
|     if (lval2.ident==iARRAY || lval2.ident==iREFARRAY)
 | |
|       error(6);         /* must be assigned to an array */
 | |
|   } /* if */
 | |
|   if (leftarray) {
 | |
|     memcopy(val*sizeof(cell));
 | |
|   } else {
 | |
|     check_userop(NULL,lval2.tag,lval3.tag,2,&lval3,&lval2.tag);
 | |
|     store(&lval3);      /* now, store the expression result */
 | |
|   } /* if */
 | |
|   if (!oper && !checktag_string(&lval3, &lval2) && !matchtag(lval3.tag,lval2.tag,TRUE))
 | |
|     error(213);         /* tagname mismatch (if "oper", warning already given in plunge2()) */
 | |
|   if (lval3.sym)
 | |
|     markusage(lval3.sym,uWRITTEN);
 | |
|   sideeffect=TRUE;
 | |
|   bitwise_opercount=bwcount;
 | |
|   lval1->ident=iEXPRESSION;
 | |
|   return FALSE;         /* expression result is never an lvalue */
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Sums up array usage in the current heap tracer and convert it into a dynamic array.
 | |
|  * This is used for the ternary operator, which needs to convert its array usage into
 | |
|  * something dynamically managed.
 | |
|  * !Note:
 | |
|  * This might break if expressions can ever return dynamic arrays.
 | |
|  * Thus, we assert() if something is non-static here.
 | |
|  * Right now, this poses no problem because this type of expression is impossible:
 | |
|  *   (a() ? return_array() : return_array()) ? return_array() : return_array()
 | |
|  */
 | |
| 
 | |
| long dynarray_from_heaplist(memuse_list_t *heap)
 | |
| {
 | |
|   memuse_t *use=heap->head;
 | |
|   memuse_t *tmp;
 | |
|   long total=0;
 | |
|   while (use) {
 | |
|     assert(use->type==MEMUSE_STATIC);
 | |
|     total+=use->size;
 | |
|     tmp=use->prev;
 | |
|     free(use);
 | |
|     use=tmp;
 | |
|   }
 | |
|   free(heap);
 | |
|   if (total)
 | |
|     setheap_save(-total*sizeof(cell));
 | |
|   return total;
 | |
| }
 | |
| 
 | |
| static int hier13(value *lval)
 | |
| {
 | |
|   int lvalue=plnge1(hier12,lval);
 | |
|   if (matchtoken('?')) {
 | |
|     int flab1=getlabel();
 | |
|     int flab2=getlabel();
 | |
|     value lval2={0};
 | |
|     int array1,array2;
 | |
|     long total1,total2;
 | |
|     memuse_list_t *heap;
 | |
|     
 | |
|     pushheaplist();
 | |
|     if (lvalue) {
 | |
|       rvalue(lval);
 | |
|     } else if (lval->ident==iCONSTEXPR) {
 | |
|       ldconst(lval->constval,sPRI);
 | |
|       error(lval->constval ? 206 : 205);        /* redundant test */
 | |
|     } /* if */
 | |
|     jmp_eq0(flab1);             /* go to second expression if primary register==0 */
 | |
|     PUSHSTK_I(sc_allowtags);
 | |
|     sc_allowtags=FALSE;         /* do not allow tagnames here (colon is a special token) */
 | |
|     if (hier13(lval))
 | |
|       rvalue(lval);
 | |
|     if (lval->ident==iCONSTEXPR)        /* load constant here */
 | |
|       ldconst(lval->constval,sPRI);
 | |
|     sc_allowtags=(short)POPSTK_I();     /* restore */
 | |
|     heap=popsaveheaplist();
 | |
|     total1=dynarray_from_heaplist(heap);
 | |
|     pushheaplist();
 | |
|     jumplabel(flab2);
 | |
|     setlabel(flab1);
 | |
|     needtoken(':');
 | |
|     if (hier13(&lval2))
 | |
|       rvalue(&lval2);
 | |
|     if (lval2.ident==iCONSTEXPR)        /* load constant here */
 | |
|       ldconst(lval2.constval,sPRI);
 | |
|     array1= (lval->ident==iARRAY || lval->ident==iREFARRAY);
 | |
|     array2= (lval2.ident==iARRAY || lval2.ident==iREFARRAY);
 | |
|     if (array1 && !array2) {
 | |
|       const char *ptr = "-unknown-";
 | |
| 	  if (lval->sym != NULL && lval->sym->name != NULL)
 | |
|         ptr = lval->sym->name;
 | |
|       error(33,ptr);            /* array must be indexed */
 | |
|     } else if (!array1 && array2) {
 | |
|       char *ptr=(lval2.sym->name!=NULL) ? lval2.sym->name : "-unknown-";
 | |
|       error(33,ptr);            /* array must be indexed */
 | |
|     } /* if */
 | |
|     /* ??? if both are arrays, should check dimensions */
 | |
|     if (!matchtag(lval->tag,lval2.tag,FALSE))
 | |
|       error(213);               /* tagname mismatch ('true' and 'false' expressions) */
 | |
|     heap=popsaveheaplist();
 | |
|     total2=dynarray_from_heaplist(heap);
 | |
|     setlabel(flab2);
 | |
|     if ((array1 && array2) && (total1 && total2)) {
 | |
|       markheap(MEMUSE_DYNAMIC, 0);
 | |
|     }
 | |
|     /* If both sides are arrays, we should return the maximal as the lvalue.
 | |
|      * Otherwise we could buffer overflow and the compiler is too stupid.
 | |
|      * Literal strings have a constval == -(num_cells) so the cmp is flipped.
 | |
|      */
 | |
|     if (lval->ident==iARRAY && lval2.ident==iARRAY
 | |
|         && lval->constval < 0
 | |
|         && lval->constval > lval2.constval) {
 | |
|       *lval = lval2;
 | |
|     }
 | |
|     if (lval->ident==iARRAY)
 | |
|       lval->ident=iREFARRAY;    /* iARRAY becomes iREFARRAY */
 | |
|     else if (lval->ident!=iREFARRAY)
 | |
|       lval->ident=iEXPRESSION;  /* iREFARRAY stays iREFARRAY, rest becomes iEXPRESSION */
 | |
|     return FALSE;               /* conditional expression is no lvalue */
 | |
|   } else {
 | |
|     return lvalue;
 | |
|   } /* if */
 | |
| }
 | |
| 
 | |
| /* the order of the operators in these lists is important and must be
 | |
|  * the same as the order of the operators in the array "op1"
 | |
|  */
 | |
| static int list3[]  = {'*','/','%',0};
 | |
| static int list4[]  = {'+','-',0};
 | |
| static int list5[]  = {tSHL,tSHR,tSHRU,0};
 | |
| static int list6[]  = {'&',0};
 | |
| static int list7[]  = {'^',0};
 | |
| static int list8[]  = {'|',0};
 | |
| static int list9[]  = {tlLE,tlGE,'<','>',0};
 | |
| static int list10[] = {tlEQ,tlNE,0};
 | |
| static int list11[] = {tlAND,0};
 | |
| static int list12[] = {tlOR,0};
 | |
| 
 | |
| static int hier12(value *lval)
 | |
| {
 | |
|   return skim(list12,jmp_ne0,1,0,hier11,lval);
 | |
| }
 | |
| 
 | |
| static int hier11(value *lval)
 | |
| {
 | |
|   return skim(list11,jmp_eq0,0,1,hier10,lval);
 | |
| }
 | |
| 
 | |
| static int hier10(value *lval)
 | |
| { /* ==, != */
 | |
|   return plnge(list10,15,hier9,lval,"bool",TRUE);
 | |
| }                  /* ^ this variable is the starting index in the op1[]
 | |
|                     *   array of the operators of this hierarchy level */
 | |
| 
 | |
| static int hier9(value *lval)
 | |
| { /* <=, >=, <, > */
 | |
|   return plnge_rel(list9,11,hier8,lval);
 | |
| }
 | |
| 
 | |
| static int hier8(value *lval)
 | |
| { /* | */
 | |
|   return plnge(list8,10,hier7,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier7(value *lval)
 | |
| { /* ^ */
 | |
|   return plnge(list7,9,hier6,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier6(value *lval)
 | |
| { /* & */
 | |
|   return plnge(list6,8,hier5,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier5(value *lval)
 | |
| { /* <<, >>, >>> */
 | |
|   return plnge(list5,5,hier4,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier4(value *lval)
 | |
| { /* +, - */
 | |
|   return plnge(list4,3,hier3,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier3(value *lval)
 | |
| { /* *, /, % */
 | |
|   return plnge(list3,0,hier2,lval,NULL,FALSE);
 | |
| }
 | |
| 
 | |
| static int hier2(value *lval)
 | |
| {
 | |
|   int lvalue,tok;
 | |
|   int tag,paranthese;
 | |
|   cell val;
 | |
|   char *st;
 | |
|   symbol *sym;
 | |
|   int saveresult;
 | |
| 
 | |
|   tok=lex(&val,&st);
 | |
|   switch (tok) {
 | |
|   case tINC:                    /* ++lval */
 | |
|     if (!hier2(lval))
 | |
|       return error(22);         /* must be lvalue */
 | |
|     assert(lval->sym!=NULL);
 | |
|     if ((lval->sym->usage & uCONST)!=0)
 | |
|       return error(22);         /* assignment to const argument */
 | |
|     if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag))
 | |
|       inc(lval);                /* increase variable first */
 | |
|     rvalue(lval);               /* and read the result into PRI */
 | |
|     sideeffect=TRUE;
 | |
|     return FALSE;               /* result is no longer lvalue */
 | |
|   case tDEC:                    /* --lval */
 | |
|     if (!hier2(lval))
 | |
|       return error(22);         /* must be lvalue */
 | |
|     assert(lval->sym!=NULL);
 | |
|     if ((lval->sym->usage & uCONST)!=0)
 | |
|       return error(22);         /* assignment to const argument */
 | |
|     if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag))
 | |
|       dec(lval);                /* decrease variable first */
 | |
|     rvalue(lval);               /* and read the result into PRI */
 | |
|     sideeffect=TRUE;
 | |
|     return FALSE;               /* result is no longer lvalue */
 | |
|   case '~':                     /* ~ (one's complement) */
 | |
|     if (hier2(lval))
 | |
|       rvalue(lval);
 | |
|     invert();                   /* bitwise NOT */
 | |
|     lval->constval=~lval->constval;
 | |
|     return FALSE;
 | |
|   case '!':                     /* ! (logical negate) */
 | |
|     if (hier2(lval))
 | |
|       rvalue(lval);
 | |
|     if (check_userop(lneg,lval->tag,0,1,NULL,&lval->tag)) {
 | |
|       lval->ident=iEXPRESSION;
 | |
|       lval->constval=0;
 | |
|     } else {
 | |
|       lneg();                   /* 0 -> 1,  !0 -> 0 */
 | |
|       lval->constval=!lval->constval;
 | |
|       lval->tag=pc_addtag("bool");
 | |
|     } /* if */
 | |
|     return FALSE;
 | |
|   case '-':                     /* unary - (two's complement) */
 | |
|     if (hier2(lval))
 | |
|       rvalue(lval);
 | |
|     /* make a special check for a constant expression with the tag of a
 | |
|      * rational number, so that we can simple swap the sign of that constant.
 | |
|      */
 | |
|     if (lval->ident==iCONSTEXPR && lval->tag==sc_rationaltag && sc_rationaltag!=0) {
 | |
|       if (rational_digits==0) {
 | |
|         #if PAWN_CELL_SIZE==32
 | |
|           float *f = (float *)&lval->constval;
 | |
|         #elif PAWN_CELL_SIZE==64
 | |
|           double *f = (double *)&lval->constval;
 | |
|         #else
 | |
|           #error Unsupported cell size
 | |
|         #endif
 | |
|         *f= - *f; /* this modifies lval->constval */
 | |
|       } else {
 | |
|         /* the negation of a fixed point number is just an integer negation */
 | |
|         lval->constval=-lval->constval;
 | |
|       } /* if */
 | |
|     } else if (check_userop(neg,lval->tag,0,1,NULL,&lval->tag)) {
 | |
|       lval->ident=iEXPRESSION;
 | |
|       lval->constval=0;
 | |
|     } else {
 | |
|       neg();                    /* arithmic negation */
 | |
|       lval->constval=-lval->constval;
 | |
|     } /* if */
 | |
|     return FALSE;
 | |
|   case tLABEL:                  /* tagname override */
 | |
|     tag=pc_addtag(st);
 | |
|     lval->cmptag=tag;
 | |
|     lvalue=hier2(lval);
 | |
|     lval->tag=tag;
 | |
|     return lvalue;
 | |
|   case tDEFINED:
 | |
|     paranthese=0;
 | |
|     while (matchtoken('('))
 | |
|       paranthese++;
 | |
|     tok=lex(&val,&st);
 | |
|     if (tok!=tSYMBOL)
 | |
|       return error(20,st);      /* illegal symbol name */
 | |
|     sym=findloc(st);
 | |
|     if (sym==NULL)
 | |
|       sym=findglb(st,sSTATEVAR);
 | |
|     if (sym!=NULL && sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0)
 | |
|       sym=NULL;                 /* symbol is not a function, it is in the table, but not "defined" */
 | |
|     val= (sym!=NULL);
 | |
|     if (!val && find_subst(st,strlen(st))!=NULL)
 | |
|       val=1;
 | |
|     clear_value(lval);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->constval= val;
 | |
|     lval->tag=pc_addtag("bool");
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     while (paranthese--)
 | |
|       needtoken(')');
 | |
|     return FALSE;
 | |
|   case tSIZEOF:
 | |
|     paranthese=0;
 | |
|     while (matchtoken('('))
 | |
|       paranthese++;
 | |
|     tok=lex(&val,&st);
 | |
|     if (tok!=tSYMBOL)
 | |
|       return error(20,st);      /* illegal symbol name */
 | |
|     sym=findloc(st);
 | |
|     if (sym==NULL)
 | |
|       sym=findglb(st,sSTATEVAR);
 | |
|     if (sym==NULL)
 | |
|       return error(17,st);      /* undefined symbol */
 | |
|     if (sym->ident==iCONSTEXPR)
 | |
|       error(39);                /* constant symbol has no size */
 | |
|     else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC)
 | |
|       error(72);                /* "function" symbol has no size */
 | |
|     else if ((sym->usage & uDEFINE)==0)
 | |
|       return error(17,st);      /* undefined symbol (symbol is in the table, but it is "used" only) */
 | |
|     clear_value(lval);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->constval=1;           /* preset */
 | |
|     if (sym->ident==iARRAY || sym->ident==iREFARRAY) {
 | |
|       int level;
 | |
|       symbol *idxsym=NULL;
 | |
|       symbol *subsym=sym;
 | |
|       for (level=0; matchtoken('['); level++) {
 | |
|         idxsym=NULL;
 | |
|         if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) {
 | |
|           char *idxname;
 | |
|           int cmptag=subsym->x.tags.index;
 | |
|           tokeninfo(&val,&idxname);
 | |
|           if ((idxsym=findconst(idxname,&cmptag))==NULL)
 | |
|             error(80,idxname);  /* unknown symbol, or non-constant */
 | |
|           else if (cmptag>1)
 | |
|             error(91,idxname);  /* ambiguous constant */
 | |
|         } /* if */
 | |
|         needtoken(']');
 | |
|         if (subsym!=NULL)
 | |
|           subsym=finddepend(subsym);
 | |
|       } /* for */
 | |
| 	  if (level>sym->dim.array.level+1) {
 | |
|         error(28,sym->name);  /* invalid subscript */
 | |
|       } else if (level==sym->dim.array.level+1) {
 | |
|         lval->constval=(idxsym!=NULL && idxsym->dim.array.length>0) ? idxsym->dim.array.length : 1;
 | |
|       } else {
 | |
|         lval->constval=array_levelsize(sym,level);
 | |
|       }
 | |
|       if (lval->constval==0 && strchr((char *)lptr,PREPROC_TERM)==NULL)
 | |
|         error(224,st);          /* indeterminate array size in "sizeof" expression */
 | |
|     } /* if */
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     while (paranthese--)
 | |
|       needtoken(')');
 | |
|     return FALSE;
 | |
|   case tCELLSOF:
 | |
|     paranthese=0;
 | |
|     while (matchtoken('('))
 | |
|       paranthese++;
 | |
|     tok=lex(&val,&st);
 | |
|     if (tok!=tSYMBOL)
 | |
|       return error(20,st);      /* illegal symbol name */
 | |
|     sym=findloc(st);
 | |
|     if (sym==NULL)
 | |
|       sym=findglb(st,sSTATEVAR);
 | |
|     if (sym==NULL)
 | |
|       return error(17,st);      /* undefined symbol */
 | |
|     if (sym->ident==iCONSTEXPR)
 | |
|       error(39);                /* constant symbol has no size */
 | |
|     else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC)
 | |
|       error(72);                /* "function" symbol has no size */
 | |
|     else if ((sym->usage & uDEFINE)==0)
 | |
|       return error(17,st);      /* undefined symbol (symbol is in the table, but it is "used" only) */
 | |
|     clear_value(lval);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->constval=1;           /* preset */
 | |
|     if (sym->ident==iARRAY || sym->ident==iREFARRAY) {
 | |
|       int level;
 | |
|       symbol *idxsym=NULL;
 | |
|       symbol *subsym=sym;
 | |
|       for (level=0; matchtoken('['); level++) {
 | |
|         idxsym=NULL;
 | |
|         if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) {
 | |
|           char *idxname;
 | |
|           int cmptag=subsym->x.tags.index;
 | |
|           tokeninfo(&val,&idxname);
 | |
|           if ((idxsym=findconst(idxname,&cmptag))==NULL)
 | |
|             error(80,idxname);  /* unknown symbol, or non-constant */
 | |
|           else if (cmptag>1)
 | |
|             error(91,idxname);  /* ambiguous constant */
 | |
|         } /* if */
 | |
|         needtoken(']');
 | |
|         if (subsym!=NULL)
 | |
|           subsym=finddepend(subsym);
 | |
|       } /* for */
 | |
| 	  if (level>sym->dim.array.level+1) {
 | |
|         error(28,sym->name);  /* invalid subscript */
 | |
|       } else if (level==sym->dim.array.level+1) {
 | |
|         lval->constval= (idxsym!=NULL && idxsym->dim.array.length>0) ? idxsym->dim.array.length : 1;
 | |
|       } else {
 | |
|         lval->constval=array_levelsize(sym,level);
 | |
|       }
 | |
|       if (lval->constval==0 && strchr((char *)lptr,PREPROC_TERM)==NULL)
 | |
|         error(224,st);          /* indeterminate array size in "sizeof" expression */
 | |
|     } /* if */
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     while (paranthese--)
 | |
|       needtoken(')');
 | |
|     return FALSE;
 | |
|   case tTAGOF:
 | |
|     paranthese=0;
 | |
|     while (matchtoken('('))
 | |
|       paranthese++;
 | |
|     tok=lex(&val,&st);
 | |
|     if (tok!=tSYMBOL && tok!=tLABEL)
 | |
|       return error(20,st);      /* illegal symbol name */
 | |
|     if (tok==tLABEL) {
 | |
|       constvalue *tagsym=find_constval(&tagname_tab,st,0);
 | |
|       tag=(int)((tagsym!=NULL) ? tagsym->value : 0);
 | |
|     } else {
 | |
|       sym=findloc(st);
 | |
|       if (sym==NULL)
 | |
|         sym=findglb(st,sSTATEVAR);
 | |
|       if (sym==NULL)
 | |
|         return error(17,st);      /* undefined symbol */
 | |
|       if ((sym->usage & uDEFINE)==0)
 | |
|         return error(17,st);      /* undefined symbol (symbol is in the table, but it is "used" only) */
 | |
|       tag=sym->tag;
 | |
|     } /* if */
 | |
|     if (sym->ident==iARRAY || sym->ident==iREFARRAY) {
 | |
|       int level;
 | |
|       symbol *idxsym=NULL;
 | |
|       symbol *subsym=sym;
 | |
|       for (level=0; matchtoken('['); level++) {
 | |
|         idxsym=NULL;
 | |
|         if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) {
 | |
|           char *idxname;
 | |
|           int cmptag=subsym->x.tags.index;
 | |
|           tokeninfo(&val,&idxname);
 | |
|           if ((idxsym=findconst(idxname,&cmptag))==NULL)
 | |
|             error(80,idxname);  /* unknown symbol, or non-constant */
 | |
|           else if (cmptag>1)
 | |
|             error(91,idxname);  /* ambiguous constant */
 | |
|         } /* if */
 | |
|         needtoken(']');
 | |
|         if (subsym!=NULL)
 | |
|           subsym=finddepend(subsym);
 | |
|       } /* for */
 | |
|       if (level>sym->dim.array.level+1)
 | |
|         error(28,sym->name);  /* invalid subscript */
 | |
|       else if (level==sym->dim.array.level+1 && idxsym!=NULL)
 | |
|         tag= idxsym->x.tags.index;
 | |
|     } /* if */
 | |
|     exporttag(tag);
 | |
|     clear_value(lval);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->constval=tag | PUBLICTAG;
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     while (paranthese--)
 | |
|       needtoken(')');
 | |
|     return FALSE;
 | |
|   case tSTATE: {
 | |
|     constvalue *automaton;
 | |
|     constvalue *state;
 | |
|     if (sc_getstateid(&automaton,&state)) {
 | |
|       assert(automaton!=NULL);
 | |
|       assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
 | |
|       loadreg(automaton->value,sALT);
 | |
|       assert(state!=NULL);
 | |
|       ldconst(state->value,sPRI);
 | |
|       ob_eq();
 | |
|       clear_value(lval);
 | |
|       lval->ident=iEXPRESSION;
 | |
|       lval->tag=pc_addtag("bool");
 | |
|     } /* if */
 | |
|     return FALSE;
 | |
|   } /* case */
 | |
|   default:
 | |
|     lexpush();
 | |
|     lvalue=hier1(lval);
 | |
|     /* check for postfix operators */
 | |
|     if (matchtoken(';')) {
 | |
|       /* Found a ';', do not look further for postfix operators */
 | |
|       lexpush();                /* push ';' back after successful match */
 | |
|       return lvalue;
 | |
|     } else if (matchtoken(tTERM)) {
 | |
|       /* Found a newline that ends a statement (this is the case when
 | |
|        * semicolons are optional). Note that an explicit semicolon was
 | |
|        * handled above. This case is similar, except that the token must
 | |
|        * not be pushed back.
 | |
|        */
 | |
|       return lvalue;
 | |
|     } else {
 | |
|       tok=lex(&val,&st);
 | |
|       switch (tok) {
 | |
|       case tINC:                /* lval++ */
 | |
|         if (!lvalue)
 | |
|           return error(22);     /* must be lvalue */
 | |
|         assert(lval->sym!=NULL);
 | |
|         if ((lval->sym->usage & uCONST)!=0)
 | |
|           return error(22);     /* assignment to const argument */
 | |
|         /* on incrementing array cells, the address in PRI must be saved for
 | |
|          * incremening the value, whereas the current value must be in PRI
 | |
|          * on exit.
 | |
|          */
 | |
|         saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR);
 | |
|         if (saveresult)
 | |
|           pushreg(sPRI);        /* save address in PRI */
 | |
|         rvalue(lval);           /* read current value into PRI */
 | |
|         if (saveresult)
 | |
|           swap1();              /* save PRI on the stack, restore address in PRI */
 | |
|         if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag))
 | |
|           inc(lval);            /* increase variable afterwards */
 | |
|         if (saveresult)
 | |
|           popreg(sPRI);         /* restore PRI (result of rvalue()) */
 | |
|         sideeffect=TRUE;
 | |
|         return FALSE;           /* result is no longer lvalue */
 | |
|       case tDEC:                /* lval-- */
 | |
|         if (!lvalue)
 | |
|           return error(22);     /* must be lvalue */
 | |
|         assert(lval->sym!=NULL);
 | |
|         if ((lval->sym->usage & uCONST)!=0)
 | |
|           return error(22);     /* assignment to const argument */
 | |
|         saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR);
 | |
|         if (saveresult)
 | |
|           pushreg(sPRI);        /* save address in PRI */
 | |
|         rvalue(lval);           /* read current value into PRI */
 | |
|         if (saveresult)
 | |
|           swap1();              /* save PRI on the stack, restore address in PRI */
 | |
|         if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag))
 | |
|           dec(lval);            /* decrease variable afterwards */
 | |
|         if (saveresult)
 | |
|           popreg(sPRI);         /* restore PRI (result of rvalue()) */
 | |
|         sideeffect=TRUE;
 | |
|         return FALSE;
 | |
| /* This is temporarily disabled because we detect it automatically.
 | |
|  * Thus, it could be weird if both were used at once
 | |
|  */
 | |
| #if 0
 | |
|       case tCHAR:               /* char (compute required # of cells */
 | |
|         if (lval->ident==iCONSTEXPR) {
 | |
|           lval->constval *= sCHARBITS/8;  /* from char to bytes */
 | |
|           lval->constval = (lval->constval + sizeof(cell)-1) / sizeof(cell);
 | |
|         } else {
 | |
|           if (lvalue)
 | |
|             rvalue(lval);       /* fetch value if not already in PRI */
 | |
|           char2addr();          /* from characters to bytes */
 | |
|           addconst(sizeof(cell)-1);     /* make sure the value is rounded up */
 | |
|           addr2cell();          /* truncate to number of cells */
 | |
|         } /* if */
 | |
|         return FALSE;
 | |
| #endif
 | |
|       default:
 | |
|         lexpush();
 | |
|         return lvalue;
 | |
|       } /* switch */
 | |
|     } /* if */
 | |
|   } /* switch */
 | |
| }
 | |
| 
 | |
| /*  hier1
 | |
|  *
 | |
|  *  The highest hierarchy level: it looks for pointer and array indices
 | |
|  *  and function calls.
 | |
|  *  Generates code to fetch a pointer value if it is indexed and code to
 | |
|  *  add to the pointer value or the array address (the address is already
 | |
|  *  read at primary()). It also generates code to fetch a function address
 | |
|  *  if that hasn't already been done at primary() (check lval[4]) and calls
 | |
|  *  callfunction() to call the function.
 | |
|  */
 | |
| static int hier1(value *lval1)
 | |
| {
 | |
|   int lvalue,index,tok,symtok;
 | |
|   cell val,cidx;
 | |
|   value lval2={0};
 | |
|   char *st;
 | |
|   char close;
 | |
|   symbol *sym;
 | |
|   int magic_string=0;
 | |
|   symbol dummysymbol,*cursym;   /* for changing the index tags in case of enumerated pseudo-arrays */
 | |
| 
 | |
|   lvalue=primary(lval1);
 | |
|   symtok=tokeninfo(&val,&st);   /* get token read by primary() */
 | |
|   cursym=lval1->sym;
 | |
| restart:
 | |
|   sym=cursym;
 | |
|   if (matchtoken('[') || matchtoken('{') || matchtoken('(')) {
 | |
|     tok=tokeninfo(&val,&st);    /* get token read by matchtoken() */
 | |
|     magic_string = (sym && (sym->tag == pc_tag_string && sym->dim.array.level == 0));
 | |
|     if (sym==NULL && symtok!=tSYMBOL) {
 | |
|       /* we do not have a valid symbol and we appear not to have read a valid
 | |
|        * symbol name (so it is unlikely that we would have read a name of an
 | |
|        * undefined symbol) */
 | |
|       error(29);                /* expression error, assumed 0 */
 | |
|       lexpush();                /* analyse '(', '{' or '[' again later */
 | |
|       return FALSE;
 | |
|     } /* if */
 | |
|     if (tok=='[' || tok=='{') { /* subscript */
 | |
|       close = (char)((tok=='[') ? ']' : '}');
 | |
|       if (sym==NULL) {  /* sym==NULL if lval is a constant or a literal */
 | |
|         error(28,"<no variable>");  /* cannot subscript */
 | |
|         needtoken(close);
 | |
|         return FALSE;
 | |
|       } else if (sym->ident!=iARRAY && sym->ident!=iREFARRAY){
 | |
|         error(28,sym->name);    /* cannot subscript, variable is not an array */
 | |
|         needtoken(close);
 | |
|         return FALSE;
 | |
|       } else if (sym->dim.array.level>0 && close!=']') {
 | |
|         error(51);      /* invalid subscript, must use [ ] */
 | |
|         needtoken(close);
 | |
|         return FALSE;
 | |
|       } /* if */
 | |
|       /* set the tag to match (enumeration fields as indices) */
 | |
|       lval2.cmptag=sym->x.tags.index;
 | |
|       stgget(&index,&cidx);     /* mark position in code generator */
 | |
|       pushreg(sPRI);            /* save base address of the array */
 | |
|       if (hier14(&lval2))       /* create expression for the array index */
 | |
|         rvalue(&lval2);
 | |
|       if (lval2.ident==iARRAY || lval2.ident==iREFARRAY)
 | |
|         error(33,lval2.sym->name);      /* array must be indexed */
 | |
|       needtoken(close);
 | |
|       if ((sym->usage & uENUMROOT) && !matchtag(sym->x.tags.index,lval2.tag,TRUE))
 | |
|         error(213);
 | |
|       if (lval2.ident==iCONSTEXPR) {    /* constant expression */
 | |
|         stgdel(index,cidx);             /* scratch generated code */
 | |
|         if (lval1->arrayidx!=NULL) {    /* keep constant index, for checking */
 | |
|           assert(sym->dim.array.level>=0 && sym->dim.array.level<sDIMEN_MAX);
 | |
|           lval1->arrayidx[sym->dim.array.level]=lval2.constval;
 | |
|         } /* if */
 | |
|         if (close==']' && !(sym->tag == pc_tag_string && sym->dim.array.level == 0)) {
 | |
|           /* normal array index */
 | |
|           if (lval2.constval<0 || sym->dim.array.length!=0 && sym->dim.array.length<=lval2.constval)
 | |
|             error(32,sym->name);        /* array index out of bounds */
 | |
|           if (lval2.constval!=0) {
 | |
|             /* don't add offsets for zero subscripts */
 | |
|             #if PAWN_CELL_SIZE==16
 | |
|               ldconst(lval2.constval<<1,sALT);
 | |
|             #elif PAWN_CELL_SIZE==32
 | |
|               ldconst(lval2.constval<<2,sALT);
 | |
|             #elif PAWN_CELL_SIZE==64
 | |
|               ldconst(lval2.constval<<3,sALT);
 | |
|             #else
 | |
|               #error Unsupported cell size
 | |
|             #endif
 | |
|             ob_add();
 | |
|           } /* if */
 | |
|         } else {
 | |
|           /* character index */
 | |
|           if (lval2.constval<0 || sym->dim.array.length!=0
 | |
|               && sym->dim.array.length*((8*sizeof(cell))/sCHARBITS)<=(ucell)lval2.constval)
 | |
|             error(32,sym->name);        /* array index out of bounds */
 | |
|           if (lval2.constval!=0) {
 | |
|             /* don't add offsets for zero subscripts */
 | |
|             #if sCHARBITS==16
 | |
|               ldconst(lval2.constval<<1,sALT);/* 16-bit character */
 | |
|             #else
 | |
|               ldconst(lval2.constval,sALT);   /* 8-bit character */
 | |
|             #endif
 | |
|             ob_add();
 | |
|           } /* if */
 | |
|           charalign();                  /* align character index into array */
 | |
|         } /* if */
 | |
|         /* if the array index is a field from an enumeration, get the tag name
 | |
|          * from the field and save the size of the field too.
 | |
|          */
 | |
|         assert(lval2.sym==NULL || lval2.sym->dim.array.level==0);
 | |
|         if (lval2.sym!=NULL && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) {
 | |
|           lval1->tag=lval2.sym->x.tags.index;
 | |
|           lval1->constval=lval2.sym->dim.array.length;
 | |
|         } /* if */
 | |
|       } else {
 | |
|         /* array index is not constant */
 | |
|         lval1->arrayidx=NULL;           /* reset, so won't be checked */
 | |
|         if (close==']' && !magic_string) {
 | |
|           if (sym->dim.array.length!=0)
 | |
|             ffbounds(sym->dim.array.length-1);  /* run time check for array bounds */
 | |
|           cell2addr();  /* normal array index */
 | |
|         } else {
 | |
|           if (sym->dim.array.length!=0)
 | |
|             ffbounds(sym->dim.array.length*(32/sCHARBITS)-1);
 | |
|           char2addr();  /* character array index */
 | |
|         } /* if */
 | |
|         popreg(sALT);
 | |
|         ob_add();       /* base address was popped into secondary register */
 | |
|         if (close!=']' || magic_string)
 | |
|           charalign();  /* align character index into array */
 | |
|       } /* if */
 | |
|       /* the indexed item may be another array (multi-dimensional arrays) */
 | |
|       assert(cursym==sym && sym!=NULL); /* should still be set */
 | |
|       if (sym->dim.array.level>0) {
 | |
|         assert(close==']');     /* checked earlier */
 | |
|         assert(cursym==lval1->sym);
 | |
|         /* read the offset to the subarray and add it to the current address */
 | |
|         lval1->ident=iARRAYCELL;
 | |
|         pushreg(sPRI);          /* the optimizer makes this to a MOVE.alt */
 | |
|         rvalue(lval1);
 | |
|         popreg(sALT);
 | |
|         ob_add();
 | |
|         /* adjust the "value" structure and find the referenced array */
 | |
|         lval1->ident=iREFARRAY;
 | |
|         lval1->sym=finddepend(sym);
 | |
|         assert(lval1->sym!=NULL);
 | |
|         assert(lval1->sym->dim.array.level==sym->dim.array.level-1);
 | |
|         cursym=lval1->sym;
 | |
|         /* try to parse subsequent array indices */
 | |
|         lvalue=FALSE;   /* for now, a iREFARRAY is no lvalue */
 | |
|         goto restart;
 | |
|       } /* if */
 | |
|       assert(sym->dim.array.level==0);
 | |
|       /* set type to fetch... INDIRECTLY */
 | |
|       if (sym->tag == pc_tag_string) {
 | |
|         lval1->ident = iARRAYCHAR;
 | |
|       } else {
 | |
|         lval1->ident= (char)((close==']') ? iARRAYCELL : iARRAYCHAR);
 | |
|       }
 | |
|       /* if the array index is a field from an enumeration, get the tag name
 | |
|        * from the field and save the size of the field too. Otherwise, the
 | |
|        * tag is the one from the array symbol.
 | |
|        */
 | |
|       if (lval2.ident==iCONSTEXPR && lval2.sym!=NULL
 | |
|           && lval2.sym->dim.array.length>0 && sym->dim.array.level==0)
 | |
|       {
 | |
|         lval1->tag=lval2.sym->x.tags.index;
 | |
|         lval1->constval=lval2.sym->dim.array.length;
 | |
|         if (lval2.tag==sym->x.tags.index && lval1->constval>1 && matchtoken('[')) {
 | |
|           /* an array indexed with an enumeration field may be considered a sub-array */
 | |
|           lexpush();
 | |
|           lvalue=FALSE;   /* for now, a iREFARRAY is no lvalue */
 | |
|           lval1->ident=iREFARRAY;
 | |
|           /* initialize a dummy symbol, which is a copy of the current symbol,
 | |
|            * but with an adjusted index tag
 | |
|            */
 | |
|           assert(sym!=NULL);
 | |
|           dummysymbol=*sym;
 | |
|           /* get the tag of the root of the enumeration */
 | |
|           assert(lval2.sym!=NULL);
 | |
|           dummysymbol.x.tags.index=lval2.sym->x.tags.field;
 | |
|           dummysymbol.dim.array.length=lval2.sym->dim.array.length;
 | |
|           cursym=&dummysymbol;
 | |
|           /* recurse */
 | |
|           goto restart;
 | |
|         } /* if */
 | |
|       } else {
 | |
|         assert(sym!=NULL);
 | |
|         if (cursym!=&dummysymbol)
 | |
|           lval1->tag=sym->tag;
 | |
|         lval1->constval=0;
 | |
|       } /* if */
 | |
|       /* a cell in an array is an lvalue, a character in an array is not
 | |
|        * always a *valid* lvalue */
 | |
|       return TRUE;
 | |
|     } else {            /* tok=='(' -> function(...) */
 | |
|       assert(tok=='(');
 | |
|       if (sym==NULL
 | |
|           || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC))
 | |
|       {
 | |
|         if (sym==NULL && sc_status==statFIRST) {
 | |
|           /* could be a "use before declaration"; in that case, create a stub
 | |
|            * function so that the usage can be marked.
 | |
|            */
 | |
|           sym=fetchfunc(lastsymbol,0);
 | |
|           if (sym==NULL)
 | |
|             error(123); /* insufficient memory */
 | |
|           markusage(sym,uREAD);
 | |
|         } else {
 | |
|           return error(12);           /* invalid function call */
 | |
|         } /* if */
 | |
|       } else if ((sym->usage & uMISSING)!=0) {
 | |
|         char symname[2*sNAMEMAX+16];  /* allow space for user defined operators */
 | |
|         funcdisplayname(symname,sym->name);
 | |
|         error(4,symname);             /* function not defined */
 | |
|       } /* if */
 | |
|       callfunction(sym,lval1,TRUE);
 | |
|       return FALSE;             /* result of function call is no lvalue */
 | |
|     } /* if */
 | |
|   } /* if */
 | |
|   if (sym!=NULL && lval1->ident==iFUNCTN) {
 | |
|     assert(sym->ident==iFUNCTN);
 | |
|     if (sc_allowproccall) {
 | |
|       callfunction(sym,lval1,FALSE);
 | |
|     } else if ((sym->usage & uNATIVE) != uNATIVE) {
 | |
|       symbol *oldsym=sym;
 | |
|       int n=-1,iter=0;
 | |
|       int usage = ((sym->usage & uPUBLIC) == uPUBLIC) ? uPUBLIC : 0;
 | |
|       cell code_addr=0;
 | |
|       for (sym=glbtab.next; sym!=NULL; sym=sym->next) {
 | |
|         if (sym->ident==iFUNCTN && sym->vclass == sGLOBAL && (!usage || (sym->usage & usage)))
 | |
|         {
 | |
|           if (strcmp(sym->name, lval1->sym->name)==0) {
 | |
|             n = iter;
 | |
|             code_addr = sym->codeaddr;
 | |
|             break;
 | |
|           }
 | |
|           iter++;
 | |
|         }
 | |
|       }
 | |
|       if (n!=-1) {
 | |
|         char faketag[sNAMEMAX+1];
 | |
|         lval1->sym=NULL;
 | |
|         lval1->ident=iCONSTEXPR;
 | |
|         /* Generate a quick pseudo-tag! */
 | |
|         if (usage == uPUBLIC) {
 | |
|           lval1->constval=(n<<1)|1;
 | |
|           snprintf(faketag, sizeof(faketag)-1, "$Func@%d", n);
 | |
|         } else {
 | |
|           lval1->constval=(code_addr<<1)|0;
 | |
|           snprintf(faketag, sizeof(faketag)-1, "$Func!%d", code_addr);
 | |
|         }
 | |
|         lval1->tag=pc_addfunctag(faketag);
 | |
|         oldsym->usage |= uREAD;
 | |
| 		sym->usage |= uREAD;
 | |
|       } else {
 | |
|         error(76);                /* invalid function call, or syntax error */
 | |
|       } /* if */
 | |
|       return FALSE;
 | |
| 	} else {
 | |
| 	  error(76);                  /* invalid function call, or syntax error */
 | |
| 	}
 | |
|   } /* if */
 | |
|   return lvalue;
 | |
| }
 | |
| 
 | |
| /*  primary
 | |
|  *
 | |
|  *  Returns 1 if the operand is an lvalue (everything except arrays, functions
 | |
|  *  constants and -of course- errors).
 | |
|  *  Generates code to fetch the address of arrays. Code for constants is
 | |
|  *  already generated by constant().
 | |
|  *  This routine first clears the entire lval array (all fields are set to 0).
 | |
|  *
 | |
|  *  Global references: sc_intest  (may be altered, but restored upon termination)
 | |
|  */
 | |
| static int primary(value *lval)
 | |
| {
 | |
|   char *st;
 | |
|   int lvalue,tok;
 | |
|   cell val;
 | |
|   symbol *sym;
 | |
| 
 | |
|   if (matchtoken('(')){         /* sub-expression - (expression,...) */
 | |
|     PUSHSTK_I(sc_intest);
 | |
|     PUSHSTK_I(sc_allowtags);
 | |
| 
 | |
|     sc_intest=FALSE;            /* no longer in "test" expression */
 | |
|     sc_allowtags=TRUE;          /* allow tagnames to be used in parenthesized expressions */
 | |
|     sc_allowproccall=FALSE;
 | |
|     do
 | |
|       lvalue=hier14(lval);
 | |
|     while (matchtoken(','));
 | |
|     needtoken(')');
 | |
|     lexclr(FALSE);              /* clear lex() push-back, it should have been
 | |
|                                  * cleared already by needtoken() */
 | |
|     sc_allowtags=(short)POPSTK_I();
 | |
|     sc_intest=(short)POPSTK_I();
 | |
|     return lvalue;
 | |
|   } /* if */
 | |
| 
 | |
|   clear_value(lval);    /* clear lval */
 | |
|   tok=lex(&val,&st);
 | |
|   if (tok==tSYMBOL) {
 | |
|     /* lastsymbol is char[sNAMEMAX+1], lex() should have truncated any symbol
 | |
|      * to sNAMEMAX significant characters */
 | |
|     assert(strlen(st)<sizeof lastsymbol);
 | |
|     strcpy(lastsymbol,st);
 | |
|   } /* if */
 | |
|   if (tok==tSYMBOL && !findconst(st,NULL)) {
 | |
|     /* first look for a local variable */
 | |
|     if ((sym=findloc(st))!=0) {
 | |
|       if (sym->ident==iLABEL) {
 | |
|         error(29);          /* expression error, assumed 0 */
 | |
|         ldconst(0,sPRI);    /* load 0 */
 | |
|         return FALSE;       /* return 0 for labels (expression error) */
 | |
|       } /* if */
 | |
|       lval->sym=sym;
 | |
|       lval->ident=sym->ident;
 | |
|       lval->tag=sym->tag;
 | |
|       if (sym->ident==iARRAY || sym->ident==iREFARRAY) {
 | |
|         address(sym,sPRI);  /* get starting address in primary register */
 | |
|         return FALSE;       /* return 0 for array (not lvalue) */
 | |
|       } else {
 | |
|         return TRUE;        /* return 1 if lvalue (not label or array) */
 | |
|       } /* if */
 | |
|     } /* if */
 | |
|     /* now try a global variable */
 | |
|     if ((sym=findglb(st,sSTATEVAR))!=0) {
 | |
|       if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) {
 | |
|         /* if the function is only in the table because it was inserted as a
 | |
|          * stub in the first pass (i.e. it was "used" but never declared or
 | |
|          * implemented, issue an error
 | |
|          */
 | |
|         if ((sym->usage & uPROTOTYPED)==0)
 | |
|           error(17,st);
 | |
|       } else {
 | |
|         if ((sym->usage & uDEFINE)==0)
 | |
|           error(17,st);
 | |
|         lval->sym=sym;
 | |
|         lval->ident=sym->ident;
 | |
|         lval->tag=sym->tag;
 | |
|         if (sym->ident==iARRAY || sym->ident==iREFARRAY) {
 | |
|           address(sym,sPRI);    /* get starting address in primary register */
 | |
|           return FALSE;         /* return 0 for array (not lvalue) */
 | |
|         } else {
 | |
|           return TRUE;          /* return 1 if lvalue (not function or array) */
 | |
|         } /* if */
 | |
|       } /* if */
 | |
|     } else {
 | |
|       if (!sc_allowproccall)
 | |
|         return error(17,st);    /* undefined symbol */
 | |
|       /* an unknown symbol, but used in a way compatible with the "procedure
 | |
|        * call" syntax. So assume that the symbol refers to a function.
 | |
|        */
 | |
|       assert(sc_status==statFIRST);
 | |
|       sym=fetchfunc(st,0);
 | |
|       if (sym==NULL)
 | |
|         error(123);     /* insufficient memory */
 | |
|     } /* if */
 | |
|     assert(sym!=NULL);
 | |
|     assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC);
 | |
|     lval->sym=sym;
 | |
|     lval->ident=sym->ident;
 | |
|     lval->tag=sym->tag;
 | |
|     return FALSE;       /* return 0 for function (not an lvalue) */
 | |
|   } /* if */
 | |
|   lexpush();            /* push the token, it is analyzed by constant() */
 | |
|   if (constant(lval)==0) {
 | |
|     error(29);          /* expression error, assumed 0 */
 | |
|     ldconst(0,sPRI);    /* load 0 */
 | |
|   } /* if */
 | |
|   return FALSE;         /* return 0 for constants (or errors) */
 | |
| }
 | |
| 
 | |
| static void clear_value(value *lval)
 | |
| {
 | |
|   lval->sym=NULL;
 | |
|   lval->constval=0L;
 | |
|   lval->tag=0;
 | |
|   lval->ident=0;
 | |
|   lval->boolresult=FALSE;
 | |
|   /* do not clear lval->arrayidx, it is preset in hier14() */
 | |
|   /* do not clear lval->cmptag */
 | |
| }
 | |
| 
 | |
| static void setdefarray(cell *string,cell size,cell array_sz,cell *dataaddr,int fconst)
 | |
| {
 | |
|   /* The routine must copy the default array data onto the heap, as to avoid
 | |
|    * that a function can change the default value. An optimization is that
 | |
|    * the default array data is "dumped" into the data segment only once (on the
 | |
|    * first use).
 | |
|    */
 | |
|   /* check whether to dump the default array */
 | |
|   assert(dataaddr!=NULL);
 | |
|   if (sc_status==statWRITE && *dataaddr<0) {
 | |
|     int i;
 | |
|     *dataaddr=(litidx+glb_declared)*sizeof(cell);
 | |
|     for (i=0; i<size; i++)
 | |
|       litadd(*string++);
 | |
|   } /* if */
 | |
| 
 | |
|   /* if the function is known not to modify the array (meaning that it also
 | |
|    * does not modify the default value), directly pass the address of the
 | |
|    * array in the data segment.
 | |
|    */
 | |
|   if (fconst || !string) {
 | |
|     ldconst(*dataaddr,sPRI);
 | |
|   } else {
 | |
|     /* Generate the code:
 | |
|      *  CONST.pri dataaddr                ;address of the default array data
 | |
|      *  HEAP      array_sz*sizeof(cell)   ;heap address in ALT
 | |
|      *  MOVS      size*sizeof(cell)       ;copy data from PRI to ALT
 | |
|      *  MOVE.PRI                          ;PRI = address on the heap
 | |
|      */
 | |
|     ldconst(*dataaddr,sPRI);
 | |
|     /* "array_sz" is the size of the argument (the value between the brackets
 | |
|      * in the declaration), "size" is the size of the default array data.
 | |
|      */
 | |
|     assert(array_sz>=size);
 | |
|     modheap((int)array_sz*sizeof(cell));
 | |
|     markheap(MEMUSE_STATIC, array_sz);
 | |
|     /* ??? should perhaps fill with zeros first */
 | |
|     memcopy(size*sizeof(cell));
 | |
|     moveto1();
 | |
|   } /* if */
 | |
| }
 | |
| 
 | |
| static int findnamedarg(arginfo *arg,char *name)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i=0; arg[i].ident!=0 && arg[i].ident!=iVARARGS; i++)
 | |
|     if (strcmp(arg[i].name,name)==0)
 | |
|       return i;
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| int checktag(int tags[],int numtags,int exprtag)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   assert(tags!=0);
 | |
|   assert(numtags>0);
 | |
|   for (i=0; i<numtags; i++)
 | |
|     if (matchtag(tags[i],exprtag,TRUE))
 | |
|       return TRUE;    /* matching tag */
 | |
|   return FALSE;       /* no tag matched */
 | |
| }
 | |
| 
 | |
| enum {
 | |
|   ARG_UNHANDLED,
 | |
|   ARG_IGNORED,
 | |
|   ARG_DONE,
 | |
| };
 | |
| 
 | |
| /*  callfunction
 | |
|  *
 | |
|  *  Generates code to call a function. This routine handles default arguments
 | |
|  *  and positional as well as named parameters.
 | |
|  */
 | |
| static void callfunction(symbol *sym,value *lval_result,int matchparanthesis)
 | |
| {
 | |
| static long nest_stkusage=0L;
 | |
| static int nesting=0;
 | |
|   int close,lvalue;
 | |
|   int argpos;       /* index in the output stream (argpos==nargs if positional parameters) */
 | |
|   int argidx=0;     /* index in "arginfo" list */
 | |
|   int nargs=0;      /* number of arguments */
 | |
|   int heapalloc=0;
 | |
|   int namedparams=FALSE;
 | |
|   value lval = {0};
 | |
|   arginfo *arg;
 | |
|   char arglist[sMAXARGS];
 | |
|   constvalue arrayszlst = { NULL, "", 0, 0};/* array size list starts empty */
 | |
|   constvalue taglst = { NULL, "", 0, 0};    /* tag list starts empty */
 | |
|   symbol *symret;
 | |
|   cell lexval;
 | |
|   char *lexstr;
 | |
| 
 | |
|   assert(sym!=NULL);
 | |
|   lval_result->ident=iEXPRESSION; /* preset, may be changed later */
 | |
|   lval_result->constval=0;
 | |
|   lval_result->tag=sym->tag;
 | |
|   /* check whether this is a function that returns an array */
 | |
|   symret=finddepend(sym);
 | |
|   assert(symret==NULL || symret->ident==iREFARRAY);
 | |
|   if (symret!=NULL) {
 | |
|     int retsize;
 | |
|     /* allocate space on the heap for the array, and pass the pointer to the
 | |
|      * reserved memory block as a hidden parameter
 | |
|      */
 | |
|     retsize=(int)array_totalsize(symret);
 | |
|     assert(retsize>0);
 | |
|     modheap(retsize*sizeof(cell));/* address is in ALT */
 | |
|     pushreg(sALT);                /* pass ALT as the last (hidden) parameter */
 | |
|     markheap(MEMUSE_STATIC, retsize);
 | |
|     /* also mark the ident of the result as "array" */
 | |
|     lval_result->ident=iREFARRAY;
 | |
|     lval_result->sym=symret;
 | |
|   } /* if */
 | |
|   pushheaplist();
 | |
| 
 | |
|   nesting++;
 | |
|   assert(nest_stkusage>=0);
 | |
|   #if !defined NDEBUG
 | |
|     if (nesting==1)
 | |
|       assert(nest_stkusage==0);
 | |
|   #endif
 | |
|   sc_allowproccall=FALSE;       /* parameters may not use procedure call syntax */
 | |
| 
 | |
|   if ((sym->flags & flgDEPRECATED)!=0) {
 | |
|     char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
 | |
|     error(234,sym->name,ptr);   /* deprecated (probably a native function) */
 | |
|   } /* if */
 | |
| 
 | |
|   /* run through the arguments */
 | |
|   arg=sym->dim.arglist;
 | |
|   assert(arg!=NULL);
 | |
|   stgmark(sSTARTREORDER);
 | |
|   memset(arglist,ARG_UNHANDLED,sizeof arglist);
 | |
|   if (matchparanthesis) {
 | |
|     /* Opening brace was already parsed, if closing brace follows, this
 | |
|      * call passes no parameters.
 | |
|      */
 | |
|     close=matchtoken(')');
 | |
|   } else {
 | |
|     /* When we find an end of line here, it may be a function call passing
 | |
|      * no parameters, or it may be that the first parameter is on a line
 | |
|      * below. But as a parameter can be anything, this is difficult to check.
 | |
|      * The only simple check that we have is the use of "named parameters".
 | |
|      */
 | |
|     close=matchtoken(tTERM);
 | |
|     if (close) {
 | |
|       close=!matchtoken('.');
 | |
|       if (!close)
 | |
|         lexpush();                /* reset the '.' */
 | |
|     } /* if */
 | |
|   } /* if */
 | |
|   if (!close) {
 | |
|     do {
 | |
|       if (matchtoken('.')) {
 | |
|         namedparams=TRUE;
 | |
|         if (needtoken(tSYMBOL))
 | |
|           tokeninfo(&lexval,&lexstr);
 | |
|         else
 | |
|           lexstr="";
 | |
|         argpos=findnamedarg(arg,lexstr);
 | |
|         if (argpos<0) {
 | |
|           error(17,lexstr);       /* undefined symbol */
 | |
|           break;                  /* exit loop, argpos is invalid */
 | |
|         } /* if */
 | |
|         needtoken('=');
 | |
|         argidx=argpos;
 | |
|       } else {
 | |
|         if (namedparams)
 | |
|           error(44);   /* positional parameters must precede named parameters */
 | |
|         argpos=nargs;
 | |
|       } /* if */
 | |
|       /* the number of arguments this was already checked at the declaration
 | |
|        * of the function; check it again for functions with a variable
 | |
|        * argument list
 | |
|        */
 | |
|       if (argpos>=sMAXARGS)
 | |
|         error(45);                /* too many function arguments */
 | |
|       stgmark((char)(sEXPRSTART+argpos));/* mark beginning of new expression in stage */
 | |
|       if (arglist[argpos]!=ARG_UNHANDLED)
 | |
|         error(58);                /* argument already set */
 | |
|       if (matchtoken('_')) {
 | |
|         arglist[argpos]=ARG_IGNORED;  /* flag argument as "present, but ignored" */
 | |
|         if (arg[argidx].ident==0 || arg[argidx].ident==iVARARGS) {
 | |
|           error(92);             /* argument count mismatch */
 | |
|         } else if (!arg[argidx].hasdefault) {
 | |
|           error(34,nargs+1);      /* argument has no default value */
 | |
|         } /* if */
 | |
|         if (arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS)
 | |
|           argidx++;
 | |
|         /* The rest of the code to handle default values is at the bottom
 | |
|          * of this routine where default values for unspecified parameters
 | |
|          * are (also) handled. Note that above, the argument is flagged as
 | |
|          * ARG_IGNORED.
 | |
|          */
 | |
|       } else {
 | |
|         arglist[argpos]=ARG_DONE; /* flag argument as "present" */
 | |
|         if (arg[argidx].ident!=0 && arg[argidx].numtags==1)     /* set the expected tag, if any */
 | |
|           lval.cmptag=arg[argidx].tags[0];
 | |
|         lvalue=hier14(&lval);
 | |
|         assert(sc_status==statFIRST || arg[argidx].tags!=NULL);
 | |
|         switch (arg[argidx].ident) {
 | |
|         case 0:
 | |
|           error(92);             /* argument count mismatch */
 | |
|           break;
 | |
|         case iVARARGS:
 | |
|           /* always pass by reference */
 | |
|           if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) {
 | |
|             assert(lval.sym!=NULL);
 | |
|             if ((lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) {
 | |
|               /* treat a "const" variable passed to a function with a non-const
 | |
|                * "variable argument list" as a constant here */
 | |
|               if (!lvalue) {
 | |
|                 error(22);        /* need lvalue */
 | |
|               } else {
 | |
|                 rvalue(&lval);    /* get value in PRI */
 | |
|                 setheap_pri();    /* address of the value on the heap in PRI */
 | |
|                 heapalloc+=markheap(MEMUSE_STATIC, 1);
 | |
|                 nest_stkusage++;
 | |
|               } /* if */
 | |
|             } else if (lvalue) {
 | |
|               address(lval.sym,sPRI);
 | |
|             } else {
 | |
|               setheap_pri();      /* address of the value on the heap in PRI */
 | |
|               heapalloc+=markheap(MEMUSE_STATIC, 1);
 | |
|               nest_stkusage++;
 | |
|             } /* if */
 | |
|           } else if (lval.ident==iCONSTEXPR || lval.ident==iEXPRESSION)
 | |
|           {
 | |
|             /* allocate a cell on the heap and store the
 | |
|              * value (already in PRI) there */
 | |
|             setheap_pri();        /* address of the value on the heap in PRI */
 | |
|             heapalloc+=markheap(MEMUSE_STATIC, 1);
 | |
|             nest_stkusage++;
 | |
|           } /* if */
 | |
|           /* ??? handle const array passed by reference */
 | |
|           /* otherwise, the address is already in PRI */
 | |
|           if (lval.sym!=NULL)
 | |
|             markusage(lval.sym,uWRITTEN);
 | |
|           if (!checktags_string(arg[argidx].tags, arg[argidx].numtags, &lval)
 | |
|               && !checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag))
 | |
|             error(213);
 | |
|           break;
 | |
|         case iVARIABLE:
 | |
|           if (lval.ident==iLABEL || lval.ident==iFUNCTN || lval.ident==iREFFUNC
 | |
|               || lval.ident==iARRAY || lval.ident==iREFARRAY)
 | |
|             error(35,argidx+1);   /* argument type mismatch */
 | |
|           if (lvalue)
 | |
|             rvalue(&lval);        /* get value (direct or indirect) */
 | |
|           /* otherwise, the expression result is already in PRI */
 | |
|           assert(arg[argidx].numtags>0);
 | |
|           check_userop(NULL,lval.tag,arg[argidx].tags[0],2,NULL,&lval.tag);
 | |
|           if (!checktags_string(arg[argidx].tags, arg[argidx].numtags, &lval)
 | |
|               && !checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag))
 | |
| 		  {
 | |
| 			if (arg[argidx].numtags == 1 && arg[argidx].tags[0] & FUNCTAG)
 | |
|               error(100);         /* error - function prototypes do not match */
 | |
| 			else
 | |
|               error(213);         /* warning - tag mismatch */
 | |
| 		  }
 | |
|           if (lval.tag!=0)
 | |
|             append_constval(&taglst,arg[argidx].name,lval.tag,0);
 | |
|           argidx++;               /* argument done */
 | |
|           break;
 | |
|         case iREFERENCE:
 | |
|           if (!lvalue)
 | |
|             error(35,argidx+1);   /* argument type mismatch */
 | |
|           if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0)
 | |
|             error(35,argidx+1);   /* argument type mismatch */
 | |
|           if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) {
 | |
|             if (lvalue) {
 | |
|               assert(lval.sym!=NULL);
 | |
|               address(lval.sym,sPRI);
 | |
|             } else {
 | |
|               setheap_pri();      /* address of the value on the heap in PRI */
 | |
|               heapalloc+=markheap(MEMUSE_STATIC, 1);
 | |
|               nest_stkusage++;
 | |
|             } /* if */
 | |
|           } /* if */
 | |
|           /* otherwise, the address is already in PRI */
 | |
|           if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag))
 | |
|             error(213);
 | |
|           if (lval.tag!=0)
 | |
|             append_constval(&taglst,arg[argidx].name,lval.tag,0);
 | |
|           argidx++;               /* argument done */
 | |
|           if (lval.sym!=NULL)
 | |
|             markusage(lval.sym,uWRITTEN);
 | |
|           break;
 | |
|         case iREFARRAY:
 | |
|           if (lval.ident!=iARRAY && lval.ident!=iREFARRAY
 | |
|               && lval.ident!=iARRAYCELL && lval.ident!=iARRAYCHAR)
 | |
|           {
 | |
|             error(35,argidx+1);   /* argument type mismatch */
 | |
|             break;
 | |
|           } /* if */
 | |
|           if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0)
 | |
|             error(35,argidx+1); /* argument type mismatch */
 | |
|           /* Verify that the dimensions match with those in arg[argidx].
 | |
|            * A literal array always has a single dimension.
 | |
|            * An iARRAYCELL parameter is also assumed to have a single dimension.
 | |
|            */
 | |
|           if (lval.sym==NULL || lval.ident==iARRAYCELL || lval.ident==iARRAYCHAR) {
 | |
|             if (arg[argidx].numdim!=1) {
 | |
|               error(48);        /* array dimensions must match */
 | |
|             } else if (arg[argidx].dim[0]!=0) {
 | |
|               assert(arg[argidx].dim[0]>0);
 | |
|               if (lval.ident==iARRAYCELL) {
 | |
|                 error(47);        /* array sizes must match */
 | |
|               } else {
 | |
|                 assert(lval.constval!=0); /* literal array must have a size */
 | |
|                 /* A literal array must have exactly the same size as the
 | |
|                  * function argument; a literal string may be smaller than
 | |
|                  * the function argument.
 | |
|                  */
 | |
|                 if (lval.constval>0 && arg[argidx].dim[0]!=lval.constval
 | |
|                     || lval.constval<0 && arg[argidx].dim[0] < -lval.constval)
 | |
|                   error(47);      /* array sizes must match */
 | |
|               } /* if */
 | |
|             } /* if */
 | |
|             if (lval.ident!=iARRAYCELL && lval.ident!=iARRAYCHAR) {
 | |
|               /* save array size, for default values with uSIZEOF flag */
 | |
|               cell array_sz=lval.constval;
 | |
|               assert(array_sz!=0);/* literal array must have a size */
 | |
|               if (array_sz<0)
 | |
|                 array_sz= -array_sz;
 | |
|               append_constval(&arrayszlst,arg[argidx].name,array_sz,0);
 | |
|             }/* if */
 | |
|           } else {
 | |
|             symbol *sym=lval.sym;
 | |
|             short level=0;
 | |
|             assert(sym!=NULL);
 | |
|             if (sym->dim.array.level+1!=arg[argidx].numdim)
 | |
|               error(48);          /* array dimensions must match */
 | |
|             /* the lengths for all dimensions must match, unless the dimension
 | |
|              * length was defined at zero (which means "undefined")
 | |
|              */
 | |
|             while (sym->dim.array.level>0) {
 | |
|               assert(level<sDIMEN_MAX);
 | |
|               if (arg[argidx].dim[level]!=0 && sym->dim.array.length!=arg[argidx].dim[level])
 | |
|                 error(47);        /* array sizes must match */
 | |
|               else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE))
 | |
|                 error(229,sym->name);   /* index tag mismatch */
 | |
|               append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level);
 | |
|               sym=finddepend(sym);
 | |
|               assert(sym!=NULL);
 | |
|               level++;
 | |
|             } /* if */
 | |
|             /* the last dimension is checked too, again, unless it is zero */
 | |
|             assert(level<sDIMEN_MAX);
 | |
|             assert(sym!=NULL);
 | |
|             if (arg[argidx].dim[level]!=0 && sym->dim.array.length!=arg[argidx].dim[level])
 | |
|               error(47);          /* array sizes must match */
 | |
|             else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE))
 | |
|               error(229,sym->name);   /* index tag mismatch */
 | |
|             append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level);
 | |
|           } /* if */
 | |
|           /* address already in PRI */
 | |
|           if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag))
 | |
|             error(213);
 | |
|           if (lval.tag!=0)
 | |
|             append_constval(&taglst,arg[argidx].name,lval.tag,0);
 | |
|           // ??? set uWRITTEN?
 | |
|           argidx++;               /* argument done */
 | |
|           break;
 | |
|         } /* switch */
 | |
|         pushreg(sPRI);            /* store the function argument on the stack */
 | |
|         markexpr(sPARM,NULL,0);   /* mark the end of a sub-expression */
 | |
|         nest_stkusage++;
 | |
|       } /* if */
 | |
|       assert(arglist[argpos]!=ARG_UNHANDLED);
 | |
|       nargs++;
 | |
|       if (matchparanthesis) {
 | |
|         close=matchtoken(')');
 | |
|         if (!close)               /* if not paranthese... */
 | |
|           if (!needtoken(','))    /* ...should be comma... */
 | |
|             break;                /* ...but abort loop if neither */
 | |
|       } else {
 | |
|         close=!matchtoken(',');
 | |
|         if (close) {              /* if not comma... */
 | |
|           if (needtoken(tTERM)==1)/* ...must be end of statement */
 | |
|             lexpush();            /* push again, because end of statement is analised later */
 | |
|         } /* if */
 | |
|       } /* if */
 | |
|     } while (!close && freading && !matchtoken(tENDEXPR)); /* do */
 | |
|   } /* if */
 | |
|   /* check remaining function arguments (they may have default values) */
 | |
|   for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) {
 | |
|     if (arglist[argidx]==ARG_DONE)
 | |
|       continue;                 /* already seen and handled this argument */
 | |
|     /* in this first stage, we also skip the arguments with uSIZEOF and uTAGOF;
 | |
|      * these are handled last
 | |
|      */
 | |
|     if ((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0) {
 | |
|       assert(arg[argidx].ident==iVARIABLE);
 | |
|       continue;
 | |
|     } /* if */
 | |
|     stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */
 | |
|     if (arg[argidx].hasdefault) {
 | |
|       if (arg[argidx].ident==iREFARRAY) {
 | |
|         short level;
 | |
|         setdefarray(arg[argidx].defvalue.array.data,
 | |
|                     arg[argidx].defvalue.array.size,
 | |
|                     arg[argidx].defvalue.array.arraysize,
 | |
|                     &arg[argidx].defvalue.array.addr,
 | |
|                     (arg[argidx].usage & uCONST)!=0);
 | |
|         if (arg[argidx].defvalue.array.data != NULL) {
 | |
|           if ((arg[argidx].usage & uCONST)==0) {
 | |
|             heapalloc+=arg[argidx].defvalue.array.arraysize;
 | |
|             nest_stkusage+=arg[argidx].defvalue.array.arraysize;
 | |
|           } /* if */
 | |
|           /* keep the lengths of all dimensions of a multi-dimensional default array */
 | |
|           assert(arg[argidx].numdim>0);
 | |
|           if (arg[argidx].numdim==1) {
 | |
|             append_constval(&arrayszlst,arg[argidx].name,arg[argidx].defvalue.array.arraysize,0);
 | |
|           } else {
 | |
|             for (level=0; level<arg[argidx].numdim; level++) {
 | |
|               assert(level<sDIMEN_MAX);
 | |
|               append_constval(&arrayszlst,arg[argidx].name,arg[argidx].dim[level],level);
 | |
|             } /* for */
 | |
|           } /* if */
 | |
|         }
 | |
|       } else if (arg[argidx].ident==iREFERENCE) {
 | |
|         setheap(arg[argidx].defvalue.val);
 | |
|         /* address of the value on the heap in PRI */
 | |
|         heapalloc+=markheap(MEMUSE_STATIC, 1);
 | |
|         nest_stkusage++;
 | |
|       } else {
 | |
|         int dummytag=arg[argidx].tags[0];
 | |
|         ldconst(arg[argidx].defvalue.val,sPRI);
 | |
|         assert(arg[argidx].numtags>0);
 | |
|         check_userop(NULL,arg[argidx].defvalue_tag,arg[argidx].tags[0],2,NULL,&dummytag);
 | |
|         assert(dummytag==arg[argidx].tags[0]);
 | |
|       } /* if */
 | |
|       pushreg(sPRI);            /* store the function argument on the stack */
 | |
|       markexpr(sPARM,NULL,0);   /* mark the end of a sub-expression */
 | |
|       nest_stkusage++;
 | |
|     } else {
 | |
|       error(92,argidx);        /* argument count mismatch */
 | |
|     } /* if */
 | |
|     if (arglist[argidx]==ARG_UNHANDLED)
 | |
|       nargs++;
 | |
|     arglist[argidx]=ARG_DONE;
 | |
|   } /* for */
 | |
|   /* now a second loop to catch the arguments with default values that are
 | |
|    * the "sizeof" or "tagof" of other arguments
 | |
|    */
 | |
|   for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) {
 | |
|     constvalue *asz;
 | |
|     cell array_sz;
 | |
|     if (arglist[argidx]==ARG_DONE)
 | |
|       continue;                 /* already seen and handled this argument */
 | |
|     stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */
 | |
|     assert(arg[argidx].ident==iVARIABLE);           /* if "sizeof", must be single cell */
 | |
|     /* if unseen, must be "sizeof" or "tagof" */
 | |
|     assert((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0);
 | |
|     if ((arg[argidx].hasdefault & uSIZEOF)!=0) {
 | |
|       /* find the argument; if it isn't found, the argument's default value
 | |
|        * was a "sizeof" of a non-array (a warning for this was already given
 | |
|        * when declaring the function)
 | |
|        */
 | |
|       asz=find_constval(&arrayszlst,arg[argidx].defvalue.size.symname,
 | |
|                         arg[argidx].defvalue.size.level);
 | |
|       if (asz!=NULL) {
 | |
|         array_sz=asz->value;
 | |
|         if (array_sz==0)
 | |
|           error(224,arg[argidx].name);    /* indeterminate array size in "sizeof" expression */
 | |
|       } else {
 | |
|         array_sz=1;
 | |
|       } /* if */
 | |
|     } else {
 | |
|       asz=find_constval(&taglst,arg[argidx].defvalue.size.symname,
 | |
|                         arg[argidx].defvalue.size.level);
 | |
|       if (asz != NULL) {
 | |
|         exporttag(asz->value);
 | |
|         array_sz=asz->value | PUBLICTAG;  /* must be set, because it just was exported */
 | |
|       } else {
 | |
|         array_sz=0;
 | |
|       } /* if */
 | |
|     } /* if */
 | |
|     ldconst(array_sz,sPRI);
 | |
|     pushreg(sPRI);              /* store the function argument on the stack */
 | |
|     markexpr(sPARM,NULL,0);
 | |
|     nest_stkusage++;
 | |
|     if (arglist[argidx]==ARG_UNHANDLED)
 | |
|       nargs++;
 | |
|     arglist[argidx]=ARG_DONE;
 | |
|   } /* for */
 | |
|   stgmark(sENDREORDER);         /* mark end of reversed evaluation */
 | |
|   pushval((cell)nargs /* *sizeof(cell)*/ );
 | |
|   nest_stkusage++;
 | |
|   ffcall(sym,NULL,nargs);
 | |
|   if (sc_status!=statSKIP)
 | |
|     markusage(sym,uREAD);       /* do not mark as "used" when this call itself is skipped */
 | |
|   if ((sym->usage & uNATIVE)!=0 &&sym->x.lib!=NULL)
 | |
|     sym->x.lib->value += 1;     /* increment "usage count" of the library */
 | |
|   if (symret!=NULL)
 | |
|     popreg(sPRI);               /* pop hidden parameter as function result */
 | |
|   sideeffect=TRUE;              /* assume functions carry out a side-effect */
 | |
|   delete_consttable(&arrayszlst);     /* clear list of array sizes */
 | |
|   delete_consttable(&taglst);   /* clear list of parameter tags */
 | |
| 
 | |
|   /* maintain max. amount of memory used */
 | |
|   {
 | |
|     long totalsize;
 | |
|     totalsize=declared+heapalloc+1;   /* local variables & return value size,
 | |
|                                        * +1 for PROC opcode */
 | |
|     if (lval_result->ident==iREFARRAY)
 | |
|       totalsize++;                    /* add hidden parameter (on the stack) */
 | |
|     if ((sym->usage & uNATIVE)==0)
 | |
|       totalsize++;                    /* add "call" opcode */
 | |
|     totalsize+=nest_stkusage;
 | |
|     if (curfunc != NULL) {
 | |
|       if (curfunc->x.stacksize<totalsize)
 | |
|         curfunc->x.stacksize=totalsize;
 | |
|     } else {
 | |
|       error(10);
 | |
|     }
 | |
|     nest_stkusage-=nargs+heapalloc+1; /* stack/heap space, +1 for argcount param */
 | |
|     /* if there is a syntax error in the script, the stack calculation is
 | |
|      * probably incorrect; but we may not allow it to drop below zero
 | |
|      */
 | |
|     if (nest_stkusage<0)
 | |
|       nest_stkusage=0;
 | |
|   }
 | |
| 
 | |
|   /* scrap any arrays left on the heap, with the exception of the array that
 | |
|    * this function has as a result (in other words, scrap all arrays on the
 | |
|    * heap that caused by expressions in the function arguments)
 | |
|    */
 | |
|   popheaplist();
 | |
|   nesting--;
 | |
| }
 | |
| 
 | |
| /*  dbltest
 | |
|  *
 | |
|  *  Returns a non-zero value if lval1 an array and lval2 is not an array and
 | |
|  *  the operation is addition or subtraction.
 | |
|  *
 | |
|  *  Returns the "shift" count (1 for 16-bit, 2 for 32-bit) to align a cell
 | |
|  *  to an array offset.
 | |
|  */
 | |
| static int dbltest(void (*oper)(),value *lval1,value *lval2)
 | |
| {
 | |
|   if ((oper!=ob_add) && (oper!=ob_sub))
 | |
|     return 0;
 | |
|   if (lval1->ident!=iARRAY)
 | |
|     return 0;
 | |
|   if (lval2->ident==iARRAY)
 | |
|     return 0;
 | |
|   return sizeof(cell)/2;        /* 1 for 16-bit, 2 for 32-bit */
 | |
| }
 | |
| 
 | |
| /*  commutative
 | |
|  *
 | |
|  *  Test whether an operator is commutative, i.e. x oper y == y oper x.
 | |
|  *  Commutative operators are: +  (addition)
 | |
|  *                             *  (multiplication)
 | |
|  *                             == (equality)
 | |
|  *                             != (inequality)
 | |
|  *                             &  (bitwise and)
 | |
|  *                             ^  (bitwise xor)
 | |
|  *                             |  (bitwise or)
 | |
|  *
 | |
|  *  If in an expression, code for the left operand has been generated and
 | |
|  *  the right operand is a constant and the operator is commutative, the
 | |
|  *  precautionary "push" of the primary register is scrapped and the constant
 | |
|  *  is read into the secondary register immediately.
 | |
|  */
 | |
| static int commutative(void (*oper)())
 | |
| {
 | |
|   return oper==ob_add || oper==os_mult
 | |
|          || oper==ob_eq || oper==ob_ne
 | |
|          || oper==ob_and || oper==ob_xor || oper==ob_or;
 | |
| }
 | |
| 
 | |
| /*  constant
 | |
|  *
 | |
|  *  Generates code to fetch a number, a literal character (which is returned
 | |
|  *  by lex() as a number as well) or a literal string (lex() stores the
 | |
|  *  strings in the literal queue). If the operand was a number, it is stored
 | |
|  *  in lval->constval.
 | |
|  *
 | |
|  *  The function returns 1 if the token was a constant or a string, 0
 | |
|  *  otherwise.
 | |
|  */
 | |
| static int constant(value *lval)
 | |
| {
 | |
|   int tok,index,ident;
 | |
|   cell val,item,cidx;
 | |
|   char *st;
 | |
|   symbol *sym;
 | |
|   int cmptag=lval->cmptag;
 | |
| 
 | |
|   tok=lex(&val,&st);
 | |
|   if (tok==tSYMBOL && (sym=findconst(st,&cmptag))!=0) {
 | |
|     if (cmptag>1)
 | |
|       error(91,sym->name);  /* ambiguity: multiple matching constants (different tags) */
 | |
|     lval->constval=sym->addr;
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->tag=sym->tag;
 | |
|     lval->sym=sym;
 | |
|     markusage(sym,uREAD);
 | |
|   } else if (tok==tNUMBER) {
 | |
|     lval->constval=val;
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|   } else if (tok==tRATIONAL) {
 | |
|     lval->constval=val;
 | |
|     ldconst(lval->constval,sPRI);
 | |
|     lval->ident=iCONSTEXPR;
 | |
|     lval->tag=sc_rationaltag;
 | |
|   } else if (tok==tSTRING) {
 | |
|     /* lex() stores starting index of string in the literal table in 'val' */
 | |
|     ldconst((val+glb_declared)*sizeof(cell),sPRI);
 | |
|     lval->ident=iARRAY;         /* pretend this is a global array */
 | |
|     lval->constval=val-litidx;  /* constval == the negative value of the
 | |
|                                  * size of the literal array; using a negative
 | |
|                                  * value distinguishes between literal arrays
 | |
|                                  * and literal strings (this was done for
 | |
|                                  * array assignment). */
 | |
| 	lval->tag=pc_tag_string;
 | |
|   } else if (tok=='{') {
 | |
|     int tag,lasttag=-1;
 | |
|     val=litidx;
 | |
|     do {
 | |
|       /* cannot call constexpr() here, because "staging" is already turned
 | |
|        * on at this point */
 | |
|       assert(staging);
 | |
|       stgget(&index,&cidx);     /* mark position in code generator */
 | |
|       ident=expression(&item,&tag,NULL,FALSE,NULL);
 | |
|       stgdel(index,cidx);       /* scratch generated code */
 | |
|       if (ident!=iCONSTEXPR)
 | |
|         error(8);               /* must be constant expression */
 | |
|       if (lasttag<0)
 | |
|         lasttag=tag;
 | |
|       else if (!matchtag(lasttag,tag,FALSE))
 | |
|         error(213);             /* tagname mismatch */
 | |
|       litadd(item);             /* store expression result in literal table */
 | |
|     } while (matchtoken(','));
 | |
|     if (!needtoken('}'))
 | |
|       lexclr(FALSE);
 | |
|     ldconst((val+glb_declared)*sizeof(cell),sPRI);
 | |
|     lval->ident=iARRAY;         /* pretend this is a global array */
 | |
|     lval->constval=litidx-val;  /* constval == the size of the literal array */
 | |
|   } else {
 | |
|     return FALSE;               /* no, it cannot be interpreted as a constant */
 | |
|   } /* if */
 | |
|   return TRUE;                  /* yes, it was a constant value */
 | |
| }
 | |
| 
 |