GLK: TADS2: Added code for tokenizer & vocabulary

This commit is contained in:
Paul Gilbert 2019-05-17 11:02:01 -10:00
parent 0279143a62
commit e83972f502
12 changed files with 2685 additions and 31 deletions

View File

@ -99,6 +99,7 @@ MODULE_OBJS := \
tads/tads2/error.o \
tads/tads2/error_handling.o \
tads/tads2/file_io.o \
tads/tads2/line_source_file.o \
tads/tads2/memory_cache.o \
tads/tads2/memory_cache_heap.o \
tads/tads2/memory_cache_loader.o \

View File

@ -34,8 +34,20 @@ osfildef *osfoprb(const char *fname, os_filetype_t typ) {
return nullptr;
}
osfildef *osfoprwtb(const char *fname, os_filetype_t typ) {
Common::DumpFile *df = new Common::DumpFile();
if (df->open(fname))
return df;
delete df;
return nullptr;
}
int osfrb(osfildef *fp, void *buf, size_t count) {
return fp->read(buf, count);
return dynamic_cast<Common::ReadStream *>(fp)->read(buf, count);
}
bool osfwb(osfildef *fp, void *buf, size_t count) {
return dynamic_cast<Common::WriteStream *>(fp)->write(buf, count) != count;
}
} // End of namespace TADS

View File

@ -38,6 +38,12 @@
namespace Glk {
namespace TADS {
#define OSPATHCHAR '/'
#define OSPATHALT ""
#define OSPATHURL "/"
#define OSPATHSEP ':'
#define OS_NEWLINE_SEQ "\n"
/* Defined for Gargoyle. */
#define HAVE_STDINT_
@ -92,8 +98,12 @@ namespace TADS {
#define OSFNMAX 255
/* File handle structure for osfxxx functions. */
typedef Common::SeekableReadStream osfildef;
/**
* File handle structure for osfxxx functions
* Note that we need to define it as a Common::Stream since the type is used by
* TADS for both reading and writing files
*/
typedef Common::Stream osfildef;
/* Directory handle for searches via os_open_dir() et al. */
typedef Common::FSNode *osdirhdl_t;
@ -188,18 +198,18 @@ osfildef *osfoprwt(const char *fname, os_filetype_t typ);
#define osfoprs osfoprt
/* Open binary file for reading. */
osfildef *osfoprb(const char *fname, os_filetype_t typ);
inline osfildef *osfoprb(const char *fname, os_filetype_t typ);
/* Open binary file for reading/writing. If the file already exists,
* keep the existing contents. Create a new file if it doesn't already
* exist. */
osfildef*
osfoprwb( const char* fname, os_filetype_t typ );
osfoprwb(const char *fname, os_filetype_t typ);
/* Open binary file for reading/writing. If the file already exists,
/* Open binary file for writing. If the file already exists,
* truncate the existing contents. Create a new file if it doesn't
* already exist. */
#define osfoprwtb(fname,typ) (fopen((fname),"w+b"))
inline osfildef *osfoprwtb(const char *fname, os_filetype_t typ);
/* Get a line of text from a text file. */
#define osfgets fgets
@ -208,7 +218,7 @@ osfoprwb( const char* fname, os_filetype_t typ );
#define osfputs fputs
/* Write bytes to file. */
#define osfwb(fp,buf,bufl) (fwrite((buf),(bufl),1,(fp))!=1)
inline bool osfwb(osfildef *fp, void *buf, size_t count);
/* Flush buffered writes to a file. */
#define osfflush fflush

View File

@ -28,6 +28,8 @@
#define GLK_TADS_TADS2_FILE_IO
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/memory_cache_loader.h"
#include "glk/tads/tads2/object.h"
namespace Glk {
namespace TADS {
@ -35,6 +37,9 @@ namespace TADS2 {
/* forward declarations */
struct voccxdef;
struct tokpdef;
struct tokthdef;
struct tokcxdef;
/* load-on-demand context (passed in by mcm in load callback) */
typedef struct fiolcxdef fiolcxdef;
@ -65,20 +70,16 @@ void fiowrt(struct mcmcxdef *mctx, voccxdef *vctx,
#define FIOFLIN2 0x80 /* new-style line records */
/* read game from binary file; sets up loader callback context */
void fiord(struct mcmcxdef *mctx, voccxdef *vctx,
struct tokcxdef *tctx,
char *fname, char *exename,
struct fiolcxdef *setupctx, objnum *preinit, uint *flagp,
struct tokpdef *path, uchar **fmtsp, uint *fmtlp,
uint *pcntptr, int flags, struct appctxdef *appctx,
char *argv0);
void fiord(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx, char *fname,
char *exename, fiolcxdef *setupctx, objnum *preinit, uint *flagp,
tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags,
appctxdef *appctx, char *argv0);
/* shut down load-on-demand subsystem, close load file */
void fiorcls(fiolcxdef *ctx);
/* loader callback - load an object on demand */
void OS_LOADDS fioldobj(void *ctx, mclhd handle, uchar *ptr,
ushort siz);
void OS_LOADDS fioldobj(void *ctx, mclhd handle, uchar *ptr, ushort siz);
/*
* Save a game - returns TRUE on failure. We'll save the file to

View File

@ -134,7 +134,7 @@ void varused();
* anything outside of the normal ASCII set as spaces.
*/
#define t_isspace(c) \
(((unsigned char)(c)) <= 127 && isspace((unsigned char)(c)))
(((unsigned char)(c)) <= 127 && Common::isSpace((unsigned char)(c)))
/* round a size to worst-case alignment boundary */

View File

@ -0,0 +1,33 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "glk/tads/tads2/line_source_file.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@ -0,0 +1,170 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GLK_TADS_TADS2_LINE_SOURCE_FILE
#define GLK_TADS_TADS2_LINE_SOURCE_FILE
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/debug.h"
#include "glk/tads/tads2/line_source.h"
#include "glk/tads/tads2/object.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
struct tokpdef;
/* maximum number of pages of debugging records we can keep */
#define LINFPGMAX 128
/*
* executable line information structure: this record relates one
* executable line to the object containing the p-code, and the offset
* in the object of the p-code for the start of the line
*/
struct linfinfo {
/*
* OPCLINE data (file seek position or line number, depending on how
* the game was compiled: -ds -> file seek offset, -ds2 -> line
* number)
*/
ulong fpos;
/* object number */
objnum objn;
/* offset from start of code */
uint ofs;
};
/*
* file line source
*/
struct linfdef {
lindef linflin; /* superclass data */
osfildef *linffp; /* file pointer for this line source */
char linfbuf[100]; /* buffer for the line contents */
int linfbufnxt; /* offset in buffer of start of next line */
int linfnxtlen; /* length of data after linfbufnxt */
ulong linfnum; /* current line number */
ulong linfseek; /* seek position of current line */
mcmcxdef *linfmem; /* memory manager context */
mcmon linfpg[LINFPGMAX]; /* pages for debugging records */
ulong linfcrec; /* number of debugger records written so far */
char linfnam[1]; /* name of file being read */
};
/* initialize a file line source, opening the file for the line source */
linfdef *linfini(mcmcxdef *mctx, errcxdef *errctx, char *filename,
int flen, tokpdef *path, int must_find_file,
int new_line_records);
/* initialize a pre-allocated linfdef, skipping debugger page setup */
void linfini2(mcmcxdef *mctx, linfdef *linf,
char *filename, int flen, osfildef *fp, int new_line_records);
/* get next line from line source */
int linfget(lindef *lin);
/* generate printable rep of current position in source (for errors) */
void linfppos(lindef *lin, char *buf, uint bufl);
/* close line source */
void linfcls(lindef *lin);
/* generate source-line debug instruction operand */
void linfglop(lindef *lin, uchar *buf);
/* generate new-style source-line debug instructino operand */
void linfglop2(lindef *lin, uchar *buf);
/* save line source to binary (.gam) file */
int linfwrt(lindef *lin, osfildef *fp);
/* load a file-line-source from binary (.gam) file */
int linfload(osfildef *fp, dbgcxdef *dbgctx, errcxdef *ec,
tokpdef *path);
/* add a debugger line record for the current line */
void linfcmp(lindef *lin, uchar *buf);
/* find nearest line record to a file seek location */
void linffind(lindef *lin, char *buf, objnum *objp, uint *ofsp);
/* activate line source for debugging */
void linfact(lindef *lin);
/* disactivate line source */
void linfdis(lindef *lin);
/* get current seek position */
void linftell(lindef *lin, uchar *pos);
/* seek */
void linfseek(lindef *lin, uchar *pos);
/* read */
int linfread(lindef *lin, uchar *buf, uint siz);
/* add a signed delta to a seek positon */
void linfpadd(lindef *lin, uchar *pos, long delta);
/* query whether at top of file */
int linfqtop(lindef *lin, uchar *pos);
/* read one line at current seek position */
int linfgets(lindef *lin, uchar *buf, uint bufsiz);
/* get name of line source */
void linfnam(lindef *lin, char *buf);
/* get the current line number */
ulong linflnum(lindef *lin);
/* go to top or bottom */
void linfgoto(lindef *lin, int where);
/* return the current offset in the line source */
long linfofs(lindef *lin);
/* renumber an object */
void linfren(lindef *lin, objnum oldnum, objnum newnum);
/* delete an object */
void linfdelnum(lindef *lin, objnum objn);
/* copy line records to an array of linfinfo structures */
void linf_copy_linerecs(linfdef *linf, linfinfo *info);
/* debugging echo */
#ifdef DEBUG
# define LINFDEBUG(x) x
#else /* DEBUG */
# define LINFDEBUG(x)
#endif /* DEBUG */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@ -56,6 +56,7 @@ void TADS2::runGame() {
}
void TADS2::trdmain1(errcxdef *errctx) {
#ifdef TODO
osfildef *swapfp = (osfildef *)0;
runcxdef runctx;
bifcxdef bifctx;
@ -99,7 +100,7 @@ void TADS2::trdmain1(errcxdef *errctx) {
char *charmap = 0; /* character map file */
int charmap_none; /* explicitly do not use a character set */
int doublespace = TRUE; /* formatter double-space setting */
#ifdef TODO
NOREG((&loadopen))
/* initialize the output formatter */

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/tads2/line_source.h"
#include "glk/tads/tads2/line_source_file.h"
#include "glk/tads/tads2/memory_cache.h"
namespace Glk {
@ -356,7 +357,7 @@ struct tokcxdef {
int tokcxifcnt; /* number of #endif's we expect to find */
char tokcxif[TOKIFNEST]; /* #if state for each nesting level */
int tokcxifcur; /* current #if state, obeying nesting */
struct linfdef *tokcxhdr; /* list of previously included headers */
linfdef *tokcxhdr; /* list of previously included headers */
tokscdef *tokcxsc[1]; /* special character table */
};
@ -453,16 +454,16 @@ void tok_write_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec);
/* determine if a char is a valid non-initial character in a symbol name */
#define TOKISSYM(c) \
(isalpha((uchar)(c)) || isdigit((uchar)(c)) || (c)=='_' || (c)=='$')
(Common::isAlpha((uchar)(c)) || Common::isDigit((uchar)(c)) || (c)=='_' || (c)=='$')
/* numeric conversion and checking macros */
#define TOKISHEX(c) \
(isdigit((uchar)(c))||((c)>='a'&&(c)<='f')||((c)>='A'&&(c)<='F'))
(Common::isDigit((uchar)(c))||((c)>='a'&&(c)<='f')||((c)>='A'&&(c)<='F'))
#define TOKISOCT(c) \
(isdigit((uchar)(c))&&!((c)=='8'||(c)=='9'))
(Common::isDigit((uchar)(c))&&!((c)=='8'||(c)=='9'))
#define TOKHEX2INT(c) \
(isdigit((uchar)c)?(c)-'0':((c)>='a'?(c)-'a'+10:(c)-'A'+10))
(Common::isDigit((uchar)c)?(c)-'0':((c)>='a'?(c)-'a'+10:(c)-'A'+10))
#define TOKOCT2INT(c) ((c)-'0')
#define TOKDEC2INT(c) ((c)-'0')

View File

@ -22,12 +22,912 @@
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/memory_cache_heap.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
// TODO: Rest of vocabulary stuff
/*
* Main vocabulary context. This can be saved globally if desired, so
* that routines that don't have any other access to it (such as
* Unix-style signal handlers) can reach it.
*/
voccxdef *main_voc_ctx = 0;
#ifdef VOCW_IN_CACHE
vocwdef *vocwget(voccxdef *ctx, uint idx)
{
uint pg;
if (idx == VOCCXW_NONE)
return 0;
/* get the page we need */
pg = idx/VOCWPGSIZ;
/* if it's not locked, lock it */
if (pg != ctx->voccxwplck)
{
/* unlock the old page */
if (ctx->voccxwplck != MCMONINV)
mcmunlck(ctx->voccxmem, ctx->voccxwp[ctx->voccxwplck]);
/* lock the new page */
ctx->voccxwpgptr = (vocwdef *)mcmlck(ctx->voccxmem,
ctx->voccxwp[pg]);
ctx->voccxwplck = pg;
}
/* return the entry on that page */
return ctx->voccxwpgptr + (idx % VOCWPGSIZ);
}
#endif /*VOCW_IN_CACHE */
/* hash value is based on first 6 characters only to allow match-in-6 */
uint vochsh(uchar *t, int len)
{
uint ret = 0;
if (len > 6) len = 6;
for ( ; len ; --len, ++t)
ret = (ret + (uint)(vocisupper(*t) ? tolower(*t) : *t))
& (VOCHASHSIZ - 1);
return(ret);
}
/* copy vocabulary word, and convert to lower case */
static void voccpy(uchar *dst, uchar *src, int len)
{
for ( ; len ; --len, ++dst, ++src)
*dst = vocisupper(*src) ? tolower(*src) : *src;
}
/* allocate and set up a new vocwdef record, linking into a vocdef's list */
static void vocwset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
int classflg)
{
vocwdef *vw;
uint inx;
vocwdef *vw2;
/*
* look through the vocdef list to see if there's an existing entry
* with the DELETED marker -- if so, simply undelete it
*/
for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
inx = vw->vocwnxt, vw = vocwget(ctx, inx))
{
/* if this entry was deleted, and otherwise matches, undelete it */
if ((vw->vocwflg & VOCFDEL)
&& vw->vocwobj == objn && vw->vocwtyp == p)
{
/*
* Remove the deleted flag. We will otherwise leave the
* flags unchanged, since the VOCFDEL flag applies only to
* statically allocated objects, and hence the original
* flags should take precedence over any run-time flags.
*/
vw->vocwflg &= ~VOCFDEL;
/* we're done */
return;
}
}
/* make sure the word+object+type record isn't already defined */
for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
inx = vw->vocwnxt, vw = vocwget(ctx, inx))
{
if (vw->vocwobj == objn && vw->vocwtyp == p
&& (vw->vocwflg & VOCFCLASS) == (classflg & VOCFCLASS))
{
/* it matches - don't add a redundant record */
return;
}
}
/* look in the free list for an available vocwdef */
if (ctx->voccxwfre != VOCCXW_NONE)
{
inx = ctx->voccxwfre;
vw = vocwget(ctx, inx); /* get the free vocwdef */
ctx->voccxwfre = vw->vocwnxt; /* unlink from free list */
}
else
{
/* allocate another page of vocwdef's if necssary */
if ((ctx->voccxwalocnt % VOCWPGSIZ) == 0)
{
int pg = ctx->voccxwalocnt / VOCWPGSIZ;
/* make sure we haven't exceeded the available page count */
if (pg >= VOCWPGMAX) errsig(ctx->voccxerr, ERR_VOCMNPG);
/* allocate on the new page */
#ifdef VOCW_IN_CACHE
mcmalo(ctx->voccxmem, (ushort)(VOCWPGSIZ * sizeof(vocwdef)),
&ctx->voccxwp[pg]);
mcmunlck(ctx->voccxmem, ctx->voccxwp[pg]);
#else
ctx->voccxwp[pg] =
(vocwdef *)mchalo(ctx->voccxerr,
(VOCWPGSIZ * sizeof(vocwdef)),
"vocwset");
#endif
}
/* get the next entry, and increment count of used entries */
inx = ctx->voccxwalocnt++;
vw = vocwget(ctx, inx);
}
/* link the new vocwdef into the vocdef's relation list */
vw->vocwnxt = v->vocwlst;
v->vocwlst = inx;
/* set up the new vocwdef */
vw->vocwtyp = (uchar)p;
vw->vocwobj = objn;
vw->vocwflg = classflg;
/*
* Scan the list and make sure we're not adding a redundant verb.
* Don't bother with the warning if this is a class.
*/
if (p == PRP_VERB && (ctx->voccxflg & VOCCXFVWARN)
&& (vw->vocwflg & VOCFCLASS) == 0)
{
for (vw2 = vocwget(ctx, v->vocwlst) ; vw2 ;
vw2 = vocwget(ctx, vw2->vocwnxt))
{
/*
* if this is a different object, and it's not a class, and
* it's defined as a verb, warn about it
*/
if (vw2 != vw
&& (vw2->vocwflg & VOCFCLASS) == 0
&& vw2->vocwtyp == PRP_VERB)
{
if (v->vocln2 != 0)
errlog2(ctx->voccxerr, ERR_VOCREVB,
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt, v->voclen),
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt + v->voclen, v->vocln2));
else
errlog1(ctx->voccxerr, ERR_VOCREVB,
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt, v->voclen));
break;
}
}
}
}
/* set up a vocdef record, and link into hash table */
static void vocset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
int classflg, uchar *wrdtxt, int len,
uchar *wrd2, int len2)
{
uint hshval = vochsh(wrdtxt, len);
v->vocnxt = ctx->voccxhsh[hshval];
ctx->voccxhsh[hshval] = v;
v->voclen = len;
v->vocln2 = len2;
voccpy(v->voctxt, wrdtxt, len);
if (wrd2) voccpy(v->voctxt + len, wrd2, len2);
/* allocate and initialize a vocwdef for the object */
vocwset(ctx, v, p, objn, classflg);
}
/* internal addword - already parsed into two words and have lengths */
void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg,
uchar *wrdtxt, int len, uchar *wrd2, int len2)
{
vocdef *v;
vocdef *prv;
uint need;
uint hshval;
/* if the word is null, ignore it entirely */
if (len == 0 && len2 == 0)
return;
/* look for a vocdef entry with the same word text */
hshval = vochsh(wrdtxt, len);
for (v = ctx->voccxhsh[hshval] ; v ; v = v->vocnxt)
{
/* if it matches on both words, use this entry */
if (v->voclen == len && !memcmp(wrdtxt, v->voctxt, (size_t)len)
&& ((!wrd2 && v->vocln2 == 0)
|| (v->vocln2 == len2 &&
!memcmp(wrd2, v->voctxt + len, (size_t)len2))))
{
vocwset(ctx, v, p, objn, classflg);
return;
}
}
/* look for a free vocdef entry of the same size */
for (prv = (vocdef *)0, v = ctx->voccxfre ; v ; prv = v, v = v->vocnxt)
if (v->voclen == len + len2) break;
if (v)
{
/* we found something - unlink from free list */
if (prv) prv->vocnxt = v->vocnxt;
else ctx->voccxfre = v->vocnxt;
/* reuse the entry */
v->vocwlst = VOCCXW_NONE;
vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
return;
}
/* didn't find an existing vocdef; allocate a new one */
need = sizeof(vocdef) + len + len2 - 1;
if (ctx->voccxrem < need)
{
/* not enough space in current page; allocate a new one */
ctx->voccxpool = mchalo(ctx->voccxerr, VOCPGSIZ, "vocadd2");
ctx->voccxrem = VOCPGSIZ;
}
/* use top of current pool, and update pool pointer and size */
v = (vocdef *)ctx->voccxpool;
need = osrndsz(need);
ctx->voccxpool += need;
if (ctx->voccxrem > need) ctx->voccxrem -= need;
else ctx->voccxrem = 0;
/* set up new vocdef */
v->vocwlst = VOCCXW_NONE;
vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
}
static void voc_parse_words(char **wrdtxt, int *len, char **wrd2, int *len2)
{
/* get length and pointer to actual text */
*len = osrp2(*wrdtxt) - 2;
*wrdtxt += 2;
/* see if there's a second word - look for a space */
for (*wrd2 = *wrdtxt, *len2 = *len ; *len2 && !vocisspace(**wrd2) ;
++*wrd2, --*len2) ;
if (*len2)
{
*len -= *len2;
while (*len2 && vocisspace(**wrd2)) ++*wrd2, --*len2;
}
else
{
/* no space ==> no second word */
*wrd2 = (char *)0;
}
}
void vocadd(voccxdef *ctx, prpnum p, objnum objn, int classflg, char *wrdtxt)
{
int len;
char *wrd2;
int len2;
voc_parse_words(&wrdtxt, &len, &wrd2, &len2);
vocadd2(ctx, p, objn, classflg, (uchar *)wrdtxt, len, (uchar *)wrd2, len2);
}
/* make sure we have a page table entry for an object, allocating one if not */
void vocialo(voccxdef *ctx, objnum obj)
{
if (!ctx->voccxinh[obj >> 8])
{
ctx->voccxinh[obj >> 8] =
(vocidef **)mchalo(ctx->voccxerr,
(256 * sizeof(vocidef *)), "vocialo");
memset(ctx->voccxinh[obj >> 8], 0, (size_t)(256 * sizeof(vocidef *)));
}
}
/* add an inheritance/location record */
void vociadd(voccxdef *ctx, objnum obj, objnum loc,
int numsc, objnum *sc, int flags)
{
vocidef *v;
vocidef *min;
vocidef *prv;
vocidef *minprv = nullptr;
/* make sure we have a page table entry for this object */
vocialo(ctx, obj);
/* look in free list for an entry that's big enough */
for (prv = (vocidef *)0, min = (vocidef *)0, v = ctx->voccxifr ; v ;
prv = v, v = v->vocinxt)
{
if (v->vocinsc == numsc)
{
min = v;
minprv = prv;
break;
}
else if (v->vocinsc > numsc)
{
if (!min || v->vocinsc < min->vocinsc)
{
min = v;
minprv = prv;
}
}
}
if (!min)
{
uint need;
/* nothing in free list; allocate a new entry */
need = osrndsz(sizeof(vocidef) + (numsc - 1)*sizeof(objnum));
if (ctx->voccxilst + need >= VOCISIZ)
{
/* nothing left on current page; allocate a new page */
ctx->voccxip[++(ctx->voccxiplst)] =
mchalo(ctx->voccxerr, VOCISIZ, "vociadd");
ctx->voccxilst = 0;
}
/* allocate space out of current page */
v = (vocidef *)(ctx->voccxip[ctx->voccxiplst] + ctx->voccxilst);
ctx->voccxilst += need;
}
else
{
/* unlink from chain and use */
v = min;
if (minprv)
minprv->vocinxt = v->vocinxt;
else
ctx->voccxifr = v->vocinxt;
}
/* set up the entry */
if (vocinh(ctx, obj) != (vocidef *)0) errsig(ctx->voccxerr, ERR_VOCINUS);
v->vociloc = loc;
v->vociilc = MCMONINV;
v->vociflg = (flags & ~VOCIFXLAT);
v->vocinsc = numsc;
if (numsc)
{
if (flags & VOCIFXLAT)
{
int i;
for (i = 0 ; i < numsc ; ++i)
v->vocisc[i] = osrp2(&sc[i]);
}
else
memcpy(v->vocisc, sc, (size_t)(numsc * sizeof(objnum)));
}
vocinh(ctx, obj) = v; /* set page table entry */
}
/* revert all objects to original state, using inheritance records */
void vocrevert(voccxdef *vctx)
{
vocidef ***vpg;
vocidef **v;
int i;
int j;
objnum obj;
/*
* Go through the inheritance records. Delete each dynamically
* allocated object, and revert each static object to its original
* load state.
*/
for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
{
if (!*vpg) continue;
for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
{
if (*v)
{
/* if the object was dynamically allocated, delete it */
if ((*v)->vociflg & VOCIFNEW)
{
/* delete vocabulary and inheritance data for the object */
vocidel(vctx, obj);
vocdel(vctx, obj);
/* delete the object */
mcmfre(vctx->voccxmem, (mcmon)obj);
}
else
{
/* revert the object */
mcmrevert(vctx->voccxmem, (mcmon)obj);
}
}
}
}
/*
* Revert the vocabulary list: delete all newly added words, and
* undelete all original words marked as deleted.
*/
vocdel1(vctx, MCMONINV, (char *)0, 0, TRUE, TRUE, FALSE);
}
/* initialize voc context */
void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx,
runcxdef *runctx, objucxdef *undoctx,
int fuses, int daemons, int notifiers)
{
CLRSTRUCT(*vocctx);
vocctx->voccxerr = errctx;
vocctx->voccxiplst = (uint)-1;
vocctx->voccxilst = VOCISIZ;
vocctx->voccxmem = memctx;
vocctx->voccxrun = runctx;
vocctx->voccxundo = undoctx;
vocctx->voccxme =
vocctx->voccxme_init =
vocctx->voccxvtk =
vocctx->voccxstr =
vocctx->voccxnum =
vocctx->voccxit =
vocctx->voccxhim =
vocctx->voccxprd =
vocctx->voccxpre =
vocctx->voccxpre2 =
vocctx->voccxppc =
vocctx->voccxlsv =
vocctx->voccxpreinit =
vocctx->voccxper =
vocctx->voccxprom =
vocctx->voccxpostprom =
vocctx->voccxpdis =
vocctx->voccxper2 =
vocctx->voccxperp =
vocctx->voccxpdef =
vocctx->voccxpdef2 =
vocctx->voccxpask =
vocctx->voccxpask2 =
vocctx->voccxpask3 =
vocctx->voccxinitrestore =
vocctx->voccxpuv =
vocctx->voccxpnp =
vocctx->voccxpostact =
vocctx->voccxendcmd =
vocctx->voccxher = MCMONINV;
vocctx->voccxthc = 0;
#ifdef VOCW_IN_CACHE
vocctx->voccxwplck = MCMONINV;
#endif
vocctx->voccxactor = MCMONINV;
vocctx->voccxverb = MCMONINV;
vocctx->voccxprep = MCMONINV;
vocctx->voccxdobj = 0;
vocctx->voccxiobj = 0;
vocctx->voccxunknown = 0;
vocctx->voccxlastunk = 0;
vocctx->voc_stk_ptr = 0;
vocctx->voc_stk_cur = 0;
vocctx->voc_stk_end = 0;
/* allocate fuses, daemons, notifiers */
vocinialo(vocctx, &vocctx->voccxfus, (vocctx->voccxfuc = fuses));
vocinialo(vocctx, &vocctx->voccxdmn, (vocctx->voccxdmc = daemons));
vocinialo(vocctx, &vocctx->voccxalm, (vocctx->voccxalc = notifiers));
/* no entries in vocwdef free list yet */
vocctx->voccxwfre = VOCCXW_NONE;
}
/* uninitialize the voc context */
void vocterm(voccxdef *ctx)
{
/* delete the fuses, daemons, and notifiers */
voctermfree(ctx->voccxfus);
voctermfree(ctx->voccxdmn);
voctermfree(ctx->voccxalm);
/* delete the private stack */
if (ctx->voc_stk_ptr != 0)
mchfre(ctx->voc_stk_ptr);
}
/* clean up the vocab context */
void voctermfree(vocddef *what)
{
if (what != 0)
mchfre(what);
}
/*
* Iterate through all words for a particular object, calling a
* function with each vocwdef found. If objn == MCMONINV, we'll call
* the callback for every word.
*/
void voc_iterate(voccxdef *ctx, objnum objn,
void (*fn)(void *, vocdef *, vocwdef *), void *fnctx)
{
int i;
vocdef *v;
vocdef **vp;
vocwdef *vw;
uint idx;
/* go through each hash value looking for matching words */
for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
{
/* go through all words in this hash chain */
for (v = *vp ; v ; v = v->vocnxt)
{
/* go through each object relation for this word */
for (idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
idx = vw->vocwnxt, vw = vocwget(ctx, idx))
{
/*
* if this word is for this object, call the callback
*/
if (objn == MCMONINV || vw->vocwobj == objn)
(*fn)(fnctx, v, vw);
}
}
}
}
/* callback context for voc_count */
struct voc_count_ctx
{
int cnt;
int siz;
prpnum prp;
};
/* callback for voc_count */
static void voc_count_cb(void *ctx0, vocdef *voc, vocwdef *vocw)
{
struct voc_count_ctx *ctx = (struct voc_count_ctx *)ctx0;
VARUSED(vocw);
/*
* If it matches the property (or we want all properties), count
* it. Don't count deleted objects.
*/
if ((ctx->prp == 0 || ctx->prp == vocw->vocwtyp)
&& !(vocw->vocwflg & VOCFDEL))
{
/* count the word */
ctx->cnt++;
/* count the size */
ctx->siz += voc->voclen + voc->vocln2;
}
}
/*
* Get the number and size of words defined for an object. The size
* returns the total byte count from all the words involved. Do not
* include deleted words in the count.
*/
void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz)
{
struct voc_count_ctx fnctx;
/* set up the context with zero initial counts */
fnctx.cnt = 0;
fnctx.siz = 0;
fnctx.prp = prp;
/* iterate over all words for the object with our callback */
voc_iterate(ctx, objn, voc_count_cb, &fnctx);
/* return the data */
if (cnt) *cnt = fnctx.cnt;
if (siz) *siz = fnctx.siz;
}
/*
* Delete a particular word associated with an object, or all words if
* the word pointer is null. If the word isn't marked as added at
* runtime (i.e., the VOCFNEW flag is not set for the word), the word is
* simply marked as deleted, rather than being actually deleted.
* However, if the 'really_delete' flag is set, the word is actually
* deleted. If the 'revert' flag is true, this routine deletes _every_
* dynamically created word, and undeletes all dynamically deleted words
* that were in the original vocabulary.
*/
void vocdel1(voccxdef *ctx, objnum objn, char *wrd1, prpnum prp,
int really_delete, int revert, int keep_undo)
{
int i;
vocdef *v;
vocdef *prv;
vocdef *nxt;
vocdef **vp;
vocwdef *vw;
vocwdef *prvw;
vocwdef *nxtw;
uint nxtidx;
uint idx;
int deleted_vocdef;
char *wrd2 = nullptr;
int len1 = 0, len2 = 0;
int do_del;
char *orgwrd;
/* parse the word if provided */
orgwrd = wrd1;
if (wrd1)
voc_parse_words(&wrd1, &len1, &wrd2, &len2);
/* go through each hash value looking for matching words */
for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
{
/* go through all words in this hash chain */
for (prv = (vocdef *)0, v = *vp ; v ; v = nxt)
{
/* remember next word in hash chain */
nxt = v->vocnxt;
/* if this word doesn't match, skip it */
if (wrd1)
{
/* compare the first word */
if (v->voclen != len1
|| memicmp((char *)v->voctxt, wrd1, (size_t)len1))
{
prv = v;
continue;
}
/* if there's a second word, compare it as well */
if (wrd2 && (v->vocln2 != len2
|| memicmp((char *)v->voctxt + len1,
wrd2, (size_t)len2)))
{
prv = v;
continue;
}
}
/* presume we're not going to delete this vocdef */
deleted_vocdef = FALSE;
/* go through all object relations for this word */
for (prvw = 0, idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
vw = nxtw, idx = nxtidx)
{
/* remember next word in relation list */
nxtidx = vw->vocwnxt;
nxtw = vocwget(ctx, nxtidx);
/*
* figure out whether to delete this word, based on the
* caller's specified operating mode
*/
if (revert)
{
/* if reverting, delete all new words */
do_del = (vw->vocwflg & VOCFNEW);
/* also, remove the DELETED flag if present */
vw->vocwflg &= ~VOCFDEL;
}
else
{
/*
* delete the word if the object number matches,
* AND either we're not searching for a specific
* vocabulary word (in which case wrd1 will be null)
* or the word matches the vocabulary word we're
* seeking (in which case the part of speech must
* match)
*/
do_del = (vw->vocwobj == objn
&& (wrd1 == 0 || vw->vocwtyp == prp));
/*
* if we're not in really_delete mode, and the word
* matches and it wasn't dynamically added at
* run-time, simply mark it as deleted -- this will
* allow it to be undeleted if the game is reverted
* to RESTART conditions
*/
if (do_del && !really_delete && !(vw->vocwflg & VOCFNEW))
{
/* geneate undo for the operation */
if (keep_undo && orgwrd)
vocdusave_delwrd(ctx, objn, prp,
vw->vocwflg, orgwrd);
/* just mark the word for deletion, but keep vw */
vw->vocwflg |= VOCFDEL;
do_del = FALSE;
}
}
/* now delete the structure if we decided we should */
if (do_del)
{
/* geneate undo for the operation */
if (keep_undo && orgwrd)
vocdusave_delwrd(ctx, objn, prp, vw->vocwflg, orgwrd);
/* unlink this vocwdef from the vocdef's list */
if (prvw)
prvw->vocwnxt = vw->vocwnxt;
else
v->vocwlst = vw->vocwnxt;
/* link the vocwdef into the vocwdef free list */
vw->vocwnxt = ctx->voccxwfre;
ctx->voccxwfre = idx;
/*
* if there's nothing left in the vocdef's list,
* delete the entire vocdef as well
*/
if (v->vocwlst == VOCCXW_NONE)
{
if (prv) prv->vocnxt = v->vocnxt;
else *vp = v->vocnxt;
/* link into free chain */
v->vocnxt = ctx->voccxfre;
ctx->voccxfre = v;
/* note that it's been deleted */
deleted_vocdef = TRUE;
}
}
else
{
/* we're not deleting the word, so move prvw forward */
prvw = vw;
}
}
/* if we didn't delete this vocdef, move prv forward onto it */
if (!deleted_vocdef)
prv = v;
}
}
}
/* delete all vocabulary for an object */
void vocdel(voccxdef *ctx, objnum objn)
{
vocdel1(ctx, objn, (char *)0, (prpnum)0, TRUE, FALSE, FALSE);
}
/* delete object inheritance records for a particular object */
void vocidel(voccxdef *ctx, objnum obj)
{
vocidef *v;
/* get entry out of page table, and clear page table slot */
v = vocinh(ctx, obj);
vocinh(ctx, obj) = (vocidef *)0;
/* link into free list */
if (v)
{
v->vocinxt = ctx->voccxifr;
ctx->voccxifr = v;
}
}
/*
* Find template matching a verb+object+prep combination; return TRUE
* if a suitable template is found, FALSE otherwise.
*/
int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep,
uchar *tplout, int *newstyle)
{
uchar *tplptr;
uchar *thistpl;
int found;
int tplcnt;
uint tplofs;
objnum verb;
/* look for a new-style template first */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, FALSE);
if (tplofs)
{
/* flag the presence of the new-style template */
*newstyle = TRUE;
}
else
{
/* no new-style template - look for old version */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, FALSE);
*newstyle = FALSE;
}
/* inherit templates until we run out of them */
for (;;)
{
/* if we found something already, use it */
if (tplofs)
{
size_t siz;
/* figure the size of this template style */
siz = (*newstyle ? VOCTPL2SIZ : VOCTPLSIZ);
/* lock the verb object, and get the property value pointer */
tplptr = mcmlck(ctx->voccxmem, verb);
thistpl = prpvalp(tplptr + tplofs);
/* first byte is number of templates in array */
tplcnt = *thistpl++;
/* look for a template that matches the preposition object */
for (found = FALSE ; tplcnt ; thistpl += siz, --tplcnt)
{
if (voctplpr(thistpl) == prep)
{
found = TRUE;
break;
}
}
/* unlock the object and return the value if we found one */
mcmunlck(ctx->voccxmem, verb);
if (found)
{
memcpy(tplout, thistpl, siz);
return(TRUE);
}
}
/* try inheriting a template (new-style, then old-style) */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, TRUE);
if (tplofs)
*newstyle = TRUE;
else
{
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, TRUE);
*newstyle = FALSE;
}
/* return not-found if we couldn't inherit it */
if (!tplofs)
return FALSE;
/* use the newly found verb */
verb_in = verb;
}
}
/*
* Set the "Me" object
*/
void voc_set_me(voccxdef *ctx, objnum new_me)
{
/* save undo for the change */
vocdusave_me(ctx, ctx->voccxme);
/* set the new "Me" object */
ctx->voccxme = new_me;
}
} // End of namespace TADS2
} // End of namespace TADS

