cfad47cfa3/t2compiler/tads2/prs.c
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 54.9 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers#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 <string.h>
#include <assert.h>
#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 = "<unknown token>";
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);
}
|