#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/PRS.C,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name prs.c - code parser Function Parse TADS code Notes This module contains the TADS code parser. TADS code is basically a series of object definitions (a function is just a special kind of object). So, this module has a function that reads from a line source and generates a compiled object. Compiled objects are completely self-contained, apart from references to other objects. We initially allocate a block of space for the object, then make it larger if need be. If it's a class object, we will reduce its size down to its actual size, because class objects generally are not modified at run-time. Modified 04/11/99 CNebel - Fix signing errors. 09/14/92 MJRoberts - note where an object was created 08/27/91 MJRoberts - creation */ #include #include #include "os.h" #include "std.h" #include "mcm.h" #include "tok.h" #include "prs.h" #include "lst.h" #include "prp.h" #include "obj.h" #include "opc.h" #include "emt.h" #include "mch.h" #include "voc.h" /* allocate space from node pool */ static prsndef *prsalo(prscxdef *ctx, int subnodes); /* parse an expression (can be called recursively) */ static prsndef *prsxexp(prscxdef *ctx, int inlist); /* add a symbol to the symbol table if it's not already there */ void prsdef(prscxdef *ctx, tokdef *tok, int typ) { toktdef *tab; tab = (typ == TOKSTLABEL ? ctx->prscxgtab : ctx->prscxstab); if (tok->toktyp == TOKTSYMBOL && tok->toksym.tokstyp == TOKSTUNK) { if (!tab) errsig(ctx->prscxerr, ERR_UNDFSYM); /* see if it's been defined since we last looked */ if (!(*tab->toktfsea)(tab, tok->toknam, tok->toklen, tok->tokhash, &tok->toksym)) { /* still not defined; add to the symbol table */ (*tab->toktfadd)(tab, tok->toknam, tok->toklen, TOKSTUNK, 0, tok->tokhash); (*tab->toktfsea)(tab, tok->toknam, tok->toklen, tok->tokhash, &tok->toksym); } } if (tok->toktyp == TOKTSYMBOL && tok->toksym.tokstyp == TOKSTUNK) { if (!tab) errsig(ctx->prscxerr, ERR_UNDFSYM); switch(typ) { case TOKSTPROP: tok->toksym.toksval = ctx->prscxprp++; break; case TOKSTFWDOBJ: case TOKSTFWDFN: /* fill in the symbol with the object information */ prsdefobj(ctx, tok, typ); break; case TOKSTEXTERN: tok->toksym.toksval = ctx->prscxextc++; break; case TOKSTLABEL: tok->toksym.toksval = emtglbl(ctx->prscxemt); break; } tok->toksym.tokstyp = typ; (*tab->toktfset)(tab, &tok->toksym); } } /* define a symbol as an object or forward object */ void prsdefobj(prscxdef *ctx, tokdef *tok, int typ) { mcmon newobj; uchar *p; /* allocate the object and clear its memory */ p = mcmalo(ctx->prscxmem, (ushort)PRSOBJSIZ, &newobj); memset(p, 0, (size_t)OBJDEFSIZ); /* save the line position after the object header */ linppos(ctx->prscxtok->tokcxlin, (char *)p + OBJDEFSIZ, (uint)(PRSOBJSIZ - OBJDEFSIZ)); #ifdef OS_ERRLINE /* save the source line for error displays later on */ { int len; int need; /* compute how much space we'll need for the line */ len = ctx->prscxtok->tokcxlin->linlen + 1; need = len + OBJDEFSIZ + strlen((char *)p + OBJDEFSIZ) + 1; /* reallocate the object if we need more room */ if (need > PRSOBJSIZ) p = mcmrealo(ctx->prscxmem, newobj, need); /* copy the line */ p += OBJDEFSIZ; p += strlen((char *)p) + 1; memcpy(p, ctx->prscxtok->tokcxlin->linbuf, (size_t)len); p[len] = '\0'; } #endif /* unlock the object */ mcmtch(ctx->prscxmem, newobj); mcmunlck(ctx->prscxmem, newobj); /* set the symbol to point to the new object */ tok->toksym.toksval = newobj; } /* require a property identifier, returning the property number */ prpnum prsrqpr(prscxdef *ctx) { tokdef *tok = &ctx->prscxtok->tokcxcur; prpnum ret; /* make sure we get a symbol */ if (tok->toktyp != TOKTSYMBOL) errsig(ctx->prscxerr, ERR_REQSYM); /* if it's an undefined symbol, make it a property id */ if (tok->toksym.tokstyp == TOKSTUNK) prsdef(ctx, tok, TOKSTPROP); /* make sure we have a property */ if (tok->toksym.tokstyp != TOKSTPROP) errsig(ctx->prscxerr, ERR_REQPRP); ret = tok->toksym.toksval; toknext(ctx->prscxtok); return(ret); } /* signal a "missing required token" error, finding token name */ void prssigreq(prscxdef *ctx, int t) { int i; char *p; static struct { int tokid; char *toknam; } toklist[] = { { TOKTSEM, "semicolon" }, { TOKTCOLON, "colon" }, { TOKTFUNCTION, "\"function\"" }, { TOKTCOMMA, "comma" }, { TOKTLBRACE, "left brace ('{')" }, { TOKTRPAR, "right paren (')')" }, { TOKTRBRACK, "right square bracket (']')" }, { TOKTWHILE, "\"while\"" }, { TOKTLPAR, "left paren ('(')" }, { TOKTEQ, "'='" }, { 0, (char *)0 } }; for (i = 0 ; toklist[i].toknam ; ++i) { if (toklist[i].tokid == t) { p = toklist[i].toknam; break; } } if (!toklist[i].toknam) p = ""; errsig1(ctx->prscxerr, ERR_REQTOK, ERRTSTR, p); } /* check for and skip a required token */ void prsreq(prscxdef *ctx, int t) { tokdef *tok = &ctx->prscxtok->tokcxcur; if (tok->toktyp != t) prssigreq(ctx, t); toknext(ctx->prscxtok); } /* get next token, require it to be a specific token, and skip it */ void prsnreq(prscxdef *ctx, int t) { tokdef *tok = &ctx->prscxtok->tokcxcur; toknext(ctx->prscxtok); if (tok->toktyp != t) prssigreq(ctx, t); toknext(ctx->prscxtok); } /* allocate bytes from node pool */ uchar *prsbalo(prscxdef *ctx, uint siz) { uchar *ret; /* round size for alignment, and see if we have room for it */ siz = osrndsz(siz); if (ctx->prscxnrem < siz) errsig(ctx->prscxerr, ERR_NONODE); /* adjust sizes and pointers, and return the new node */ ret = ctx->prscxnode; ctx->prscxnrem -= siz; ctx->prscxnode += siz; return(ret); } /* allocate a node with a given number of subnodes from node pool */ static prsndef *prsalo(prscxdef *ctx, int subnodes) { uint siz; /* figure size of node we actually need */ siz = offsetof(prsndef, prsnv.prsnvt); if (subnodes) siz += (subnodes * sizeof(prsndef *)); else siz += sizeof(tokdef); return((prsndef *)prsbalo(ctx, siz)); } /* begin a string in expression evaluation mode (puts in node pool) */ ushort prsxsst(prscxdef *ctx) { /* make sure there's space for the length prefix (a ushort) */ if (ctx->prscxnrem < 2) errsig(ctx->prscxerr, ERR_NONODE); ctx->prscxsofs = ctx->prscxnode - &ctx->prscxpool[0]; ctx->prscxnode += 2; ctx->prscxnrem -= 2; ctx->prscxslen = 2; return(ctx->prscxsofs); } /* add bytes to a string in expression mode */ void prsxsad(prscxdef *ctx, char *p, ushort len) { /* make sure there's room for the extra bytes */ if (ctx->prscxnrem < len) errsig(ctx->prscxerr, ERR_NONODE); memcpy(ctx->prscxnode, p, (size_t)len); ctx->prscxnode += len; ctx->prscxnrem -= len; ctx->prscxslen += len; } /* terminate a string in expression evaluation mode */ void prsxsend(prscxdef *ctx) { ushort siz; ushort rounded; /* figure total size of string, including padding for alignment */ siz = ctx->prscxslen; /* write length at beginning of string */ oswp2(&ctx->prscxpool[ctx->prscxsofs], siz); /* if desired, write the string to the strings file */ if (ctx->prscxstrfile != 0) { uchar *str; str = &ctx->prscxpool[ctx->prscxsofs] + 2; str[siz - 2] = '\0'; osfputs((char *)str, ctx->prscxstrfile); osfputs("\n", ctx->prscxstrfile); } /* round size of string if necessary */ rounded = osrndsz(siz); if (rounded - siz) { rounded -= siz; if (ctx->prscxnrem < rounded) errsig(ctx->prscxerr, ERR_NONODE); ctx->prscxnode += rounded; ctx->prscxnrem -= rounded; } } /* parse a list */ static prsndef *prslst(prscxdef *ctx) { prsndef *node = (prsndef *)0; for (;;) { if (ctx->prscxtok->tokcxcur.toktyp == TOKTRBRACK) break; node = prsnew2(ctx, TOKTRBRACK, prsxexp(ctx, TRUE), node); /* allow optional comma (just skip it if one is present) */ if (ctx->prscxtok->tokcxcur.toktyp == TOKTCOMMA) toknext(ctx->prscxtok); } toknext(ctx->prscxtok); /* skip the right bracket */ /* if it's an empty list, make an empty list token for it */ if (!node) { tokdef t; t.toktyp = TOKTLIST; t.tokofs = 0; node = prsnew0(ctx, &t); } return(node); } /* build a four-way operator node */ prsndef *prsnew4(prscxdef *ctx, int t, prsndef *n1, prsndef *n2, prsndef *n3, prsndef *n4) { prsndef *n = prsalo(ctx, 4); n->prsntyp = t; n->prsnnlf = 4; n->prsnv.prsnvn[0] = n1; n->prsnv.prsnvn[1] = n2; n->prsnv.prsnvn[2] = n3; n->prsnv.prsnvn[3] = n4; return(n); } /* build a tertiary operator node - three subnodes */ prsndef *prsnew3(prscxdef *ctx, int t, prsndef *n1, prsndef *n2, prsndef *n3) { prsndef *n = prsalo(ctx, 3); n->prsntyp = t; n->prsnnlf = 3; n->prsnv.prsnvn[0] = n1; n->prsnv.prsnvn[1] = n2; n->prsnv.prsnvn[2] = n3; return(n); } /* build a binary operator node - two subnodes */ prsndef *prsnew2(prscxdef *ctx, int t, prsndef *n1, prsndef *n2) { prsndef *n = prsalo(ctx, 2); n->prsntyp = t; n->prsnnlf = 2; n->prsnv.prsnvn[0] = n1; n->prsnv.prsnvn[1] = n2; return(n); } /* build a unary operator node - one subnode */ prsndef *prsnew1(prscxdef *ctx, int t, prsndef *n) { prsndef *newnode = prsalo(ctx, 1); newnode->prsntyp = t; newnode->prsnnlf = 1; newnode->prsnv.prsnvn[0] = n; return newnode; } /* build a new value node - no subnodes */ prsndef *prsnew0(prscxdef *ctx, tokdef *tokp) { prsndef *n = prsalo(ctx, 0); n->prsntyp = 0; /* value node */ n->prsnnlf = 0; OSCPYSTRUCT(n->prsnv.prsnvt, *tokp); return(n); } /* binary operator definition */ typedef struct prsbdef prsbdef; struct prsbdef { int *prsblst; /* list of tokens at this precedence level */ prsndef *(*prsblf)(prscxdef *ctx, prsbdef *binctx); prsndef *(*prsbrf)(prscxdef *ctx, prsbdef *binctx); prsbdef *prsbctx; /* secondary context for left & right */ int prsbrpt; /* TRUE ==> iterate right operand */ }; /* generate code for a function call */ static void prsgfn(prscxdef *ctx, prsndef *n, int argcnt); /* generate code for an argument list; returns argument count */ static int prsgargs(prscxdef *ctx, prsndef *n); /* generate code for a property lookup */ static void prsgdot(prscxdef *ctx, prsndef *n, int argcnt); /* generate code for a property lookup during speculative evaluation */ static void prsgdotspec(prscxdef *ctx, prsndef *n); /* generate code to assign an l-value */ static void prsglval(prscxdef *ctx, int typ, prsndef *n); /* Build a general binary node. The context tells us how. */ static prsndef *prsxbin(prscxdef *ctx, prsbdef *binctx); /* parse an argument list */ static prsndef *prsxarg(prscxdef *ctx); /* parse an atom */ static prsndef *prsxatm(prscxdef *ctx); /* parse a structure member expression */ static prsndef *prsxmem(prscxdef *ctx); /* parse a unary operator expression */ static prsndef *prsxun(prscxdef *ctx, prsbdef *binctx); /* parse a conditional expression */ static prsndef *prsxcnd(prscxdef *ctx, prsbdef *binctx); /* parse an object definition (superclass list is already parsed) */ static void prsobj(prscxdef *ctx, tokdef *objtok, int numsc, objnum *sclist, int classflg); /* factor */ static int prsl_fact[] = { TOKTTIMES, TOKTDIV, TOKTMOD, 0 }; static prsbdef prsb_fact = { prsl_fact, prsxun, prsxun, 0, TRUE }; /* term */ static int prsl_term[] = { TOKTPLUS, TOKTMINUS, 0 }; static prsbdef prsb_term = { prsl_term, prsxbin, prsxbin, &prsb_fact, TRUE }; /* shift */ static int prsl_shift[] = { TOKTSHL, TOKTSHR, 0 }; static prsbdef prsb_shift = { prsl_shift, prsxbin, prsxbin, &prsb_term, TRUE }; /* relational (magnitude comparison) */ static int prsl_rel[] = { TOKTGT, TOKTGE, TOKTLT, TOKTLE, 0 }; static prsbdef prsb_rel = { prsl_rel, prsxbin, prsxbin, &prsb_shift, FALSE }; /* comparison */ /* WARNING - keep prsl_cmp and prsl_cmp_C in sync with one another */ static int prsl_cmp[] = { TOKTEQ, TOKTNE, 0 }; static prsbdef prsb_cmp = { prsl_cmp, prsxbin, prsxbin, &prsb_rel, FALSE }; static int prsl_cmp_C[] = { TOKTEQEQ, TOKTNE, 0 }; static prsbdef prsb_cmp_C = { prsl_cmp_C, prsxbin, prsxbin, &prsb_rel, FALSE }; /* bitwise AND */ static int prsl_band[] = { TOKTBOR, 0 }; static prsbdef prsb_band = { prsl_band, prsxbin, prsxbin, &prsb_cmp, TRUE }; static prsbdef prsb_band_C = {prsl_band, prsxbin, prsxbin, &prsb_cmp_C, TRUE }; /* bitwise XOR */ static int prsl_xor[] = { TOKTXOR, 0 }; static prsbdef prsb_xor = { prsl_xor, prsxbin, prsxbin, &prsb_band, TRUE }; static prsbdef prsb_xor_C = { prsl_xor, prsxbin, prsxbin, &prsb_band_C, TRUE }; /* bitwise OR */ static int prsl_bor[] = { TOKTBAND, 0 }; static prsbdef prsb_bor = { prsl_bor, prsxbin, prsxbin, &prsb_xor, TRUE }; static prsbdef prsb_bor_C = { prsl_bor, prsxbin, prsxbin, &prsb_xor_C, TRUE }; /* logical AND */ static int prsl_and[] = { TOKTAND, 0 }; static prsbdef prsb_and = { prsl_and, prsxbin, prsxbin, &prsb_bor, TRUE }; static prsbdef prsb_and_C = { prsl_and, prsxbin, prsxbin, &prsb_bor_C, TRUE }; /* logical OR */ static int prsl_or[] = { TOKTOR, 0 }; static prsbdef prsb_or = { prsl_or, prsxbin, prsxbin, &prsb_and, TRUE }; static prsbdef prsb_or_C = { prsl_or, prsxbin, prsxbin, &prsb_and_C, TRUE }; /* assignment */ /* WARNING - keep prsl_asi and prsl_asi_C in sync with one another */ static int prsl_asi[] = { TOKTASSIGN, TOKTPLEQ, TOKTMINEQ, TOKTDIVEQ, TOKTTIMEQ, TOKTMODEQ, TOKTBANDEQ, TOKTBOREQ, TOKTXOREQ, TOKTSHLEQ, TOKTSHREQ, 0 }; static int prsl_asi_C[] = { TOKTEQ, TOKTPLEQ, TOKTMINEQ, TOKTDIVEQ, TOKTTIMEQ, TOKTMODEQ, TOKTBANDEQ, TOKTBOREQ, TOKTXOREQ, TOKTSHLEQ, TOKTSHREQ, 0 }; static prsbdef prsb_asi = { prsl_asi, prsxcnd, prsxbin, &prsb_asi, FALSE }; static prsbdef prsb_asi_C = { prsl_asi_C, prsxcnd, prsxbin, &prsb_asi_C, FALSE }; /* comma */ static int prsl_cma[] = { TOKTCOMMA, 0 }; static prsbdef prsb_cma = { prsl_cma, prsxbin, prsxbin, &prsb_asi, TRUE }; static prsbdef prsb_cma_C = { prsl_cma, prsxbin, prsxbin, &prsb_asi_C, TRUE }; /* determine if we're in C-mode - return true if so, false otherwise */ static int prs_cmode(prscxdef *ctx) { return (ctx->prscxtok->tokcxflg & TOKCXFCMODE) != 0; } /* build general binary node, iterating to the right if desired */ static prsndef *prsxbin(prscxdef *ctx, prsbdef *binctx) { prsndef *n; int t; int *lp; n = (*binctx->prsblf)(ctx, binctx->prsbctx); do { t = ctx->prscxtok->tokcxcur.toktyp; for (lp = binctx->prsblst ; *lp && *lp != t ; ++lp) ; if (*lp == t) { /* * if inside a list, check to see if this is a binary * operator that can also server as a unary operator -- if * so, interpret it as a unary operator and warn about it */ if (ctx->prscxflg & PRSCXFLST) { static int ambig_ops[] = { TOKTBAND, TOKTPLUS, TOKTMINUS, 0 }; static char *ambig_names[] = { "&", "+", "-" }; for (lp = ambig_ops ; *lp && *lp != t ; ++lp) ; if (*lp) { errlog1(ctx->prscxerr, ERR_AMBIGBIN, ERRTSTR, ambig_names[lp - ambig_ops]); break; } } /* skip the operator */ toknext(ctx->prscxtok); /* use C-style operators if in C mode */ if (prs_cmode(ctx)) { switch(t) { case TOKTEQEQ: t = TOKTEQ; break; case TOKTEQ: t = TOKTASSIGN; break; } } /* generate a node for the binary operator */ n = prsnew2(ctx, t, n, (*binctx->prsbrf)(ctx, binctx->prsbctx)); } else break; } while (binctx->prsbrpt); return(n); } static prsndef *prsxarg(prscxdef *ctx) { prsndef *n; int t; /* arguments are assignments: just under comma-expressions */ n = prsxbin(ctx, prs_cmode(ctx) ? &prsb_asi_C : &prsb_asi); t = ctx->prscxtok->tokcxcur.toktyp; toknext(ctx->prscxtok); switch(t) { case TOKTRPAR: return(n); case TOKTCOMMA: return(prsnew2(ctx, TOKTRPAR, n, prsxarg(ctx))); default: errsig(ctx->prscxerr, ERR_REQARG); NOTREACHEDV(prsndef *); return 0; } } static prsndef *prsxatm(prscxdef *ctx) { prsndef *n; tokdef *tok = &ctx->prscxtok->tokcxcur; tokdef newtok; prpnum pr; switch(tok->toktyp) { case TOKTPOUND: case TOKTBAND: toknext(ctx->prscxtok); if (ctx->prscxtok->tokcxcur.toktyp == TOKTSYMBOL && (ctx->prscxtok->tokcxcur.toksym.tokstyp == TOKSTFWDFN || ctx->prscxtok->tokcxcur.toksym.tokstyp == TOKSTFUNC)) { n = prsnew0(ctx, tok); toknext(ctx->prscxtok); return(n); } else { pr = prsrqpr(ctx); newtok.toktyp = TOKTPOUND; newtok.tokofs = pr; return(prsnew0(ctx, &newtok)); } case TOKTLPAR: toknext(ctx->prscxtok); n = prsxexp(ctx, FALSE); prsreq(ctx, TOKTRPAR); return(n); case TOKTLBRACK: toknext(ctx->prscxtok); return(prslst(ctx)); case TOKTNUMBER: case TOKTNIL: case TOKTTRUE: case TOKTSSTRING: case TOKTDSTRING: /* create and return a new terminal node for this item */ n = prsnew0(ctx, tok); toknext(ctx->prscxtok); return n; case TOKTSYMBOL: /* create a new terminal node for the symbol */ n = prsnew0(ctx, tok); toknext(ctx->prscxtok); /* * if it was 'inherited', and there's another symbol name * following, we have the explicit superclass notation - parse * it */ if (n->prsnv.prsnvt.toksym.tokstyp == TOKSTINHERIT && tok->toktyp == TOKTSYMBOL) { /* build a new node for the explicit superclass */ n = prsnew0(ctx, tok); /* build a containing node for the inheritance operator */ n = prsnew1(ctx, TOKTEXPINH, n); /* skip the superclass name token */ toknext(ctx->prscxtok); } /* return the new node */ return n; default: errsig(ctx->prscxerr, ERR_REQOPN); return 0; NOTREACHEDV(prsndef *); } } static prsndef *prsxmem(prscxdef *ctx) { prsndef *n1, *n2; tokdef *tok; tokdef newtok; prpnum prop; n1 = prsxatm(ctx); for ( ;; ) { tok = &ctx->prscxtok->tokcxcur; switch(tok->toktyp) { case TOKTDOT: if (toknext(ctx->prscxtok) == TOKTLPAR) { toknext(ctx->prscxtok); n2 = prsxexp(ctx, FALSE); prsreq(ctx, TOKTRPAR); } else if (ctx->prscxtok->tokcxcur.toktyp == TOKTSYMBOL && ctx->prscxtok->tokcxcur.toksym.tokstyp == TOKSTLOCAL) { n2 = prsnew0(ctx, &ctx->prscxtok->tokcxcur); toknext(ctx->prscxtok); } else { prop = prsrqpr(ctx); newtok.toktyp = TOKTPOUND; newtok.tokofs = prop; n2 = prsnew0(ctx, &newtok); } /* see if there's an argument list for the method */ if (ctx->prscxtok->tokcxcur.toktyp == TOKTLPAR) { toknext(ctx->prscxtok); n1 = prsnew3(ctx, TOKTDOT, n1, n2, prsxarg(ctx)); } else n1 = prsnew2(ctx, TOKTDOT, n1, n2); break; case TOKTLBRACK: if (ctx->prscxflg & PRSCXFLST) return(n1); toknext(ctx->prscxtok); n2 = prsxexp(ctx, FALSE); n1 = prsnew2(ctx, TOKTLBRACK, n1, n2); prsreq(ctx, TOKTRBRACK); break; case TOKTLPAR: if (toknext(ctx->prscxtok) == TOKTRPAR) { toknext(ctx->prscxtok); n1 = prsnew1(ctx, TOKTLPAR, n1); } else n1 = prsnew2(ctx, TOKTLPAR, n1, prsxarg(ctx)); break; case TOKTINC: case TOKTDEC: n1 = prsnew1(ctx, tok->toktyp + 1, n1); toknext(ctx->prscxtok); return(n1); default: return(n1); } } } /* parse a unary operator expression */ static prsndef *prsxun(prscxdef *ctx, prsbdef *binctx) { int t; VARUSED(binctx); t = ctx->prscxtok->tokcxcur.toktyp; if (t == TOKTPLUS || t == TOKTMINUS || t == TOKTNOT || t == TOKTINC || t == TOKTDEC || t == TOKTTILDE || t == TOKTDELETE) { toknext(ctx->prscxtok); return(prsnew1(ctx, t, prsxun(ctx, (prsbdef *)0))); } else if (t == TOKTNEW) { if (toknext(ctx->prscxtok) == TOKTOBJECT) { tokdef tok; toknext(ctx->prscxtok); tok.toktyp = TOKTNEW; return prsnew0(ctx, &tok); } else return prsnew1(ctx, t, prsxun(ctx, (prsbdef *)0)); } return(prsxmem(ctx)); } /* parse a ternary conditional expression */ static prsndef *prsxcnd(prscxdef *ctx, prsbdef *binctx) { prsndef *n1, *n2; VARUSED(binctx); n1 = prsxbin(ctx, prs_cmode(ctx) ? &prsb_or_C : &prsb_or); for (;;) { if (ctx->prscxtok->tokcxcur.toktyp != TOKTQUESTION) break; toknext(ctx->prscxtok); n2 = prsxexp(ctx, FALSE); prsreq(ctx, TOKTCOLON); n1 = prsnew3(ctx, TOKTQUESTION, n1, n2, prsxbin(ctx, prs_cmode(ctx) ? &prsb_asi_C : &prsb_asi)); } return(n1); } /* parse an expression (called recursively during expression parsing) */ static prsndef *prsxexp(prscxdef *ctx, int inlist) { ushort oldflg = ctx->prscxflg; prsndef *retval; if (inlist) { ctx->prscxflg |= PRSCXFLST; retval = prsxbin(ctx, (prs_cmode(ctx) ? &prsb_asi_C : &prsb_asi)); } else { ctx->prscxflg &= ~PRSCXFLST; retval = prsxbin(ctx, prs_cmode(ctx) ? &prsb_cma_C : &prsb_cma); } ctx->prscxflg = oldflg; return(retval); } /* fold constants in a parse tree */ static prsndef *prsfold(prscxdef *ctx, prsndef *node) { int i; prsndef **n; int can_fold; int typ; tokdef *tok1, *tok2; int typ1, typ2; long val1, val2; prsndef *ncur; can_fold = TRUE; /* assume we can do some folding */ /* if this is a list-construction node, special handling is needed */ if (node->prsntyp == TOKTRBRACK) { for (i=0, ncur = node ; ncur ; ++i, ncur = ncur->prsnv.prsnvn[1]) { ncur->prsnv.prsnvn[0] = prsfold(ctx, ncur->prsnv.prsnvn[0]); typ = TOKTINVALID; if (ncur->prsnv.prsnvn[0]->prsnnlf != 0 || ((typ = ncur->prsnv.prsnvn[0]->prsnv.prsnvt.toktyp) != TOKTNUMBER && typ != TOKTSSTRING && typ != TOKTLIST && typ != TOKTNIL && typ != TOKTTRUE && typ != TOKTPOUND && typ != TOKTSYMBOL)) can_fold = FALSE; /* not constant - can't fold */ if (typ == TOKTSYMBOL && (ncur->prsnv.prsnvn[0]->prsnv.prsnvt.toksym.tokstyp == TOKSTLOCAL || ncur->prsnv.prsnvn[0]->prsnv.prsnvt.toksym.tokstyp == TOKSTSELF)) can_fold = FALSE; if (ncur->prsnv.prsnvn[0]->prsnnlf == 0 && typ == TOKTSYMBOL) prsdef(ctx, &ncur->prsnv.prsnvn[0]->prsnv.prsnvt, TOKSTFWDOBJ); } if (can_fold) { tokdef t; emtlidef *lst; prsndef *retval; emtledef *ele; lst = (emtlidef *)prsbalo(ctx, (uint)(sizeof(emtldef) + (i - 1) * sizeof(emtledef))); t.toktyp = TOKTLIST; t.tokofs = ((uchar *)lst) - &ctx->prscxpool[0]; retval = prsnew0(ctx, &t); lst->emtlicnt = i; for (ele = &lst->emtliele[0] ; node ; node = node->prsnv.prsnvn[1], ++ele) { ncur = node->prsnv.prsnvn[0]; switch(ele->emtletyp = ncur->prsnv.prsnvt.toktyp) { case TOKTLIST: case TOKTSSTRING: case TOKTPOUND: ele->emtleval = ncur->prsnv.prsnvt.tokofs; break; case TOKTSYMBOL: switch(ncur->prsnv.prsnvt.toksym.tokstyp) { case TOKSTFUNC: case TOKSTFWDFN: ele->emtletyp = TOKTFUNCTION; break; case TOKSTOBJ: case TOKSTFWDOBJ: ele->emtletyp = TOKTOBJECT; break; case TOKSTPROP: ele->emtletyp = TOKTDOT; break; case TOKSTPROPSPEC: ele->emtletyp = TOKTDOT; break; default: errsig(ctx->prscxerr, ERR_INVLSTE); } ele->emtleval = ncur->prsnv.prsnvt.toksym.toksval; break; default: ele->emtleval = ncur->prsnv.prsnvt.tokval; break; } } node = retval; } return(node); } /* * If we have sub-nodes, try to fold them. If they fold down to * leaf nodes, we can try to apply the expression to them. */ for (i = node->prsnnlf, n = &node->prsnv.prsnvn[0] ; i ; ++n, --i) { *n = prsfold(ctx, *n); if ((*n)->prsnnlf != 0 || ((typ = (*n)->prsnv.prsnvt.toktyp) != TOKTNUMBER && typ != TOKTSSTRING && typ != TOKTLIST && typ != TOKTNIL && typ != TOKTTRUE && typ != TOKTPOUND)) can_fold = FALSE; /* not constant - can't fold */ } /* if at a leaf, or subnodes are non-constant, can't fold anything */ if (node->prsnnlf == 0 || !can_fold) return(node); switch(node->prsnnlf) { case 1: tok1 = &node->prsnv.prsnvn[0]->prsnv.prsnvt; typ1 = tok1->toktyp; val1 = tok1->tokval; switch(node->prsntyp) { case TOKTPLUS: break; case TOKTMINUS: if (typ1 != TOKTNUMBER) errsig(ctx->prscxerr, ERR_INVOP); tok1->tokval = -val1; break; case TOKTNOT: if (!prsvlog(typ1)) errsig(ctx->prscxerr, ERR_INVOP); tok1->toktyp = (prs2log(typ1, val1) == TOKTNIL ? TOKTTRUE : TOKTNIL); break; case TOKTTILDE: if (typ1 != TOKTNUMBER) errsig(ctx->prscxerr, ERR_INVOP); tok1->tokval = ~val1; break; default: return(node); } break; case 2: tok1 = &node->prsnv.prsnvn[0]->prsnv.prsnvt; tok2 = &node->prsnv.prsnvn[1]->prsnv.prsnvt; val1 = tok1->tokval; val2 = tok2->tokval; typ1 = tok1->toktyp; typ2 = tok2->toktyp; switch(node->prsntyp) { case TOKTOR: case TOKTAND: if (!prsvlog(typ1) || !prsvlog(typ2)) errsig(ctx->prscxerr, ERR_INVOP); typ1 = (prs2log(typ1, val1) == TOKTTRUE); typ2 = (prs2log(typ2, val2) == TOKTTRUE); if (node->prsntyp == TOKTAND) typ1 = typ1 && typ2; else typ1 = typ1 || typ2; tok1->toktyp = (typ1 ? TOKTTRUE : TOKTNIL); break; case TOKTPLUS: case TOKTMINUS: if (typ1 == TOKTSSTRING || typ1 == TOKTLIST || typ2 == TOKTLIST) return(node); /* FALLTHROUGH */ case TOKTEQ: case TOKTNE: case TOKTGT: case TOKTGE: case TOKTLT: case TOKTLE: if (typ1 != TOKTNUMBER || typ2 != TOKTNUMBER) return(node); /* FALLTHROUGH */ case TOKTXOR: if ((typ1 == TOKTTRUE || typ1 == TOKTNIL) && (typ2 == TOKTTRUE || typ2 == TOKTNIL)) { int a, b; a = (typ1 == TOKTTRUE ? 1 : 0); b = (typ2 == TOKTTRUE ? 1 : 0); tok1->toktyp = ((a ^ b) ? TOKTTRUE : TOKTNIL); break; } /* FALLTHROUGH */ case TOKTDIV: case TOKTTIMES: case TOKTMOD: case TOKTSHL: case TOKTSHR: if (typ1 != TOKTNUMBER || typ2 != TOKTNUMBER) errsig(ctx->prscxerr, ERR_INVOP); switch(node->prsntyp) { case TOKTPLUS: val1 += val2; break; case TOKTMINUS: val1 -= val2; break; case TOKTTIMES: val1 *= val2; break; case TOKTDIV: if (val2 == 0) errsig(ctx->prscxerr, ERR_DIVZERO); val1 /= val2; break; case TOKTMOD: if (val2 == 0) errsig(ctx->prscxerr, ERR_DIVZERO); val1 %= val2; break; case TOKTXOR: val1 ^= val2; break; case TOKTSHL: val1 <<= val2; break; case TOKTSHR: val1 >>= val2; break; case TOKTEQ: typ1 = (val1 == val2 ? TOKTTRUE : TOKTNIL); break; case TOKTNE: typ1 = (val1 != val2 ? TOKTTRUE : TOKTNIL); break; case TOKTGT: typ1 = (val1 > val2 ? TOKTTRUE : TOKTNIL); break; case TOKTLT: typ1 = (val1 < val2 ? TOKTTRUE : TOKTNIL); break; case TOKTLE: typ1 = (val1 <= val2 ? TOKTTRUE : TOKTNIL); break; case TOKTGE: typ1 = (val1 >= val2 ? TOKTTRUE : TOKTNIL); break; default: return(node); } tok1->toktyp = typ1; tok1->tokval = val1; break; default: return(node); } break; case 3: tok1 = &node->prsnv.prsnvn[0]->prsnv.prsnvt; if (!prsvlog(tok1->toktyp)) errsig(ctx->prscxerr, ERR_INVOP); if (prs2log(tok1->toktyp, tok1->tokval) == TOKTTRUE) return(node->prsnv.prsnvn[1]); else return(node->prsnv.prsnvn[2]); default: return(node); } /* return the first sub-node, which has the folded value */ return(node->prsnv.prsnvn[0]); } /* start parsing an initializer expression (resets node pool) */ prsndef *prsxini(prscxdef *ctx) { prsndef *node; node = prsxbin(ctx, (prs_cmode(ctx) ? &prsb_asi_C : &prsb_asi)); return(prsfold(ctx, node)); /* fold constants */ } /* start parsing an expression (folds constants) */ prsndef *prsexpr(prscxdef *ctx) { return(prsfold(ctx, prsxbin(ctx, prs_cmode(ctx) ? &prsb_cma_C : &prsb_cma))); } /* parse an expression and generate code for it immediately */ void prsxgen(prscxdef *ctx) { prsndef *exprnode; exprnode = prsexpr(ctx); prsgexp(ctx, exprnode); prsrstn(ctx); } /* * Check a parse tree for speculative execution problems. */ static void prs_check_spec(prscxdef *ctx, prsndef *expr) { int i; /* * check the type of this node - if it's an assignment or a function * call, prohibit it */ if (expr->prsnnlf == 0) { /* value node - check for a symbol */ if (expr->prsnv.prsnvt.toktyp == TOKTSYMBOL) { switch(expr->prsnv.prsnvt.toksym.tokstyp) { case TOKSTPROP: /* * convert this to a speculative evaluation property, so * that the emitter knows to emit the correct opcode for * it */ expr->prsnv.prsnvt.toksym.tokstyp = TOKSTPROPSPEC; break; case TOKSTBIFN: case TOKSTINHERIT: case TOKSTEXTERN: /* don't allow calls */ errsig(ctx->prscxerr, ERR_BADSPECEXPR); default: /* others are okay */ break; } } else if (expr->prsnv.prsnvt.toktyp == TOKTDSTRING) { /* don't allow double-quoted strings in speculative evaluation */ errsig(ctx->prscxerr, ERR_BADSPECEXPR); } } else { switch(expr->prsntyp) { case TOKTINC: case TOKTDEC: case TOKTPOSTINC: case TOKTPOSTDEC: case TOKTNEW: case TOKTDELETE: case TOKTLPAR: case TOKTASSIGN: case TOKTPLEQ: case TOKTMINEQ: case TOKTDIVEQ: case TOKTMODEQ: case TOKTTIMEQ: case TOKTBANDEQ: case TOKTBOREQ: case TOKTXOREQ: case TOKTSHLEQ: case TOKTSHREQ: case TOKTRPAR: /* assignments and function calls are all prohibited */ errsig(ctx->prscxerr, ERR_BADSPECEXPR); case TOKTDOT: /* property lookup - only allow no argument version */ if (expr->prsnnlf == 3) errsig(ctx->prscxerr, ERR_BADSPECEXPR); /* replace with data-only (no method call) version */ expr->prsntyp = TOKTDOTSPEC; break; default: /* other expressions are acceptable */ break; } /* go through all subnodes of this node */ for (i = 0 ; i < expr->prsnnlf ; ++i) prs_check_spec(ctx, expr->prsnv.prsnvn[i]); } } /* * Parse an expression and generate code for it using speculative * evaluation rules */ void prsxgen_spec(prscxdef *ctx) { prsndef *exprnode; /* parse the expression */ exprnode = prsexpr(ctx); /* check for speculative execution problems */ prs_check_spec(ctx, exprnode); /* generate the expression, then reset the parse node pool */ prsgexp(ctx, exprnode); prsrstn(ctx); } /* * Parse an expression and generate code, checking the expression for a * possibly incorrect assigment if in C mode. */ void prsxgen_pia(prscxdef *ctx) { prsndef *exprnode; /* parse the expression */ exprnode = prsexpr(ctx); /* check for a possibly incorrect assignment if in C mode */ if ((ctx->prscxtok->tokcxflg & TOKCXFCMODE) && exprnode && exprnode->prsntyp == TOKTASSIGN) errlog(ctx->prscxerr, ERR_PIA); /* generate the expression, and reset the parse node pool */ prsgexp(ctx, exprnode); prsrstn(ctx); } /* opcodes for general binary operators */ uchar prsbopl[] = { OPCADD, OPCSUB, OPCDIV, OPCMUL, 0, OPCEQ, OPCNE, OPCGT, OPCGE, OPCLT, OPCLE, OPCMOD, OPCBAND, OPCBOR, OPCXOR, OPCSHL, OPCSHR }; /* descend an expression tree and generate code for the expression */ void prsgexp(prscxdef *ctx, prsndef *n) { int cnt; switch(n->prsnnlf) { case 0: prsdef(ctx, &n->prsnv.prsnvt, TOKSTFWDOBJ); emtval(ctx->prscxemt, &n->prsnv.prsnvt, ctx->prscxpool); break; case 1: switch(n->prsntyp) { case TOKTINC: case TOKTDEC: case TOKTPOSTINC: case TOKTPOSTDEC: prsglval(ctx, n->prsntyp, n->prsnv.prsnvn[0]); break; case TOKTPLUS: case TOKTMINUS: case TOKTNOT: case TOKTTILDE: case TOKTNEW: case TOKTDELETE: prsgexp(ctx, n->prsnv.prsnvn[0]); if (n->prsntyp != TOKTPLUS) emtop(ctx->prscxemt, n->prsntyp == TOKTMINUS ? OPCNEG : n->prsntyp == TOKTTILDE ? OPCBNOT : n->prsntyp == TOKTNEW ? OPCNEW : n->prsntyp == TOKTDELETE ? OPCDELETE : OPCNOT); break; case TOKTLPAR: prsgfn(ctx, n->prsnv.prsnvn[0], 0); break; default: errsig(ctx->prscxerr, ERR_REQUNO); } break; case 2: switch(n->prsntyp) { case TOKTAND: case TOKTOR: { uint lab; lab = emtglbl(ctx->prscxemt); prsgexp(ctx, n->prsnv.prsnvn[0]); emtjmp(ctx->prscxemt, (uchar)(n->prsntyp == TOKTAND ? OPCJSF : OPCJST), lab); prsgexp(ctx, n->prsnv.prsnvn[1]); emtslbl(ctx->prscxemt, &lab, TRUE); break; } case TOKTPLUS: case TOKTMINUS: case TOKTDIV: case TOKTMOD: case TOKTTIMES: case TOKTEQ: case TOKTNE: case TOKTGT: case TOKTGE: case TOKTLT: case TOKTLE: case TOKTBAND: case TOKTBOR: case TOKTXOR: case TOKTSHL: case TOKTSHR: prsgexp(ctx, n->prsnv.prsnvn[0]); prsgexp(ctx, n->prsnv.prsnvn[1]); emtop(ctx->prscxemt, prsbopl[n->prsntyp - TOKTPLUS]); break; case TOKTCOMMA: prsgexp(ctx, n->prsnv.prsnvn[0]); prsgexp(ctx, n->prsnv.prsnvn[1]); break; case TOKTLPAR: /* function call with arguments */ cnt = prsgargs(ctx, n->prsnv.prsnvn[1]); /* push arguments */ prsgfn(ctx, n->prsnv.prsnvn[0], cnt); break; case TOKTASSIGN: case TOKTPLEQ: case TOKTMINEQ: case TOKTDIVEQ: case TOKTMODEQ: case TOKTTIMEQ: case TOKTBANDEQ: case TOKTBOREQ: case TOKTXOREQ: case TOKTSHLEQ: case TOKTSHREQ: prsgexp(ctx, n->prsnv.prsnvn[1]); /* eval right side of := */ prsglval(ctx, n->prsntyp, n->prsnv.prsnvn[0]); break; case TOKTDOT: prsgdot(ctx, n, 0); break; case TOKTDOTSPEC: prsgdotspec(ctx, n); break; case TOKTLBRACK: prsgexp(ctx, n->prsnv.prsnvn[0]); /* push list being indexed */ prsgexp(ctx, n->prsnv.prsnvn[1]); /* push the index value */ emtop(ctx->prscxemt, OPCINDEX); break; case TOKTRBRACK: { prsndef *nsub; uint cnt; for (cnt = 0, nsub = n ; nsub ; ++cnt, nsub = nsub->prsnv.prsnvn[1]) prsgexp(ctx, nsub->prsnv.prsnvn[0]); emtop(ctx->prscxemt, OPCCONS); /* construction operator */ emtint2(ctx->prscxemt, cnt); break; } default: errsig(ctx->prscxerr, ERR_INVBIN); } break; case 3: switch(n->prsntyp) { case TOKTQUESTION: { uint lfalse; /* label for FALSE branch */ uint ldone; /* label for end of condition */ prsgexp(ctx, n->prsnv.prsnvn[0]); /* evaluate condition */ lfalse = emtglbl(ctx->prscxemt); emtjmp(ctx->prscxemt, OPCJF, lfalse); prsgexp(ctx, n->prsnv.prsnvn[1]); /* eval TRUE expr */ ldone = emtglbl(ctx->prscxemt); emtjmp(ctx->prscxemt, OPCJMP, ldone); emtslbl(ctx->prscxemt, &lfalse, TRUE); prsgexp(ctx, n->prsnv.prsnvn[2]); /* eval FALSE branch */ emtslbl(ctx->prscxemt, &ldone, TRUE); break; } case TOKTDOT: cnt = prsgargs(ctx, n->prsnv.prsnvn[2]); prsgdot(ctx, n, cnt); break; } break; } } /* generate code for initializer */ void prsgini(prscxdef *ctx, prsndef *node, uint curfr) { int lcl; /* descend tree - always generate in order specified by user */ if (node->prsnv.prsnvn[2]) prsgini(ctx, node->prsnv.prsnvn[2], curfr); /* generate OPCLINE if one was specified */ if (node->prsnnlf == 4) { uchar *p; uint oldofs; /* tell the line source about the OPCLINE instruction */ dbgclin(ctx->prscxtok, ctx->prscxemt->emtcxobj, (uint)ctx->prscxemt->emtcxofs); /* now actually emit the OPCLINE instruction */ oldofs = ctx->prscxemt->emtcxofs; p = ctx->prscxpool + node->prsnv.prsnvn[3]->prsnv.prsnvt.tokofs; emtmem(ctx->prscxemt, p, (uint)(*(p + 1) + 1)); /* fix it up with the correct frame */ emtint2at(ctx->prscxemt, curfr, oldofs + 2); } prsgexp(ctx, node->prsnv.prsnvn[0]); emtop(ctx->prscxemt, OPCSETLCL); lcl = node->prsnv.prsnvn[1]->prsnv.prsnvt.tokval; emtint2(ctx->prscxemt, lcl); } /* generate code for a function all */ static void prsgfn(prscxdef *ctx, prsndef *n, int cnt) { if (n->prsnnlf == 0 && n->prsnv.prsnvt.toktyp == TOKTSYMBOL && (n->prsnv.prsnvt.toksym.tokstyp == TOKSTUNK || n->prsnv.prsnvt.toksym.tokstyp != TOKSTLOCAL)) { tokdef *t = &n->prsnv.prsnvt; /* if symbol is undefined, add to symbol table as a new function */ prsdef(ctx, t, TOKSTFWDFN); /* non-local-var symbol: interpret as a function */ switch(t->toksym.tokstyp) { case TOKSTFUNC: case TOKSTFWDFN: emtop(ctx->prscxemt, OPCCALL); break; case TOKSTEXTERN: emtop(ctx->prscxemt, OPCCALLEXT); break; case TOKSTBIFN: emtop(ctx->prscxemt, OPCBUILTIN); break; default: errlog(ctx->prscxerr, ERR_REQFCN); } emtbyte(ctx->prscxemt, cnt); /* emit argument count byte */ emtint2(ctx->prscxemt, t->toksym.toksval); } else { /* expression - evaluate, and assume it yields a function pointer */ prsgexp(ctx, n); emtop(ctx->prscxemt, OPCPTRCALL); emtbyte(ctx->prscxemt, cnt); /* argument count byte */ } } /* generate code for a property lookup */ static void prsgdot(prscxdef *ctx, prsndef *n, int cnt) { prsndef *n0 = n->prsnv.prsnvn[0]; prsndef *n1 = n->prsnv.prsnvn[1]; int inh; /* flag: "inherited.prop" */ int slf; /* flag: "self.prop" */ int objflg; /* flag: LHS of dot is constant object value */ int exp_inh; /* flag: "inherited superclass.prop" */ uchar reg_op; /* operator to use for regular property */ uchar ptr_op; /* operator to use for property pointer */ objnum exp_inh_obj; /* explicit superclass object to inherit from */ /* presume we'll have a regular object expression for the LHS */ inh = exp_inh = slf = objflg = FALSE; /* set up the default opcodes for ordinary object expressions */ reg_op = OPCGETP; ptr_op = OPCPTRGETP; /* check for special left-hand-side values */ if (n0->prsnnlf == 0 && n0->prsnv.prsnvt.toktyp == TOKTSYMBOL) { switch(n0->prsnv.prsnvt.toksym.tokstyp) { case TOKSTINHERIT: inh = TRUE; reg_op = OPCINHERIT; ptr_op = OPCPTRINH; break; case TOKSTSELF: if (ctx->prscxflg & PRSCXFFUNC) errlog(ctx->prscxerr, ERR_NOSELF); slf = TRUE; reg_op = OPCGETPSELF; ptr_op = OPCGETPPTRSELF; break; case TOKSTOBJ: case TOKSTFWDOBJ: objflg = TRUE; reg_op = OPCGETPOBJ; break; } } else if (n0->prsnnlf == 1 && n0->prsntyp == TOKTEXPINH) { tokdef *sc_tok; /* * explicit superclass inheritance - this is inheritance, but * with an explicit superclass value */ inh = TRUE; exp_inh = TRUE; reg_op = OPCEXPINH; ptr_op = OPCEXPINHPTR; /* get the explicit superclass subnode */ sc_tok = &n0->prsnv.prsnvn[0]->prsnv.prsnvt; /* make sure the superclass is an object */ prsdef(ctx, sc_tok, TOKSTFWDOBJ); /* get the explicit superclass - it's the subnode */ exp_inh_obj = sc_tok->toksym.toksval; } /* * push object whose property we're getting unless inheriting or * getting a property of 'self' or of a fixed object (in which cases * the object is implicit in the opcode, rather than being on the * stack) */ if (!inh && !slf && !objflg) prsgexp(ctx, n0); if (n1->prsnnlf == 0 && n1->prsnv.prsnvt.toktyp == TOKTPOUND) { /* RHS is a property symbol - do a simple GETP (or INHERIT) */ prpnum prop = n1->prsnv.prsnvt.tokofs; /* * emit the opcode and argument count - use the regular * (non-pointer) opcode, since we have a regular property value */ emtop(ctx->prscxemt, reg_op); emtbyte(ctx->prscxemt, cnt); /* if we are encoding the object in the instruction, emit it now */ if (objflg) emtint2(ctx->prscxemt, n0->prsnv.prsnvt.toksym.toksval); /* add the property value */ emtint2(ctx->prscxemt, prop); /* * if this is an "inherited" with an explicit superclass, add * the explicit superclass value */ if (exp_inh) emtint2(ctx->prscxemt, exp_inh_obj); } else { /* * evaluate the LHS unconditionally, even if it's just a fixed * object, since we have no special instruction that encodes an * object value in a pointer property evaluation */ if (objflg) prsgexp(ctx, n0); /* RHS is an expression - evaluate it */ prsgexp(ctx, n1); /* * emit the opcode and the byte count - use the pointer opcode, * since we have a property pointer */ emtop(ctx->prscxemt, ptr_op); emtbyte(ctx->prscxemt, cnt); /* * if this is an "inherited" with an explicit superclass, add * the explicit superclass value */ if (exp_inh) emtint2(ctx->prscxemt, exp_inh_obj); } } /* generate code for a property lookup during speculative evaluation */ static void prsgdotspec(prscxdef *ctx, prsndef *n) { prsndef *n0 = n->prsnv.prsnvn[0]; prsndef *n1 = n->prsnv.prsnvn[1]; /* push object whose property we're getting */ prsgexp(ctx, n0); if (n1->prsnnlf == 0 && n1->prsnv.prsnvt.toktyp == TOKTPOUND) { /* RHS is a property symbol - do a simple GETP (or INHERIT) */ prpnum prop = n1->prsnv.prsnvt.tokofs; /* push the data property */ emtop(ctx->prscxemt, OPCGETPDATA); emtint2(ctx->prscxemt, prop); } else { /* RHS is an expression - evaluate it */ prsgexp(ctx, n1); /* get the data property */ emtop(ctx->prscxemt, OPCPTRGETPDATA); } } /* token -> assignment type field conversions */ static uchar prsglcvt[] = { OPCASIINC | OPCASIPRE, /* pre ++ */ OPCASIINC | OPCASIPOST, /* post ++ */ OPCASIDEC | OPCASIPRE, /* pre -- */ OPCASIDEC | OPCASIPOST, /* post ++ */ OPCASIADD, /* += */ OPCASISUB, /* -= */ OPCASIDIV, /* /= */ OPCASIMUL, /* *= */ OPCASIDIR /* := */ }; /* extended assignment type field conversions */ static uchar prsglcvt_ext[] = { OPCASIMOD, /* %= */ OPCASIBAND, /* &= */ OPCASIBOR, /* |= */ OPCASIXOR, /* ^= */ OPCASISHL, /* <<= */ OPCASISHR /* >>= */ }; /* generate code for an l-value */ static void prsglval(prscxdef *ctx, int typ, prsndef *n) { uchar op; uchar op2; if (typ - TOKTINC < sizeof(prsglcvt)/sizeof(prsglcvt[0])) { op = OPCASI_MASK | prsglcvt[typ - TOKTINC]; /* form basic op code */ op2 = 0; } else { op = OPCASI_MASK | OPCASIEXT; op2 = prsglcvt_ext[typ - TOKTMODEQ]; } switch(n->prsnnlf) { case 0: { int lclnum; int styp; /* DON'T assume this is a 'self.' property if unknown */ /* DON'T prsdef(ctx, &n->prsnv.prsnvt, TOKSTPROP); */ if (n->prsnv.prsnvt.toktyp == TOKTSYMBOL) { if (ctx->prscxflg & PRSCXFWTCH) errsig(ctx->prscxerr, ERR_WTCHLCL); if ((styp = n->prsnv.prsnvt.toksym.tokstyp) == TOKSTLOCAL) { emtop(ctx->prscxemt, op | OPCASILCL); if (op2) emtop(ctx->prscxemt, op2); lclnum = n->prsnv.prsnvt.toksym.toksval; emtint2(ctx->prscxemt, lclnum); } else if (styp == TOKSTPROP) { if (ctx->prscxflg & PRSCXFFUNC) errlog(ctx->prscxerr, ERR_NOSELF); emtop(ctx->prscxemt, OPCPUSHSELF); emtop(ctx->prscxemt, op | OPCASIPRP); if (op2) emtop(ctx->prscxemt, op2); lclnum = n->prsnv.prsnvt.toksym.toksval; emtint2(ctx->prscxemt, lclnum); } else if (styp == TOKSTPROPSPEC) errsig(ctx->prscxerr, ERR_BADSPECEXPR); else errlog(ctx->prscxerr, ERR_INVASI); } else errlog(ctx->prscxerr, ERR_INVASI); } break; case 2: switch(n->prsntyp) { case TOKTDOT: { prsndef *n1 = n->prsnv.prsnvn[1]; int base_opc; int direct_prop; prpnum prop; /* generate the expression providing the object */ prsgexp(ctx, n->prsnv.prsnvn[0]); /* determine the correct base opcode */ if (n1->prsnnlf == 0 && n1->prsnv.prsnvt.toktyp == TOKTPOUND) { /* simple property assignment */ direct_prop = TRUE; base_opc = OPCASIPRP; /* remember the property id */ prop = n1->prsnv.prsnvt.tokofs; } else { /* assignment through property pointer */ direct_prop = FALSE; base_opc = OPCASIPRPPTR; /* generate the property pointer expression */ prsgexp(ctx, n1); } /* generate the appropriate property assignment opcode */ emtop(ctx->prscxemt, op | base_opc); if (op2) emtop(ctx->prscxemt, op2); /* if it's a simple assignment, add the property code */ if (direct_prop) emtint2(ctx->prscxemt, prop); break; } case TOKTLBRACK: prsgexp(ctx, n->prsnv.prsnvn[0]); /* evaluate list */ prsgexp(ctx, n->prsnv.prsnvn[1]); /* evaluate index */ emtop(ctx->prscxemt, op | OPCASIIND); if (op2) emtop(ctx->prscxemt, op2); prsglval(ctx, TOKTASSIGN, n->prsnv.prsnvn[0]); /* set list */ break; default: errlog(ctx->prscxerr, ERR_INVASI); } break; default: errlog(ctx->prscxerr, ERR_INVASI); } } /* generate code for an argument list; returns argument count */ static int prsgargs(prscxdef *ctx, prsndef *n) { int cnt = 0; if (n->prsnnlf == 2 && n->prsntyp == TOKTRPAR) { cnt = prsgargs(ctx, n->prsnv.prsnvn[1]); prsgexp(ctx, n->prsnv.prsnvn[0]); } else prsgexp(ctx, n); return(cnt + 1); }