View File

@ -27,6 +27,7 @@
#ifndef GLK_TADS_TADS2_VOCABULARY
#define GLK_TADS_TADS2_VOCABULARY
#include "common/util.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/property.h"
@ -645,11 +646,11 @@ void vocdusave_me(voccxdef *ctx, objnum old_me);
uint vochsh(uchar *t, int len);
/* TADS versions of isalpha, isspace, isdigit, etc */
#define vocisupper(c) ((uchar)(c) <= 127 && isupper((uchar)(c)))
#define vocislower(c) ((uchar)(c) <= 127 && islower((uchar)(c)))
#define vocisalpha(c) ((uchar)(c) > 127 || isalpha((uchar)(c)))
#define vocisspace(c) ((uchar)(c) <= 127 && isspace((uchar)(c)))
#define vocisdigit(c) ((uchar)(c) <= 127 && isdigit((uchar)(c)))
#define vocisupper(c) ((uchar)(c) <= 127 && Common::isUpper((uchar)(c)))
#define vocislower(c) ((uchar)(c) <= 127 && Common::isLower((uchar)(c)))
#define vocisalpha(c) ((uchar)(c) > 127 || Common::isAlpha((uchar)(c)))
#define vocisspace(c) ((uchar)(c) <= 127 && Common::isSpace((uchar)(c)))
#define vocisdigit(c) ((uchar)(c) <= 127 && Common::isDigit((uchar)(c)))
/*