Files
2026-02-02 04:50:13 +01:00

1502 lines
37 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/tads/tads2/tokenizer.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/memory_cache_heap.h"
#include "glk/tads/tads2/os.h"
#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* special temporary buffers for <<expr>> macro expansion */
static char tokmac1[] = ",say((";
static char tokmac1s[] = "(say((";
static char tokmac2[] = "),nil),\"";
static char tokmac3[] = "),nil))";
static char tokmac4[] = ")";
/* forward definition of static functions */
static int tokdfhsh(const char *sym, int len);
/* find a #define symbol */
static tokdfdef *tok_find_define(tokcxdef *ctx, const char *sym, int len)
{
int hsh;
tokdfdef *df;
/* find the appropriate chain the hash table */
hsh = tokdfhsh(sym, len);
/* search the chain for this symbol */
for (df = ctx->tokcxdf[hsh] ; df ; df = df->nxt)
{
/* if this one matches, return it */
if (df->len == len && !memcmp(df->nm, sym, (size_t)len))
{
/* fix it up if it's the special __FILE__ or __LINE__ symbol */
if (len == 8)
{
if (!memcmp(sym, "__FILE__", (size_t)8))
{
size_t elen;
/*
* put in the opening single quote, since we want
* the expanded result to be a string
*/
df->expan[0] = '\'';
/* get the name */
linnam(ctx->tokcxlin, df->expan+1);
/* get the length, and add the closing quote */
elen = strlen(df->expan);
df->expan[elen] = '\'';
/*
* set the length of the expansion, including the
* quotes (the first quote was measured in the
* length originally, but the second quote hasn't
* been counted yet, so add one to our original
* length)
*/
df->explen = (int)elen + 1;
/* if the expansion is too long, it's an error */
if (df->explen >= OSFNMAX)
errsig(ctx->tokcxerr, ERR_LONG_FILE_MACRO);
}
else if (!memcmp(sym, "__LINE__", (size_t)8))
{
ulong l;
/* get the line number */
l = linlnum(ctx->tokcxlin);
/* convert it to a textual format for the expansion */
Common::sprintf_s(df->expan, "%lu", l);
/* set the expanded value's length */
df->explen = strlen(df->expan);
/* make sure the expansion isn't too long */
if (df->explen >= 40)
errsig(ctx->tokcxerr, ERR_LONG_LINE_MACRO);
}
}
/* return it */
return df;
}
}
/* didn't find anything */
return nullptr;
}
/*
* Write preprocessor state to a file
*/
void tok_write_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec)
{
int i;
tokdfdef **dfp;
tokdfdef *df;
char buf[4];
/* write each element of the hash chains */
for (i = TOKDFHSHSIZ, dfp = ctx->tokcxdf ; i ; ++dfp, --i)
{
/* write each entry in this hash chain */
for (df = *dfp ; df ; df = df->nxt)
{
/* write this entry */
oswp2(buf, df->len);
oswp2(buf + 2, df->explen);
if (osfwb(fp, buf, 4)
|| osfwb(fp, df->nm, df->len)
|| (df->explen != 0 && osfwb(fp, df->expan, df->explen)))
errsig(ec, ERR_WRTGAM);
}
/* write a zero-length entry to indicate the end of this chain */
oswp2(buf, 0);
if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
}
}
/* compute a #define symbol's hash value */
static int tokdfhsh(const char *sym, int len)
{
uint hsh;
for (hsh = 0 ; len ; ++sym, --len)
hsh = (hsh + *sym) & TOKDFHSHMASK;
return hsh;
}
/* convert a #define symbol to lower case if folding case */
static const char *tok_casefold_defsym(tokcxdef *ctx, char *outbuf,
const char *src, int len)
{
if (ctx->tokcxflg & TOKCXCASEFOLD)
{
char *dst;
int rem;
/* make a lower-case copy of the symbol */
rem = (len > TOKNAMMAX ? TOKNAMMAX : len);
for (dst = outbuf ; rem > 0 ; ++dst, ++src, --rem)
*dst = (Common::isUpper((uchar)*src) ? Common::isLower((uchar)*src) : *src);
/* use the lower-case copy instead of the original */
return outbuf;
}
else
{
/* return the original unchanged */
return src;
}
}
/*
* convert a token to lower-case if we're folding case
*/
void tok_case_fold(tokcxdef *ctx, tokdef *tok)
{
/* if we're in case-insensitive mode, convert the token to lower-case */
if (ctx->tokcxflg & TOKCXCASEFOLD)
{
char *p;
int len;
/* convert each character in the token to lower-case */
for (p = tok->toknam, len = tok->toklen ; len != 0 ; ++p, --len)
{
/* if this character is upper-case, convert it to lower-case */
if (Common::isUpper((uchar)*p))
*p = Common::isLower((uchar)*p);
}
}
}
/* add a symbol to the #define symbol table, folding case if necessary */
void tok_add_define_cvtcase(tokcxdef *ctx, const char *sym, int len,
const char *expan, int explen)
{
char mysym[TOKNAMMAX];
/* convert to lower-case if necessary */
sym = tok_casefold_defsym(ctx, mysym, sym, len);
/* add the symbol */
tok_add_define(ctx, sym, len, expan, explen);
}
/* add a symbol to the #define symbol table */
void tok_add_define(tokcxdef *ctx, const char *sym, int len,
const char *expan, int explen)
{
int hsh;
tokdfdef *df;
/* if it's already defined, ignore it */
if (tok_find_define(ctx, sym, len))
return;
/* find the appropriate entry in the hash table */
hsh = tokdfhsh(sym, len);
/* allocate space for the symbol */
df = (tokdfdef *)mchalo(ctx->tokcxerr,
(sizeof(tokdfdef) + len + explen - 1),
"tok_add_define");
/* set up the new symbol */
df->nm = df->expan + explen;
df->len = len;
df->explen = explen;
memcpy(df->expan, expan, explen);
memcpy(df->nm, sym, len);
/* link it into the hash chain */
df->nxt = ctx->tokcxdf[hsh];
ctx->tokcxdf[hsh] = df;
}
/* add a #define symbol with a numeric value */
void tok_add_define_num_cvtcase(tokcxdef *ctx, char *sym, int len, int num)
{
char buf[20];
/* convert the value to a string */
Common::sprintf_s(buf, "%d", num);
/* add the text value */
tok_add_define_cvtcase(ctx, sym, len, buf, strlen(buf));
}
/* undefine a #define symbol */
void tok_del_define(tokcxdef *ctx, const char *sym, int len)
{
int hsh;
tokdfdef *df;
tokdfdef *prv;
/* find the appropriate chain the hash table */
hsh = tokdfhsh(sym, len);
/* search the chain for this symbol */
for (prv = nullptr, df = ctx->tokcxdf[hsh] ; df ; prv = df, df = df->nxt)
{
/* if this one matches, delete it */
if (df->len == len && !memcmp(df->nm, sym, (size_t)len))
{
/* unlink it from the chain */
if (prv)
prv->nxt = df->nxt;
else
ctx->tokcxdf[hsh] = df->nxt;
/* delete this symbol, and we're done */
mchfre(df);
break;
}
}
}
/* scan a #define symbol to see how long it is */
static int tok_scan_defsym(tokcxdef *ctx, const char *p, int len)
{
int symlen;
/* make sure it's a valid symbol */
if (!(Common::isAlpha((uchar)*p) || *p == '_' || *p == '$'))
{
errlog(ctx->tokcxerr, ERR_REQSYM);
return 0;
}
/* count characters as long as we have valid symbol characters */
for (symlen = 0 ; len && TOKISSYM(*p) ; ++p, --len, ++symlen) ;
return symlen;
}
/* process a #define */
static void tokdefine(tokcxdef *ctx, const char *p, int len)
{
const char *sym;
int symlen;
const char *expan;
char mysym[TOKNAMMAX];
/* get the symbol */
sym = p;
if (!(symlen = tok_scan_defsym(ctx, p, len)))
return;
/* if it's already in the table, log an error */
if (tok_find_define(ctx, sym, symlen))
{
errlog(ctx->tokcxerr, ERR_DEFREDEF);
return;
}
/* skip whitespace following the symbol */
expan = sym + symlen;
len -= symlen;
while (len && t_isspace(*expan)) --len, ++expan;
/* if we're folding case, convert the symbol to lower case */
sym = tok_casefold_defsym(ctx, mysym, sym, symlen);
/* define the symbol */
tok_add_define(ctx, sym, symlen, expan, len);
}
/*
* Update the #if status for the current nesting. Any enclosing
* negative #if will override everything inside, so we need to look
* through the nesting from the outside in until we either determine
* that everything is affirmative or we find a negative anywhere in the
* nesting.
*/
static void tok_update_if_stat(tokcxdef *ctx)
{
int i;
/* look through nesting from the outermost level */
for (i = 0 ; i < ctx->tokcxifcnt ; ++i)
{
/* assume this level will apply to everything inside */
ctx->tokcxifcur = ctx->tokcxif[i];
/* if this level is off, everything inside is off */
switch (ctx->tokcxif[i])
{
case TOKIF_IF_NO:
case TOKIF_ELSE_NO:
/*
* this level is off, hence everything inside is off -- stop
* here with the current (negative) determination
*/
return;
default:
/* so far we're in the "on" section, so keep looking */
break;
}
}
}
/* process an #ifdef or a #ifndef */
static void tok_ifdef_ifndef(tokcxdef *ctx, const char *p, int len, int is_ifdef)
{
int symlen;
const char *sym;
int stat;
int found;
char mysym[TOKNAMMAX];
/* get the symbol */
sym = p;
if (!(symlen = tok_scan_defsym(ctx, p, len)))
return;
/* if we're folding case, convert the symbol to lower case */
sym = tok_casefold_defsym(ctx, mysym, sym, symlen);
/* see if we can find it in the table, and set the status accordingly */
found = (tok_find_define(ctx, sym, symlen) != nullptr);
/* invert the test if this is an ifndef */
if (!is_ifdef) found = !found;
/* set the #if status accordingly */
if (found)
stat = TOKIF_IF_YES;
else
stat = TOKIF_IF_NO;
ctx->tokcxif[ctx->tokcxifcnt] = stat;
/* allocate a new #if level (making sure we have room) */
if (ctx->tokcxifcnt >= TOKIFNEST)
{
errlog(ctx->tokcxerr, ERR_MANYPIF);
return;
}
ctx->tokcxifcnt++;
/* update the current status */
tok_update_if_stat(ctx);
}
/* process a #error */
static void tok_p_error(tokcxdef *ctx, const char *p, int len)
{
errlog1(ctx->tokcxerr, ERR_P_ERROR,
ERRTSTR, errstr(ctx->tokcxerr, p, len));
}
/* process a #ifdef */
static void tokifdef(tokcxdef *ctx, const char *p, int len)
{
tok_ifdef_ifndef(ctx, p, len, TRUE);
}
/* process a #ifndef */
static void tokifndef(tokcxdef *ctx, const char *p, int len)
{
tok_ifdef_ifndef(ctx, p, len, FALSE);
}
/* process a #if */
static void tokif(tokcxdef *ctx, const char *p, int len)
{
errsig(ctx->tokcxerr, ERR_PIF_NA);
}
/* process a #elif */
static void tokelif(tokcxdef *ctx, const char *p, int len)
{
errsig(ctx->tokcxerr, ERR_PELIF_NA);
}
/* process a #else */
static void tokelse(tokcxdef *ctx, const char *p, int len)
{
int cnt;
/* if we're not expecting #else, it's an error */
cnt = ctx->tokcxifcnt;
if (cnt == 0 || ctx->tokcxif[cnt-1] == TOKIF_ELSE_YES
|| ctx->tokcxif[cnt-1] == TOKIF_ELSE_NO)
{
errlog(ctx->tokcxerr, ERR_BADPELSE);
return;
}
/* switch to the appropriate #else state (opposite the #if state) */
if (ctx->tokcxif[cnt-1] == TOKIF_IF_YES)
ctx->tokcxif[cnt-1] = TOKIF_ELSE_NO;
else
ctx->tokcxif[cnt-1] = TOKIF_ELSE_YES;
/* update the current status */
tok_update_if_stat(ctx);
}
/* process a #endif */
static void tokendif(tokcxdef *ctx, const char *p, int len)
{
/* if we're not expecting #endif, it's an error */
if (ctx->tokcxifcnt == 0)
{
errlog(ctx->tokcxerr, ERR_BADENDIF);
return;
}
/* remove the #if level */
ctx->tokcxifcnt--;
/* update the current status */
tok_update_if_stat(ctx);
}
/* process a #undef */
static void tokundef(tokcxdef *ctx, const char *p, int len)
{
const char *sym;
int symlen;
char mysym[TOKNAMMAX];
/* get the symbol */
sym = p;
if (!(symlen = tok_scan_defsym(ctx, p, len)))
return;
/* if we're folding case, convert the symbol to lower case */
sym = tok_casefold_defsym(ctx, mysym, sym, symlen);
/* if it's not defined, log a warning */
if (!tok_find_define(ctx, sym, symlen))
{
errlog(ctx->tokcxerr, ERR_PUNDEF);
return;
}
/* undefine the symbol */
tok_del_define(ctx, sym, symlen);
}
/* process a #pragma directive */
static void tokpragma(tokcxdef *ctx, const char *p, int len)
{
/* ignore empty pragmas */
if (len == 0)
{
errlog(ctx->tokcxerr, ERR_PRAGMA);
return;
}
/* see what we have */
if (len > 1
&& (*p == 'c' || *p == 'C')
&& (*(p+1) == '+' || *(p+1) == '-' || t_isspace(*(p+1))))
{
/* skip spaces after the 'C', if any */
for (++p, --len ; len && t_isspace(*p) ; ++p, --len) ;
/* look for the + or - flag */
if (len && *p == '+')
ctx->tokcxflg |= TOKCXFCMODE;
else if (len && *p == '-')
ctx->tokcxflg &= ~TOKCXFCMODE;
else
{
errlog(ctx->tokcxerr, ERR_PRAGMA);
return;
}
}
else
{
errlog(ctx->tokcxerr, ERR_PRAGMA);
}
}
/* process a #include directive */
static void tokinclude(tokcxdef *ctx, const char *p, int len)
{
linfdef *child;
tokpdef *path;
const char *fname;
int match;
int flen;
linfdef *lin;
const char *q;
size_t flen2;
/* find the filename portion */
fname = p + 1; /* remember start of filename */
path = ctx->tokcxinc; /* start with first path entry */
if (!len)
{
errlog(ctx->tokcxerr, ERR_INCNOFN);
return;
}
switch(*p)
{
case '<':
match = '>';
if (path && path->tokpnxt) path = path->tokpnxt; /* skip 1st path */
goto find_matching_delim;
case '"':
match = '"';
find_matching_delim:
for (++p, --len ; len && *p != match ; --len, ++p) ;
if (len == 0 || *p != match) errlog(ctx->tokcxerr, ERR_INCMTCH);
break;
default:
errlog(ctx->tokcxerr, ERR_INCSYN);
return;
}
flen = p - fname; /* compute length of filename */
for (q = p, flen2 = 0 ;
q > fname && *(q-1) != OSPATHCHAR && !strchr(OSPATHALT, *(q-1)) ;
--q, ++flen2) ;
/* check to see if this file has already been included */
for (lin = ctx->tokcxhdr ; lin ; lin = (linfdef *)lin->linflin.linnxt)
{
char *p2 = lin->linfnam;
p2 += strlen(p2);
while (p2 > lin->linfnam && *(p2-1) != OSPATHCHAR
&& !strchr(OSPATHALT, *(p2-1)))
--p2;
if (strlen(p2) == flen2
&& !memicmp(p2, q, flen2))
{
errlog1(ctx->tokcxerr, ERR_INCRPT, ERRTSTR,
errstr(ctx->tokcxerr, fname, flen));
return;
}
}
/* initialize the line source */
child = linfini(ctx->tokcxmem, ctx->tokcxerr, fname, flen, path, TRUE,
(ctx->tokcxflg & TOKCXFLIN2) != 0);
/* if not found, signal an error */
if (!child) errsig1(ctx->tokcxerr, ERR_INCSEAR,
ERRTSTR, errstr(ctx->tokcxerr, fname, flen));
/* link into tokenizer list of line records */
child->linflin.linnxt = (lindef *)ctx->tokcxhdr;
ctx->tokcxhdr = child;
/* if we're tracking sources for debugging, add into the chain */
if (ctx->tokcxdbg)
{
ctx->tokcxdbg->dbgcxlin = &child->linflin;
child->linflin.linid = ctx->tokcxdbg->dbgcxfid++;
}
/* remember my C-mode setting */
if (ctx->tokcxflg & TOKCXFCMODE)
ctx->tokcxlin->linflg |= LINFCMODE;
else
ctx->tokcxlin->linflg &= ~LINFCMODE;
child->linflin.linpar = ctx->tokcxlin; /* remember parent line source */
ctx->tokcxlin = &child->linflin; /* make the child the current source */
}
/* get a new line from line source, processing '#' directives */
static int tokgetlin(tokcxdef *ctx, int dopound)
{
for (;;)
{
if (linget(ctx->tokcxlin))
{
/* at eof in current source; resume parent if there is one */
if (ctx->tokcxlin->linpar)
{
lindef *parent;
parent = ctx->tokcxlin->linpar; /* remember parent */
lincls(ctx->tokcxlin); /* close included file */
if (!ctx->tokcxdbg) /* if no debug context... */
mchfre(ctx->tokcxlin); /* free line source */
ctx->tokcxlin = parent; /* reset to parent line source */
if (parent->linflg & LINFCMODE)
ctx->tokcxflg |= TOKCXFCMODE;
else
ctx->tokcxflg &= ~TOKCXFCMODE;
continue; /* back for another attempt */
}
else
{
/* check for outstanding #if/#ifdef */
if (ctx->tokcxifcnt)
errlog(ctx->tokcxerr, ERR_NOENDIF);
/* return end-of-file indication */
return TRUE;
}
}
/* if this is a multi-segment line, copy it into our own buffer */
if (ctx->tokcxlin->linflg & LINFMORE)
{
char *p;
uint rem;
int done;
if (!ctx->tokcxbuf)
{
/* allocate 1k as a default buffer */
ctx->tokcxbuf = (char *)mchalo(ctx->tokcxerr, 1024,
"tok");
ctx->tokcxbsz = 1024;
}
ctx->tokcxlen = 0;
for (done = FALSE, p = ctx->tokcxbuf, rem = ctx->tokcxbsz ;
!done ; )
{
size_t len = ctx->tokcxlin->linlen;
/* add the current segment's length into line length */
ctx->tokcxlen += len;
/* we're done after this piece if the last fetch was all */
done = !(ctx->tokcxlin->linflg & LINFMORE);
if (len + 1 > rem)
{
char *newp;
/* increase the size of the buffer */
if (ctx->tokcxbsz > (unsigned)0x8000)
errsig(ctx->tokcxerr, ERR_LONGLIN);
rem += 4096;
ctx->tokcxbsz += 4096;
/* allocate a new buffer and copy line into it */
newp = (char *)mchalo(ctx->tokcxerr, ctx->tokcxbsz, "tok");
memcpy(newp, ctx->tokcxbuf, (size_t)(p - ctx->tokcxbuf));
/* free the original buffer, and use the new one */
p = (p - ctx->tokcxbuf) + newp;
mchfre(ctx->tokcxbuf);
ctx->tokcxbuf = newp;
}
/* add the line to the buffer */
memcpy(p, ctx->tokcxlin->linbuf, len);
p += len;
rem -= len;
/* get the next piece of the line if there is one */
if (!done)
{
if (linget(ctx->tokcxlin)) break;
}
}
/* null-terminate the buffer, and use it for input */
*p = '\0';
ctx->tokcxptr = ctx->tokcxbuf;
}
else
{
ctx->tokcxptr = ctx->tokcxlin->linbuf;
ctx->tokcxlen = ctx->tokcxlin->linlen;
}
/* check for preprocessor directives */
if (dopound && ctx->tokcxlen != 0 && ctx->tokcxptr[0] == '#'
&& !(ctx->tokcxlin->linflg & LINFNOINC))
{
const char *p;
int len;
static const struct
{
const char *nm;
int len;
int ok_in_if;
void (*fn)(tokcxdef *, const char *, int);
}
*dirp, dir[] =
{
{ "include", 7, FALSE, tokinclude },
{ "pragma", 6, FALSE, tokpragma },
{ "define", 6, FALSE, tokdefine },
{ "ifdef", 5, TRUE, tokifdef },
{ "ifndef", 6, TRUE, tokifndef },
{ "if", 2, TRUE, tokif },
{ "else", 4, TRUE, tokelse },
{ "elif", 4, TRUE, tokelif },
{ "endif", 5, TRUE, tokendif },
{ "undef", 5, FALSE, tokundef },
{ "error", 5, FALSE, tok_p_error }
};
int i;
/* scan off spaces between '#' and directive */
for (len = ctx->tokcxlen - 1, p = &ctx->tokcxptr[1] ;
len && t_isspace(*p) ; --len, ++p) ;
/* find and process the directive */
for (dirp = dir, i = sizeof(dir)/sizeof(dir[0]) ; i ; --i, ++dirp)
{
/* compare this directive; if it wins, call its function */
if (len >= dirp->len && !memcmp(p, dirp->nm, (size_t)dirp->len)
&& (len == dirp->len || t_isspace(*(p + dirp->len))))
{
int cnt;
int stat;
/*
* if we're not in a #if's false part, or if the
* directive is processed even in #if false parts,
* process the line, otherwise skip it
*/
cnt = ctx->tokcxifcnt;
if (dirp->ok_in_if || cnt == 0
|| ((stat = ctx->tokcxifcur) == TOKIF_IF_YES
|| stat == TOKIF_ELSE_YES))
{
/* skip whitespace following the directive */
for (p += dirp->len, len -= dirp->len ;
len && t_isspace(*p) ;
--len, ++p) ;
/* invoke the function to process this directive */
(*dirp->fn)(ctx, p, len);
}
/* there's no need to look at more directives */
break;
}
}
/* if we didn't find anything, flag the error */
if (i == 0)
errlog(ctx->tokcxerr, ERR_PRPDIR);
/* ignore this line */
continue;
}
else
{
/*
* Check the #if level. If we're in an #if, and we're to
* ignore lines (because of a false condition or an #else
* part for a true condition), skip this line.
*/
if (ctx->tokcxifcnt != 0)
{
switch(ctx->tokcxifcur)
{
case TOKIF_IF_NO:
case TOKIF_ELSE_NO:
/* ignore this line */
continue;
default:
/* we're in a true part - keep the line */
break;
}
}
ctx->tokcxlin->linflg &= ~LINFDBG; /* no debug record yet */
return(FALSE); /* return the line we found */
}
}
}
/* get the next token, removing it from the input stream */
int toknext(tokcxdef *ctx)
{
const char *p;
tokdef *tok = &ctx->tokcxcur;
int len;
/*
* Check for the special case that we pushed an open paren prior to
* a string containing an embedded expression. If this is the case,
* immediately return the string we previously parsed.
*/
if ((ctx->tokcxflg & TOKCXF_EMBED_PAREN_PRE) != 0)
{
/*
* convert the token to a string - note that the offset
* information for the string is already in the current token
* structure, since we set everything up for it on the previous
* call where we actually parsed the beginning of the string
*/
tok->toktyp = TOKTDSTRING;
/* clear the special flag - we've now consumed the pushed string */
ctx->tokcxflg &= ~TOKCXF_EMBED_PAREN_PRE;
/* immediately return the string */
return tok->toktyp;
}
/* set up at the current scanning position */
p = ctx->tokcxptr;
len = ctx->tokcxlen;
/* scan off whitespace and comments until we find something */
do
{
skipblanks:
/* if there's nothing on this line, get the next one */
if (len == 0)
{
/* if we're in a macro expansion, continue after it */
if (ctx->tokcxmlvl)
{
ctx->tokcxmlvl--;
p = ctx->tokcxmsav[ctx->tokcxmlvl];
len = ctx->tokcxmsvl[ctx->tokcxmlvl];
}
else
{
if (tokgetlin(ctx, TRUE))
{
tok->toktyp = TOKTEOF;
goto done;
}
p = ctx->tokcxptr;
len = ctx->tokcxlen;
}
}
while (len && t_isspace(*p)) ++p, --len; /* scan off whitespace */
/* check for comments, and remove if present */
if (len >= 2 && *p == '/' && *(p+1) == '/')
len = 0;
else if (len >= 2 && *p == '/' && *(p+1) == '*')
{
while (len < 2 || *p != '*' || *(p+1) != '/')
{
if (len != 0)
++p, --len;
if (len == 0)
{
if (ctx->tokcxmlvl != 0)
{
ctx->tokcxmlvl--;
p = ctx->tokcxmsav[ctx->tokcxmlvl];
len = ctx->tokcxmsvl[ctx->tokcxmlvl];
}
else
{
if (tokgetlin(ctx, FALSE))
{
ctx->tokcxptr = p;
tok->toktyp = TOKTEOF;
goto done;
}
p = ctx->tokcxptr;
len = ctx->tokcxlen;
}
}
}
p += 2;
len -= 2;
goto skipblanks;
}
} while (len == 0);
nexttoken:
if (Common::isAlpha((uchar)*p) || *p == '_' || *p == '$')
{
int l;
int hash;
const char *q;
char *tq;
toktdef *tab;
int found = FALSE;
uchar thischar;
tokdfdef *df;
for (hash = 0, l = 0, tq = tok->toknam ;
len != 0 && TOKISSYM(*p) && l < TOKNAMMAX ;
(thischar = ((Common::isUpper((uchar)*p)
&& (ctx->tokcxflg & TOKCXCASEFOLD))
? Common::isLower((uchar)*p) : *p)),
(hash = ((hash + thischar) & (TOKHASHSIZE - 1))),
(*tq++ = thischar), ++p, --len, ++l) ;
*tq = '\0';
if (len != 0 && TOKISSYM(*p))
{
while (len != 0 && TOKISSYM(*p)) ++p, --len;
errlog1(ctx->tokcxerr, ERR_TRUNC, ERRTSTR,
errstr(ctx->tokcxerr, tok->toknam, tok->toklen));
}
tok->toklen = l;
tok->tokhash = hash;
/*
* check for the special defined() preprocessor operator
*/
if (l == 9 && !memcmp(tok->toknam,
((ctx->tokcxflg & TOKCXCASEFOLD)
? "__defined" : "__DEFINED"),
(size_t)9)
&& len > 2 && *p == '(' && TOKISSYM(*(p+1))
&& !Common::isDigit((uchar)*(p+1)))
{
int symlen;
char mysym[TOKNAMMAX];
/* find the matching ')', allowing only symbolic characters */
++p, --len;
for (symlen = 0, q = p ; len && *p != ')' && TOKISSYM(*p) ;
++p, --len, ++symlen) ;
/* make sure we found the closing paren */
if (!len || *p != ')')
errsig(ctx->tokcxerr, ERR_BADISDEF);
++p, --len;
/* if we're folding case, convert the symbol to lower case */
q = tok_casefold_defsym(ctx, mysym, q, symlen);
/* check to see if it's defined */
tok->toktyp = TOKTNUMBER;
tok->tokval = (tok_find_define(ctx, q, symlen) != nullptr);
goto done;
}
/* substitute the preprocessor #define, if any */
if ((df = tok_find_define(ctx, tok->toknam, l)) != nullptr)
{
/* save the current parsing position */
if (ctx->tokcxmlvl >= TOKMACNEST)
errsig(ctx->tokcxerr, ERR_MACNEST);
ctx->tokcxmsav[ctx->tokcxmlvl] = p;
ctx->tokcxmsvl[ctx->tokcxmlvl] = len;
ctx->tokcxmlvl++;
/* point to the token's expansion and keep going */
p = df->expan;
len = df->explen;
goto nexttoken;
}
/* look up in symbol table(s), if any */
for (tab = ctx->tokcxstab ; tab ; tab = tab->toktnxt)
{
if ((found = (*tab->toktfsea)(tab, tok->toknam, l, hash,
&tok->toksym)) != 0)
break;
}
if (found && tok->toksym.tokstyp == TOKSTKW)
tok->toktyp = tok->toksym.toksval;
else
{
tok->toktyp = TOKTSYMBOL;
if (!found) tok->toksym.tokstyp = TOKSTUNK;
}
goto done;
}
else if (Common::isDigit((uchar)*p))
{
long acc = 0;
/* check for octal/hex */
if (*p == '0')
{
++p, --len;
if (len && (*p == 'x' || *p == 'X'))
{
/* hex */
++p, --len;
while (len && TOKISHEX(*p))
{
acc = (acc << 4) + TOKHEX2INT(*p);
++p, --len;
}
}
else
{
/* octal */
while (len && TOKISOCT(*p))
{
acc = (acc << 3) + TOKOCT2INT(*p);
++p, --len;
}
}
}
else
{
/* decimal */
while (len && Common::isDigit((uchar)*p))
{
acc = (acc << 1) + (acc << 3) + TOKDEC2INT(*p);
++p, --len;
}
}
tok->tokval = acc;
tok->toktyp = TOKTNUMBER;
goto done;
}
else if (*p == '"' || *p == '\'')
{
char delim; /* closing delimiter we're looking for */
const char *strstart; /* pointer to start of string */
int warned;
delim = *p;
--len;
strstart = ++p;
if (delim == '"' && len >= 2 && *p == '<' && *(p+1) == '<')
{
/* save the current parsing position */
if (ctx->tokcxmlvl >= TOKMACNEST)
errsig(ctx->tokcxerr, ERR_MACNEST);
ctx->tokcxmsav[ctx->tokcxmlvl] = p + 2;
ctx->tokcxmsvl[ctx->tokcxmlvl] = len - 2;
ctx->tokcxmlvl++;
/*
* read from the special "<<" expansion string - use the
* version for a "<<" at the very beginning of the string
*/
p = tokmac1s;
len = strlen(p);
ctx->tokcxflg |= TOKCXFINMAC;
goto nexttoken;
}
tok->toktyp = (delim == '"' ? TOKTDSTRING : TOKTSSTRING);
tok->tokofs = (*ctx->tokcxsst)(ctx->tokcxscx); /* start the string */
for (warned = FALSE ;; )
{
if (len >= 2 && *p == '\\')
{
if (*(p+1) == '"' || *(p+1) == '\'')
{
(*ctx->tokcxsad)(ctx->tokcxscx, strstart,
(ushort)(p - strstart));
strstart = p + 1;
}
p += 2;
len -= 2;
}
else if (len == 0 || *p == delim ||
(delim == '"' && len >= 2 && *p == '<' && *(p+1) == '<'
&& !(ctx->tokcxflg & TOKCXFINMAC)))
{
(*ctx->tokcxsad)(ctx->tokcxscx, strstart,
(ushort)(p - strstart));
if (len == 0)
{
if (ctx->tokcxmlvl)
{
ctx->tokcxmlvl--;
p = ctx->tokcxmsav[ctx->tokcxmlvl];
len = ctx->tokcxmsvl[ctx->tokcxmlvl];
}
else
(*ctx->tokcxsad)(ctx->tokcxscx, " ", (ushort)1);
while (len == 0)
{
if (tokgetlin(ctx, FALSE))
errsig(ctx->tokcxerr, ERR_STREOF);
p = ctx->tokcxptr;
len = ctx->tokcxlen;
/* warn if it looks like the end of an object */
if (!warned && len && (*p == ';' || *p == '}'))
{
errlog(ctx->tokcxerr, ERR_STREND);
warned = TRUE; /* warn only once per string */
}
/* scan past whitespace at start of line */
while (len && t_isspace(*p)) ++p, --len;
}
strstart = p;
}
else break;
}
else
++p, --len;
}
/* end the string */
(*ctx->tokcxsend)(ctx->tokcxscx);
/* check to see how it ended */
if (len != 0 && *p == delim)
{
/*
* We ended with the matching delimiter. Move past the
* closing delimiter.
*/
++p;
--len;
/*
* If we have a pending close paren we need to put in
* because of an embedded expression that occurred earlier
* in the string, parse the macro to provide the paren.
*/
if ((ctx->tokcxflg & TOKCXF_EMBED_PAREN_AFT) != 0
&& !(ctx->tokcxflg & TOKCXFINMAC))
{
/* clear the flag */
ctx->tokcxflg &= ~TOKCXF_EMBED_PAREN_AFT;
/* push the current parsing position */
if (ctx->tokcxmlvl >= TOKMACNEST)
errsig(ctx->tokcxerr, ERR_MACNEST);
ctx->tokcxmsav[ctx->tokcxmlvl] = p;
ctx->tokcxmsvl[ctx->tokcxmlvl] = len;
ctx->tokcxmlvl++;
/* parse the macro */
p = tokmac4;
len = strlen(p);
}
}
else if (len != 0 && *p == '<')
{
/* save the current parsing position */
if (ctx->tokcxmlvl >= TOKMACNEST)
errsig(ctx->tokcxerr, ERR_MACNEST);
ctx->tokcxmsav[ctx->tokcxmlvl] = p + 2;
ctx->tokcxmsvl[ctx->tokcxmlvl] = len - 2;
ctx->tokcxmlvl++;
/* read from the "<<" expansion */
p = tokmac1;
len = strlen(p);
ctx->tokcxflg |= TOKCXFINMAC;
/*
* Set the special push-a-paren flag: we'll return an open
* paren now, so that we have an open paren before the
* string, and then on the next call to toknext() we'll
* immediately return the string we've already parsed here.
* This will ensure that everything in the string is
* properly grouped together as a single indivisible
* expression.
*
* Note that we only need to do this for the first embedded
* expression in a string. Once we have a close paren
* pending, we don't need more open parens.
*/
if (!(ctx->tokcxflg & TOKCXF_EMBED_PAREN_AFT))
{
ctx->tokcxflg |= TOKCXF_EMBED_PAREN_PRE;
tok->toktyp = TOKTLPAR;
}
}
goto done;
}
else if (len >= 2 && *p == '>' && *(p+1) == '>'
&& (ctx->tokcxflg & TOKCXFINMAC) != 0)
{
/* skip the ">>" */
ctx->tokcxflg &= ~TOKCXFINMAC;
p += 2;
len -= 2;
/* save the current parsing position */
if (ctx->tokcxmlvl >= TOKMACNEST)
errsig(ctx->tokcxerr, ERR_MACNEST);
ctx->tokcxmsav[ctx->tokcxmlvl] = p;
ctx->tokcxmsvl[ctx->tokcxmlvl] = len;
ctx->tokcxmlvl++;
if (*p == '"')
{
++(ctx->tokcxmsav[ctx->tokcxmlvl - 1]);
--(ctx->tokcxmsvl[ctx->tokcxmlvl - 1]);
p = tokmac3;
/*
* we won't need an extra closing paren now, since tokmac3
* provides it
*/
ctx->tokcxflg &= ~TOKCXF_EMBED_PAREN_AFT;
}
else
{
/*
* The string is continuing. Set a flag to note that we
* need to provide a close paren after the end of the
* string, and parse the glue (tokmac2) that goes between
* the expression and the resumption of the string.
*/
ctx->tokcxflg |= TOKCXF_EMBED_PAREN_AFT;
p = tokmac2;
}
len = strlen(p);
goto nexttoken;
}
else
{
tokscdef *sc;
for (sc = ctx->tokcxsc[ctx->tokcxinx[(uchar)*p]] ; sc ;
sc = sc->tokscnxt)
{
if (toksceq(sc->tokscstr, p, sc->toksclen, len))
{
tok->toktyp = sc->toksctyp;
p += sc->toksclen;
len -= sc->toksclen;
goto done;
}
}
errsig(ctx->tokcxerr, ERR_INVTOK);
}
done:
ctx->tokcxptr = p;
ctx->tokcxlen = len;
return(tok->toktyp);
}
/* initialize a linear symbol table */
void toktlini(errcxdef *errctx, toktldef *toktab, uchar *mem, uint siz)
{
CLRSTRUCT(*toktab);
/* initialize superclass data */
toktab->toktlsc.toktfadd = toktladd; /* set add-symbol method */
toktab->toktlsc.toktfsea = toktlsea; /* set search-table method */
toktab->toktlsc.toktfeach = toktleach; /* set 'each' method */
toktab->toktlsc.toktfset = toktlset; /* set 'update' method */
toktab->toktlsc.tokterr = errctx; /* set error handling context */
/* initialize class data */
toktab->toktlptr = mem;
toktab->toktlnxt = mem;
toktab->toktlsiz = siz;
}
/* add a symbol to a linear symbol table */
void toktladd(toktdef *toktab1, char *name, int namel,
int typ, int val, int hash)
{
uint siz = sizeof(toks1def) + namel;
toksdef *sym;
toktldef *toktab = (toktldef *)toktab1;
VARUSED(hash);
if (toktab->toktlsiz < siz)
errsig(toktab->toktlsc.tokterr, ERR_NOLCLSY);
sym = (toksdef *)toktab->toktlnxt;
siz = osrndsz(siz);
toktab->toktlnxt += siz;
if (siz > toktab->toktlsiz) toktab->toktlsiz = 0;
else toktab->toktlsiz -= siz;
/* set up symbol */
sym->toksval = val;
sym->tokslen = namel;
sym->tokstyp = typ;
sym->toksfr = 0;
memcpy(sym->toksnam, name, (size_t)(namel + 1));
/* indicate there's one more symbol in the table */
++(toktab->toktlcnt);
}
/* delete all symbols from a linear symbol table */
void toktldel(toktldef *tab)
{
tab->toktlcnt = 0;
tab->toktlsiz += tab->toktlnxt - tab->toktlptr;
tab->toktlnxt = tab->toktlptr;
}
/* call a function for every symbol in a linear symbol table */
void toktleach(toktdef *tab1,
void (*cb)(void *ctx, toksdef *sym), void *ctx)
{
toksdef *p;
uint cnt;
toktldef *tab = (toktldef *)tab1;
for (p = (toksdef *)tab->toktlptr, cnt = tab->toktlcnt ; cnt ; --cnt )
{
(*cb)(ctx, p);
p = (toksdef *)(((uchar *)p)
+ osrndsz(p->tokslen + sizeof(toks1def)));
}
}
/* search a linear symbol table */
int toktlsea(toktdef *tab1, char *name, int namel, int hash, toksdef *ret)
{
toksdef *p;
uint cnt;
toktldef *tab = (toktldef *)tab1;
VARUSED(hash);
for (p = (toksdef *)tab->toktlptr, cnt = tab->toktlcnt ; cnt ; --cnt )
{
if (p->tokslen == namel && !memcmp(p->toksnam, name, (size_t)namel))
{
memcpy(ret, p, (size_t)(sizeof(toks1def) + namel));
return(TRUE);
}
p = (toksdef *)(((uchar *)p)
+ osrndsz(p->tokslen + sizeof(toks1def)));
}
/* nothing found - indicate by returning FALSE */
return(FALSE);
}
/* update a symbol in a linear symbol table */
void toktlset(toktdef *tab1, toksdef *newsym)
{
toksdef *p;
uint cnt;
toktldef *tab = (toktldef *)tab1;
for (p = (toksdef *)tab->toktlptr, cnt = tab->toktlcnt ; cnt ; --cnt )
{
if (p->tokslen == newsym->tokslen
&& !memcmp(p->toksnam, newsym->toksnam, (size_t)newsym->tokslen))
{
p->toksval = newsym->toksval;
p->tokstyp = newsym->tokstyp;
return;
}
p = (toksdef *)(((uchar *)p)
+ osrndsz(p->tokslen + sizeof(toks1def)));
}
}
tokcxdef *tokcxini(errcxdef *errctx, mcmcxdef *mcmctx, tokldef *sctab)
{
int i;
int cnt;
tokldef *p;
uchar c;
uchar index[256];
tokcxdef *ret;
tokscdef *sc;
ushort siz;
/* set up index table: finds tokcxsc entry from character value */
memset(index, 0, (size_t)sizeof(index));
for (i = cnt = 0, p = sctab ; (c = p->toklstr[0]) != 0 ; ++cnt, ++p)
if (!index[c]) index[c] = ++i;
/* allocate memory for table plus the tokscdef's */
siz = sizeof(tokcxdef) + (i * sizeof(tokscdef *))
+ ((cnt + 1) * sizeof(tokscdef));
ret = (tokcxdef *)mchalo(errctx, siz, "tokcxini");
memset(ret, 0, (size_t)siz);
/* copy the index, set up fixed part */
memcpy(ret->tokcxinx, index, sizeof(ret->tokcxinx));
ret->tokcxerr = errctx;
ret->tokcxmem = mcmctx;
/* start out without an #if */
ret->tokcxifcur = TOKIF_IF_YES;
/* force the first toknext() to read a line */
ret->tokcxptr = "\000";
/* figure where the tokscdef's go (right after sc pointer array) */
sc = (tokscdef *)&ret->tokcxsc[i+1];
/* set up the individual tokscdef entries, and link into lists */
for (p = sctab ; (c = p->toklstr[0]) != 0 ; ++p, ++sc)
{
size_t len;
sc->toksctyp = p->tokltyp;
len = sc->toksclen = strlen(p->toklstr);
memcpy(sc->tokscstr, p->toklstr, len);
sc->tokscnxt = ret->tokcxsc[index[c]];
ret->tokcxsc[index[c]] = sc;
}
return(ret);
}
/* add an include path to a tokdef */
void tokaddinc(tokcxdef *ctx, char *path, int pathlen)
{
tokpdef *newpath;
tokpdef *last;
/* find the tail of the include path list, if any */
for (last = ctx->tokcxinc ; last && last->tokpnxt ;
last = last->tokpnxt) ;
/* allocate storage for and set up a new path structure */
newpath = (tokpdef *)mchalo(ctx->tokcxerr,
(sizeof(tokpdef) + pathlen - 1),
"tokaddinc");
newpath->tokplen = pathlen;
newpath->tokpnxt = (tokpdef *)nullptr;
memcpy(newpath->tokpdir, path, (size_t)pathlen);
/* link in at end of list (if no list yet, newpath becomes first entry) */
if (last)
last->tokpnxt = newpath;
else
ctx->tokcxinc = newpath;
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk