/* Small compiler - Recursive descend expresion parser * * Copyright (c) ITB CompuPhase, 1997-2003 * * 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 required. * 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$ */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include /* for PATH_MAX */ #include #include "embryo_cc_sc.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 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); 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. */ static int nextop(int *opidx, int *list) { *opidx = 0; while (*list) { if (matchtoken(*list)) { return TRUE; /* found! */ } else { list += 1; *opidx += 1; } /* if */ } /* while */ return FALSE; /* entire list scanned, nothing found */ } 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) { /* assignment operator: a special case */ strcpy(opername, "="); if (lval && (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); 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); if (!sym /*|| (sym->usage & uDEFINE)==0 */ ) { /* ??? should not check uDEFINE; first pass clears these bits */ /* check for commutative operators */ if (tag1 == tag2 || !oper || !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); if (!sym /*|| (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); if (lval->ident == iARRAYCELL || lval->ident == iARRAYCHAR) push1(); /* 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 */ push1(); /* 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); /* this was checked earlier */ assert(lval->ident == iARRAYCELL || lval->ident == iARRAYCHAR); /* checked earlier */ push2(); } /* if */ /* push parameters, call the function */ paramspassed = (!oper) ? 1 : numparam; switch (paramspassed) { case 1: push1(); 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) { push2(); push1(); } else { push1(); push2(); } /* if */ break; default: assert(0); } /* switch */ endexpr(FALSE); /* mark the end of a sub-expression */ pushval((cell) paramspassed * sizeof(cell)); assert(sym->ident == iFUNCTN); ffcall(sym, paramspassed); if (sc_status != statSKIP) markusage(sym, uREAD); /* do not mark as "used" when this call itself is skipped */ if (sym->x.lib) sym->x.lib->value += 1; /* increment "usage count" of the library */ sideeffect = TRUE; /* assume functions carry out a side-effect */ assert(!!resulttag); *resulttag = sym->tag; /* save tag of the called function */ if (savepri || savealt) pop2(); /* restore the saved PRI/ALT that into ALT */ if (oper == user_inc || oper == user_dec) { assert(!!lval); if (lval->ident == iARRAYCELL || lval->ident == iARRAYCHAR) pop2(); /* restore address (in ALT) */ store(lval); /* store PRI in the symbol */ moveto1(); /* make sure PRI is restored on exit */ } /* if */ return TRUE; } 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) 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; 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 */ if (nextop(&opidx, opstr)) { 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! */ const1(endval); jumplabel(endlab = getlabel()); setlabel(droplab); const1(dropval); setlabel(endlab); lval->sym = NULL; lval->tag = 0; 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) const1(lval->constval); (*testfunc) (exit1); } static void checkfunction(value * lval) { symbol *sym = lval->sym; if (!sym || (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 " */ } /* 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 = { NULL, 0, 0, 0, 0, NULL }; 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) lval->tag = sc_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 = { NULL, 0, 0, 0, 0, NULL }; int count; /* 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(); *lval = lval2; /* copy right hand expression of the previous iteration */ } /* 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 = sc_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) const1(lval2->constval << dbltest(oper, lval2, lval1)); const2(lval1->constval << dbltest(oper, lval2, lval1)); /* ^ doubling of constants operating on integer addresses */ /* is restricted to "add" and "subtract" operators */ } else { /* non-constant on left side */ push1(); if (plnge1(hier, lval2)) rvalue(lval2); if (lval2->ident == iCONSTEXPR) { /* constant on right side */ if (commutative(oper)) { /* test for commutative operators */ value lvaltmp = { NULL, 0, 0, 0, 0, NULL }; stgdel(index, cidx); /* scratch push1() and constant fetch (then * fetch the constant again */ const2(lval2->constval << dbltest(oper, lval1, lval2)); /* 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 { const1(lval2->constval << dbltest(oper, lval1, lval2)); pop2(); /* pop result of left operand into secondary register */ } /* if */ } else { /* non-constants on both sides */ pop2(); 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) ? lval1->sym->name : "-unknown-"; error(33, ptr); /* array must be indexed */ } else if (lval2->ident == iARRAY || lval2->ident == iREFARRAY) { char *ptr = (lval2->sym) ? 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 (!matchtag(lval1->tag, lval2->tag, FALSE)) error(213); /* tagname mismatch */ (*oper) (); /* do the (signed) operation */ lval1->ident = iEXPRESSION; } /* if */ } /* if */ } static cell truemodulus(cell a, cell b) { return (a % b + b) % b; } 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 (left - truemodulus(left, right)) / right; else if (oper == os_mod) return truemodulus(left, right); else error(29); /* invalid expression, assumed 0 (this should never occur) */ return 0; } int expression(int *constant, cell * val, int *tag, int chkfuncresult) { value lval = { NULL, 0, 0, 0, 0, NULL }; if (hier14(&lval)) rvalue(&lval); if (lval.ident == iCONSTEXPR) { /* constant expression */ *constant = TRUE; *val = lval.constval; } else { *constant = FALSE; *val = 0; } /* if */ if (tag) *tag = lval.tag; if (chkfuncresult) checkfunction(&lval); return lval.ident; } static cell array_totalsize(symbol * sym) { cell length; assert(!!sym); 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); assert(sym->ident == iARRAY || sym->ident == iREFARRAY); assert(level <= sym->dim.array.level); while (level-- > 0) { sym = finddepend(sym); assert(!!sym); } /* if */ return sym->dim.array.length; } /* hier14 * * Lowest hierarchy level (except for the , operator). * * Global references: intest (reffered to only) */ int hier14(value * lval1) { int lvalue; value lval2 = { NULL, 0, 0, 0, 0, NULL }; value lval3 = { NULL, 0, 0, 0, 0, NULL }; void (*oper) (void); int tok, level, i; cell val; char *st; int bwcount; cell arrayidx1[sDIMEN_MAX], arrayidx2[sDIMEN_MAX]; /* last used array indices */ cell *org_arrayidx; bwcount = bitwise_opercount; bitwise_opercount = 0; for (i = 0; i < sDIMEN_MAX; i++) arrayidx1[i] = arrayidx2[i] = 0; org_arrayidx = lval1->arrayidx; /* save current pointer, to reset later */ if (!lval1->arrayidx) lval1->arrayidx = arrayidx1; lvalue = plnge1(hier13, lval1); if (lval1->ident != iARRAYCELL && lval1->ident != iARRAYCHAR) lval1->arrayidx = NULL; if (lval1->ident == iCONSTEXPR) /* load constant here */ const1(lval1->constval); 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 (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); 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); if ((lval1->sym->usage & uCONST) != 0) return error(22); /* assignment to const argument */ 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) { push1(); rvalue(lval1); } /* if */ lval2.arrayidx = arrayidx2; plnge2(oper, hier14, lval1, &lval2); if (lval2.ident != iARRAYCELL && lval2.ident != iARRAYCHAR) lval2.arrayidx = NULL; if (oper) pop2(); if (!oper && lval3.arrayidx && lval2.arrayidx && lval3.ident == lval2.ident && lval3.sym == lval2.sym) { int same = TRUE; assert(lval3.arrayidx == arrayidx1); 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(). */ 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); error(226, lval3.sym->name); /* self-assignment */ } /* if */ } /* if */ } /* if */ if (lval3.ident == iARRAY || lval3.ident == iREFARRAY) { /* 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. */ int exactmatch = TRUE; if (lval2.ident != iARRAY && lval2.ident != iREFARRAY) error(33, lval3.sym->name); /* array must be indexed */ if (lval2.sym) { val = lval2.sym->dim.array.length; /* array variable */ level = lval2.sym->dim.array.level; } 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. */ if (val < 0) { val = -val; exactmatch = FALSE; } /* if */ } /* if */ if (lval3.sym->dim.array.level != level) return error(48); /* array dimensions must match */ else if (lval3.sym->dim.array.length < val || (exactmatch && lval3.sym->dim.array.length > val)) return error(47); /* array sizes must match */ if (level > 0) { /* check the sizes of all sublevels too */ symbol *sym1 = lval3.sym; symbol *sym2 = lval2.sym; int i; assert(!!sym1 && !!sym2); /* ^^^ sym2 must be valid, because only variables can be * multi-dimensional (there are no multi-dimensional arrays), * 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 && !!sym2); /* ^^^ 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 */ } /* 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 (lval3.ident == iARRAY || lval3.ident == iREFARRAY) { 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 && !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; return FALSE; /* expression result is never an lvalue */ } static int hier13(value * lval) { int lvalue, flab1, flab2; value lval2 = { NULL, 0, 0, 0, 0, NULL }; int array1, array2; lvalue = plnge1(hier12, lval); if (matchtoken('?')) { flab1 = getlabel(); flab2 = getlabel(); if (lvalue) { rvalue(lval); } else if (lval->ident == iCONSTEXPR) { const1(lval->constval); error(lval->constval ? 206 : 205); /* redundant test */ } /* if */ jmp_eq0(flab1); /* go to second expression if primary register==0 */ if (hier14(lval)) rvalue(lval); jumplabel(flab2); setlabel(flab1); needtoken(':'); if (hier14(&lval2)) rvalue(&lval2); array1 = (lval->ident == iARRAY || lval->ident == iREFARRAY); array2 = (lval2.ident == iARRAY || lval2.ident == iREFARRAY); if (array1 && !array2) { char *ptr = (lval->sym->name) ? lval->sym->name : "-unknown-"; error(33, ptr); /* array must be indexed */ } else if (!array1 && array2) { char *ptr = (lval2.sym->name) ? 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) */ setlabel(flab2); 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; } /* endif */ } /* the order of the operators in these lists is important and must cohere */ /* with 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); 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); 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 = sc_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) { float *f = (float *)&lval->constval; *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 = sc_addtag(st); 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) sym = findglb(st); if (sym && 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); if (!val && find_subst(st, strlen(st))) val = 1; clear_value(lval); lval->ident = iCONSTEXPR; lval->constval = val; const1(lval->constval); 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) sym = findglb(st); if (!sym) 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; for (level = 0; matchtoken('['); level++) needtoken(']'); if (level > sym->dim.array.level) error(28); /* invalid subscript */ else lval->constval = array_levelsize(sym, level); if (lval->constval == 0 && !strchr(lptr, PREPROC_TERM)) error(224, st); /* indeterminate array size in "sizeof" expression */ } /* if */ const1(lval->constval); 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) { tag = sc_addtag(st); } else { sym = findloc(st); if (!sym) sym = findglb(st); if (!sym) 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 */ exporttag(tag); clear_value(lval); lval->ident = iCONSTEXPR; lval->constval = tag; const1(lval->constval); while (paranthese--) needtoken(')'); return FALSE; 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); 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) push1(); /* 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) pop1(); /* 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); if ((lval->sym->usage & uCONST) != 0) return error(22); /* assignment to const argument */ saveresult = (lval->ident == iARRAYCELL || lval->ident == iARRAYCHAR); if (saveresult) push1(); /* 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) pop1(); /* restore PRI (result of rvalue()) */ sideeffect = TRUE; return FALSE; case tCHAR: /* char (compute required # of cells */ if (lval->ident == iCONSTEXPR) { lval->constval *= charbits / 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; 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 = { NULL, 0, 0, 0, 0, NULL }; char *st; char close; symbol *sym; lvalue = primary(lval1); symtok = tokeninfo(&val, &st); /* get token read by primary() */ restart: sym = lval1->sym; if (matchtoken('[') || matchtoken('{') || matchtoken('(')) { tok = tokeninfo(&val, &st); /* get token read by matchtoken() */ if (!sym && 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) { /* sym==NULL if lval is a constant or a literal */ error(28); /* cannot subscript */ needtoken(close); return FALSE; } else if (sym->ident != iARRAY && sym->ident != iREFARRAY) { error(28); /* 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 */ stgget(&index, &cidx); /* mark position in code generator */ push1(); /* 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 (!matchtag(sym->x.idxtag, lval2.tag, TRUE)) error(213); if (lval2.ident == iCONSTEXPR) { /* constant expression */ stgdel(index, cidx); /* scratch generated code */ if (lval1->arrayidx) { /* 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 == ']') { /* 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 defined(BIT16) const2(lval2.constval << 1); #else const2(lval2.constval << 2); #endif ob_add(); } /* if */ } else { /* character index */ if (lval2.constval < 0 || (sym->dim.array.length != 0 && sym->dim.array.length * ((8 * sizeof(cell)) / charbits) <= (ucell) lval2.constval)) error(32, sym->name); /* array index out of bounds */ if (lval2.constval != 0) { /* don't add offsets for zero subscripts */ if (charbits == 16) const2(lval2.constval << 1); /* 16-bit character */ else const2(lval2.constval); /* 8-bit character */ ob_add(); } /* if */ charalign(); /* align character index into array */ } /* if */ } else { /* array index is not constant */ lval1->arrayidx = NULL; /* reset, so won't be checked */ if (close == ']') { 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 / charbits) - 1); char2addr(); /* character array index */ } /* if */ pop2(); ob_add(); /* base address was popped into secondary register */ if (close != ']') charalign(); /* align character index into array */ } /* if */ /* the indexed item may be another array (multi-dimensional arrays) */ assert(lval1->sym == sym && !!sym); /* should still be set */ if (sym->dim.array.level > 0) { assert(close == ']'); /* checked earlier */ /* read the offset to the subarray and add it to the current address */ lval1->ident = iARRAYCELL; push1(); /* the optimizer makes this to a MOVE.alt */ rvalue(lval1); pop2(); ob_add(); /* adjust the "value" structure and find the referenced array */ lval1->ident = iREFARRAY; lval1->sym = finddepend(sym); assert(!!lval1->sym); assert(lval1->sym->dim.array.level == sym->dim.array.level - 1); /* 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 */ lval1->ident = (char)((close == ']') ? iARRAYCELL : iARRAYCHAR); lval1->tag = sym->tag; /* a cell in an array is an lvalue, a character in an array is not * always a *valid* lvalue */ return TRUE; } else { /* tok=='(' -> function(...) */ if (!sym || (sym->ident != iFUNCTN && sym->ident != iREFFUNC)) { if (!sym && 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) markusage(sym, uREAD); } /* if */ return error(12); /* invalid function call */ } 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->ident = iEXPRESSION; lval1->constval = 0; lval1->tag = sym->tag; return FALSE; /* result of function call is no lvalue */ } /* if */ } /* if */ if (sym && lval1->ident == iFUNCTN) { assert(sym->ident == iFUNCTN); address(sym); lval1->sym = NULL; lval1->ident = iREFFUNC; /* ??? however... function pointers (or function references are not (yet) allowed */ error(29); /* expression error, assumed 0 */ return FALSE; } /* 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: 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((stkitem) intest); pushstk((stkitem) sc_allowtags); intest = 0; /* no longer in "test" expression */ sc_allowtags = TRUE; /* allow tagnames to be used in parenthised expressions */ do lvalue = hier14(lval); while (matchtoken(',')); needtoken(')'); lexclr(FALSE); /* clear lex() push-back, it should have been * cleared already by needtoken() */ sc_allowtags = (int)(long)popstk(); intest = (int)(long)popstk(); 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)) { /* first look for a local variable */ if ((sym = findloc(st))) { if (sym->ident == iLABEL) { error(29); /* expression error, assumed 0 */ const1(0); /* 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); /* 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))) { 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); /* 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 { return error(17, st); /* undefined symbol */ } /* endif */ assert(!!sym); 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 */ const1(0); /* 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() */ } 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). */ assert(!!string); assert(size > 0); /* check whether to dump the default array */ assert(!!dataaddr); if (sc_status == statWRITE && *dataaddr < 0) { int i; *dataaddr = (litidx + glb_declared) * sizeof(cell); for (i = 0; i < size; i++) stowlit(*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) { const1(*dataaddr); } 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 */ const1(*dataaddr); /* "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)); /* ??? 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; } static 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) { 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 = { NULL, 0, 0, 0, 0, NULL }; arginfo *arg; char arglist[sMAXARGS]; constvalue arrayszlst = { NULL, "", 0, 0 }; /* array size list starts empty */ cell lexval; char *lexstr; assert(!!sym); arg = sym->dim.arglist; assert(!!arg); stgmark(sSTARTREORDER); for (argpos = 0; argpos < sMAXARGS; argpos++) arglist[argpos] = ARG_UNHANDLED; if (!matchtoken(')')) { 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 */ 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(202); /* 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" */ lvalue = hier14(&lval); switch (arg[argidx].ident) { case 0: error(202); /* argument count mismatch */ break; case iVARARGS: /* always pass by reference */ if (lval.ident == iVARIABLE || lval.ident == iREFERENCE) { assert(!!lval.sym); 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 */ assert(lvalue); rvalue(&lval); /* get value in PRI */ setheap_pri(); /* address of the value on the heap in PRI */ heapalloc++; } else if (lvalue) { address(lval.sym); } else { setheap_pri(); /* address of the value on the heap in PRI */ heapalloc++; } /* if */ } else if (lval.ident == iCONSTEXPR || lval.ident == iEXPRESSION || lval.ident == iARRAYCHAR) { /* fetch value if needed */ if (lval.ident == iARRAYCHAR) rvalue(&lval); /* 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++; } /* if */ /* ??? handle const array passed by reference */ /* otherwise, the address is already in PRI */ if (lval.sym) markusage(lval.sym, uWRITTEN); /* * Dont need this warning - its varargs. there is no way of knowing the * required tag/type... * if (!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 (!checktag (arg[argidx].tags, arg[argidx].numtags, lval.tag)) error(213); argidx++; /* argument done */ break; case iREFERENCE: if (!lvalue || lval.ident == iARRAYCHAR) error(35, argidx + 1); /* argument type mismatch */ if (lval.sym && (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); address(lval.sym); } else { setheap_pri(); /* address of the value on the heap in PRI */ heapalloc++; } /* if */ } /* if */ /* otherwise, the address is already in PRI */ if (!checktag (arg[argidx].tags, arg[argidx].numtags, lval.tag)) error(213); argidx++; /* argument done */ if (lval.sym) markusage(lval.sym, uWRITTEN); break; case iREFARRAY: if (lval.ident != iARRAY && lval.ident != iREFARRAY && lval.ident != iARRAYCELL) { error(35, argidx + 1); /* argument type mismatch */ break; } /* if */ if (lval.sym && (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 || lval.ident == iARRAYCELL) { 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) { /* 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); 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 */ append_constval(&arrayszlst, arg[argidx].name, sym->dim.array.length, level); sym = finddepend(sym); assert(!!sym); level++; } /* if */ /* the last dimension is checked too, again, unless it is zero */ assert(level < sDIMEN_MAX); assert(!!sym); if (arg[argidx].dim[level] != 0 && sym->dim.array.length != arg[argidx].dim[level]) error(47); /* array sizes must match */ 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); // ??? set uWRITTEN? argidx++; /* argument done */ break; } /* switch */ push1(); /* store the function argument on the stack */ endexpr(FALSE); /* mark the end of a sub-expression */ } /* if */ assert(arglist[argpos] != ARG_UNHANDLED); nargs++; close = matchtoken(')'); if (!close) /* if not paranthese... */ if (!needtoken(',')) /* ...should be comma... */ break; /* ...but abort loop if neither */ } 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].usage & uCONST) == 0) heapalloc += arg[argidx].defvalue.array.arraysize; /* 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++; } else { int dummytag = arg[argidx].tags[0]; const1(arg[argidx].defvalue.val); 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 */ push1(); /* store the function argument on the stack */ endexpr(FALSE); /* mark the end of a sub-expression */ } else { error(202, 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) { 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 { symbol *sym; assert((arg[argidx].hasdefault & uTAGOF) != 0); sym = findloc(arg[argidx].defvalue.size.symname); if (!sym) sym = findglb(arg[argidx].defvalue.size.symname); array_sz = (sym) ? sym->tag : 0; exporttag(array_sz); } /* if */ const1(array_sz); push1(); /* store the function argument on the stack */ endexpr(FALSE); if (arglist[argidx] == ARG_UNHANDLED) nargs++; arglist[argidx] = ARG_DONE; } /* for */ stgmark(sENDREORDER); /* mark end of reversed evaluation */ pushval((cell) nargs * sizeof(cell)); ffcall(sym, nargs); if (sc_status != statSKIP) markusage(sym, uREAD); /* do not mark as "used" when this call itself is skipped */ if (sym->x.lib) sym->x.lib->value += 1; /* increment "usage count" of the library */ modheap(-heapalloc * sizeof(cell)); sideeffect = TRUE; /* assume functions carry out a side-effect */ delete_consttable(&arrayszlst); /* clear list of array sizes */ } /* 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, constant; cell val, item, cidx; char *st; symbol *sym; tok = lex(&val, &st); if (tok == tSYMBOL && (sym = findconst(st))) { lval->constval = sym->addr; const1(lval->constval); lval->ident = iCONSTEXPR; lval->tag = sym->tag; markusage(sym, uREAD); } else if (tok == tNUMBER) { lval->constval = val; const1(lval->constval); lval->ident = iCONSTEXPR; } else if (tok == tRATIONAL) { lval->constval = val; const1(lval->constval); lval->ident = iCONSTEXPR; lval->tag = sc_rationaltag; } else if (tok == tSTRING) { /* lex() stores starting index of string in the literal table in 'val' */ const1((val + glb_declared) * sizeof(cell)); 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). */ } 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 */ expression(&constant, &item, &tag, FALSE); stgdel(index, cidx); /* scratch generated code */ if (constant == 0) error(8); /* must be constant expression */ if (lasttag < 0) lasttag = tag; else if (!matchtag(lasttag, tag, FALSE)) error(213); /* tagname mismatch */ stowlit(item); /* store expression result in literal table */ } while (matchtoken(',')); needtoken('}'); const1((val + glb_declared) * sizeof(cell)); 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 */ }