#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/TADS2/SUP.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
#endif

/* 
 *   Copyright (c) 1992, 2002 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  sup.c - set up game after compilation
Function
  performs post-compilation setup tasks, such as setting up contents lists
Notes
  none
Modified
  12/16/92 MJRoberts     - add TADS/Graphic extensions
  03/28/92 MJRoberts     - creation
*/

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "os.h"
#include "std.h"
#include "obj.h"
#include "prp.h"
#include "dat.h"
#include "tok.h"
#include "mcm.h"
#include "mch.h"
#include "sup.h"
#include "bif.h"

/*
 *   Special character sequence description table for TADS language.
 *   Note that operators that start with common sequences must be grouped
 *   together, with the shorter sequences preceding the longer sequences.
 *   For example, ":" and ":=" must be adjacent, and ":" must precede
 *   ":=".  Other than this restriction, the order of tokens doesn't
 *   matter.  
 */
tokldef supsctab[] =
{
    { TOKTCOLON,   ":"  },
    { TOKTASSIGN,  ":=" },
    { TOKTLT,      "<"  },
    { TOKTLE,      "<=" },
    { TOKTSHL,     "<<" },
    { TOKTSHLEQ,   "<<="},
    { TOKTNE,      "<>" },
    { TOKTGT,      ">"  },
    { TOKTSHR,     ">>" },
    { TOKTSHREQ,   ">>="},
    { TOKTGE,      ">=" },
    { TOKTLPAR,    "("  },
    { TOKTRPAR,    ")"  },
    { TOKTPLUS,    "+"  },
    { TOKTINC,     "++" },
    { TOKTPLEQ,    "+=" },
    { TOKTMINUS,   "-"  },
    { TOKTPOINTER, "->" },
    { TOKTDEC,     "--" },
    { TOKTMINEQ,   "-=" },
    { TOKTDIV,     "/"  },
    { TOKTMOD,     "%"  },
    { TOKTMODEQ,   "%=" },
    { TOKTDIVEQ,   "/=" },
    { TOKTTIMES,   "*"  },
    { TOKTTIMEQ,   "*=" },
    { TOKTEQ,      "="  },
    { TOKTEQEQ,    "==" },
    { TOKTLBRACK,  "["  },
    { TOKTRBRACK,  "]"  },
    { TOKTLBRACE,  "{"  },
    { TOKTRBRACE,  "}"  },
    { TOKTSEM,     ";"  },
    { TOKTCOMMA,   ","  },
    { TOKTDOT,     "."  },
    { TOKTELLIPSIS,"..." },
    { TOKTPOUND,   "#"  },
    { TOKTBAND,    "&"  },
    { TOKTBANDEQ,  "&=" },
    { TOKTAND,     "&&" },
    { TOKTBOR,     "|"  },
    { TOKTBOREQ,   "|=" },
    { TOKTOR,      "||" },
    { TOKTQUESTION,"?"  },
    { TOKTDSTRING, "\"" },
    { TOKTSSTRING, "'"  },
    { TOKTNOT,     "!"  },
    { TOKTNE,      "!=" },
    { TOKTXOR,     "^"  },
    { TOKTXOREQ,   "^=" },
    { TOKTTILDE,   "~"  },
    { 0,           ""   }
};

typedef struct supkwdef supkwdef;
struct supkwdef
{
    char *supkwnam;
    int   supkwtok;
};

static supkwdef supkwtab[] =
{
    { "not", TOKTNOT },
    { "if", TOKTIF },
    { "else", TOKTELSE },
    { "while", TOKTWHILE },
    { "break", TOKTBREAK },
    { "continue", TOKTCONTINUE },
    { "exit", TOKTEXIT },
    { "abort", TOKTABORT },
    { "and", TOKTAND },
    { "or", TOKTOR },
    { "function", TOKTFUNCTION },
    { "return", TOKTRETURN },
    { "local", TOKTLOCAL },
    { "object", TOKTOBJECT },
    { "nil", TOKTNIL },
    { "true", TOKTTRUE },
    { "pass", TOKTPASS },
    { "askdo", TOKTASKDO },
    { "askio", TOKTASKIO },
    { "ioSynonym", TOKTIOSYN },
    { "doSynonym", TOKTDOSYN },
    { "external", TOKTEXTERN },
    { "formatstring", TOKTFORMAT },
    { "compoundWord", TOKTCOMPOUND },
    { "specialWords", TOKTSPECIAL },
    { "class", TOKTCLASS },
        
    /* new keywords for V2 */
    { "\002", 0 },                  /* special flag for start of v2 section */
    { "for", TOKTFOR },
    { "\001", 0 },                /* special flag that "do" is next keyword */
    { "do", TOKTDO },
    { "switch", TOKTSWITCH },
    { "case", TOKTCASE },
    { "default", TOKTDEFAULT },
    { "goto", TOKTGOTO },
    { "replace", TOKTREPLACE },
    { "modify", TOKTMODIFY },

    { "new", TOKTNEW },
    { "delete", TOKTDELETE },
    { (char *)0, 0 }
};

typedef struct supprdef supprdef;
struct supprdef
{
    char   *supprnam;
    prpnum  supprval;
};

static supprdef supprtab[] =
{
    { "verb", PRP_VERB },
    { "noun", PRP_NOUN },
    { "adjective", PRP_ADJ },
    { "preposition", PRP_PREP },
    { "article", PRP_ARTICLE },
    { "plural", PRP_PLURAL },

    /* add some more built-in properties */
    { "doAction", PRP_DOACTION },
    { "ioAction", PRP_IOACTION },
    { "sdesc", PRP_SDESC },
    { "thedesc", PRP_THEDESC },
    { "ioDefault", PRP_IODEFAULT },
    { "doDefault", PRP_DODEFAULT },
    { "location", PRP_LOCATION },
    { "value", PRP_VALUE },
    { "roomAction", PRP_ROOMACTION },
    { "actorAction", PRP_ACTORACTION },
    { "contents", PRP_CONTENTS },
    { "prepDefault", PRP_PREPDEFAULT },
    { "verActor", PRP_VERACTOR },
    { "validDo", PRP_VALIDDO },
    { "validIo", PRP_VALIDIO },
    { "lookAround", PRP_LOOKAROUND },
    { "roomCheck", PRP_ROOMCHECK },
    { "statusLine", PRP_STATUSLINE },
    { "locationOK", PRP_LOCOK },
    { "isVisible", PRP_ISVIS },
    { "cantReach", PRP_NOREACH },
    { "isHim", PRP_ISHIM },
    { "isHer", PRP_ISHER },
    { "action", PRP_ACTION },
    { "validDoList", PRP_VALDOLIST },
    { "validIoList", PRP_VALIOLIST },
    { "dobjGen", PRP_DOBJGEN },
    { "iobjGen", PRP_IOBJGEN },
    { "nilPrep", PRP_NILPREP },
    { "rejectMultiDobj", PRP_REJECTMDO },
    { "moveInto", PRP_MOVEINTO },
    { "construct", PRP_CONSTRUCT },
    { "destruct", PRP_DESTRUCT },
    { "validActor", PRP_VALIDACTOR },
    { "preferredActor", PRP_PREFACTOR },
    { "isEquivalent", PRP_ISEQUIV },
    { "adesc", PRP_ADESC },
    { "multisdesc", PRP_MULTISDESC },
    { "anyvalue", PRP_ANYVALUE },
    { "newNumbered", PRP_NEWNUMOBJ },
    { "parseUnknownDobj", PRP_PARSEUNKNOWNDOBJ },
    { "parseUnknownIobj", PRP_PARSEUNKNOWNIOBJ },
    { "dobjCheck", PRP_DOBJCHECK },
    { "iobjCheck", PRP_IOBJCHECK },
    { "verbAction", PRP_VERBACTION },
    { "disambigDobj", PRP_DISAMBIGDO },
    { "disambigIobj", PRP_DISAMBIGIO },
    { "prefixdesc", PRP_PREFIXDESC },
    { "isThem", PRP_ISTHEM },

    /* still more - TADS/Graphic properties */
    { "gp_picture", PRP_GP_PIC },
    { "gp_name", PRP_GP_NAME },
    { "gp_defverb", PRP_GP_DEFVERB },
    { "gp_active", PRP_GP_ACTIVE },
    { "gp_hotlist", PRP_GP_HOTLIST },
    { "gp_icon", PRP_GP_ICON },
    { "gp_defverb2", PRP_GP_DEFVERB2 },
    { "gp_defprep", PRP_GP_DEFPREP },
    { "gp_hotid", PRP_GP_HOTID },
    { "gp_overlay", PRP_GP_OVERLAY },
    { "gp_hotx", PRP_GP_HOTX },
    { "gp_hoty", PRP_GP_HOTY },

    /* flag end of list with null property name */
    { (char *)0, 0 }
};

/* define a built-in symbol */
static void supaddsym(toktdef *tab, char *txt, int styp, int sval,
                      int casefold)
{
    char buf[40];

    if (casefold)
    {
        strcpy(buf, txt);
        os_strlwr(buf);
        txt = buf;
    }
    (*tab->toktfadd)(tab, txt, (int)strlen(txt), styp, sval, tokhsh(txt));
}

/* add a built-in function to a symbol table */
static void supaddbi(void (*bif[])(bifcxdef *, int),
                     toktdef *tab, char *txt,
                     void (*fn)(bifcxdef *, int), int num, int casefold)
{
    supaddsym(tab, txt, TOKSTBIFN, num, casefold);
    bif[num] = fn;
}

/* set up reserved words: built-in functions and properties, keywords, etc */
void suprsrv(supcxdef *sup, void (*bif[])(bifcxdef *, int),
             toktdef *tab, int max, int v1compat, char *new_do,
             int casefold)
{
    supkwdef *kw;
    supbidef *p;
    int       i;
    supprdef *pr;
    extern    supbidef osfar_t supbitab[];
    int       do_kw = FALSE;
    char     *kwname;
    char      buf[40];

    /* add built-in functions */
    for (p = supbitab, i = 0 ; p->supbinam ; ++i, ++p)
    {
        if (i >= max) errsig(sup->supcxerr, ERR_MANYBIF);
        supaddbi(bif, tab, p->supbinam, p->supbifn, i, casefold);
    }

    /* add keywords */
    for (kw = supkwtab ; kw->supkwnam ; ++kw)
    {
        if (kw->supkwnam[0] == '\002')
        {
            if (v1compat) break;               /* no v2 keywords - quit now */
            else continue;          /* keep going, but skip this flag entry */
        }
        
        /* if this is the "do" keyword, change to user-supplied value */
        if (do_kw && new_do)
            kwname = new_do;
        else
            kwname = kw->supkwnam;
        
        if (kw->supkwnam[0] == '\001')
        {
            do_kw = TRUE;
            continue;
        }
        else
            do_kw = FALSE;

        if (casefold)
        {
            strcpy(buf, kwname);
            os_strlwr(buf);
            kwname = buf;
        }
        (*tab->toktfadd)(tab, kwname, (int)strlen(kwname),
                         TOKSTKW, kw->supkwtok, tokhsh(kwname));
    }

    /* add pseudo-variables */
    supaddsym(tab, "self", TOKSTSELF, 0, casefold);
    supaddsym(tab, "inherited", TOKSTINHERIT, 0, casefold);
    supaddsym(tab, "argcount", TOKSTARGC, 0, casefold);
    
    /* add built-in properties */
    for (pr = supprtab ; pr->supprnam ; ++pr)
        supaddsym(tab, pr->supprnam, TOKSTPROP, pr->supprval, casefold);
}

/* get name of an object out of symbol table */
void supgnam(char *buf, tokthdef *tab, objnum objn)
{
    toksdef sym;
    
    if (!tab)
    {
        strcpy(buf, "<NO SYMBOL TABLE>");
        return;
    }
    
    if (tokthfind((toktdef *)tab, TOKSTOBJ, (uint)objn, &sym)
        || tokthfind((toktdef *)tab, TOKSTFWDOBJ, (uint)objn, &sym))
    {
        memcpy(buf, sym.toksnam, (size_t)sym.tokslen);
        buf[sym.tokslen] = '\0';
        return;
    }

    strcpy(buf, "<UNKNOWN>");
}

/* set up inherited vocabulary */
void supivoc(supcxdef *ctx)
{
    vocidef ***vpg;
    vocidef  **v;
    voccxdef  *voc = ctx->supcxvoc;
    int        i;
    int        j;
    objnum     obj;
    
    /* delete all existing inherited words */
    vocdelinh(voc);
    
    for (vpg = voc->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
    {
        if (!*vpg) continue;                     /* no entries on this page */
        for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
        {
            /* if it's not a class, inherit vocabulary for the object */
            if (!*v) continue;
            if (!((*v)->vociflg & VOCIFCLASS))
            {
                (*v)->vociilc = MCMONINV;      /* no inherited location yet */
                supivoc1(ctx, ctx->supcxvoc, *v, obj, FALSE, 0);
            }
        }
    }
}

/* find a single required object, by name */
static void supfind1(errcxdef *ec, toktdef *tab, char *nam, objnum *objp,
                     int required, int *errp, int warnlevel, int casefold)
{
    toksdef sym;
    int     namel = strlen(nam);
    char    buf[40];

    if (casefold)
    {
        strcpy(buf, nam);
        os_strlwr(buf);
        nam = buf;
    }
    if ((*tab->toktfsea)(tab, nam, namel, tokhsh(nam), &sym))
    {
        *objp = (objnum)sym.toksval;
    }
    else
    {
        if (required || warnlevel > 1)
            errlog1(ec, (required ? ERR_RQOBJNF : ERR_WRNONF),
                    ERRTSTR, errstr(ec, nam, namel));
        *objp = MCMONINV;
        if (required) *errp = 1;
    }
}

/* find required objects/functions */
void supfind(errcxdef *ec, tokthdef *htab, voccxdef *voc,
             objnum *preinit, int warnlevel, int cf)
{
    int      err = 0;
    toktdef *tab = &htab->tokthsc;

    /* look up the required and optional symbols */
    supfind1(ec, tab, "Me", &voc->voccxme, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "takeVerb", &voc->voccxvtk, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "strObj", &voc->voccxstr, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "numObj", &voc->voccxnum, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "pardon", &voc->voccxprd, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "againVerb", &voc->voccxvag, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "init", &voc->voccxini, TRUE, &err, warnlevel, cf);
    supfind1(ec, tab, "preinit", preinit, FALSE, &err, warnlevel, cf);
    supfind1(ec, tab, "preparse", &voc->voccxpre, FALSE, &err, warnlevel, cf);
    supfind1(ec, tab, "preparseExt", &voc->voccxpre2, FALSE, &err,
             warnlevel, cf);
    supfind1(ec, tab, "parseError", &voc->voccxper, FALSE, &err, warnlevel,
             cf);
    supfind1(ec, tab, "commandPrompt", &voc->voccxprom, FALSE, &err,
             warnlevel, cf);
    supfind1(ec, tab, "parseDisambig", &voc->voccxpdis, FALSE, &err,
             warnlevel, cf);
    supfind1(ec, tab, "parseError2", &voc->voccxper2, FALSE, &err, warnlevel,
             cf);
    supfind1(ec, tab, "parseDefault", &voc->voccxpdef, FALSE, &err, warnlevel,
             cf);
    supfind1(ec, tab, "parseDefaultExt", &voc->voccxpdef2, FALSE, &err,
             warnlevel, cf);
    supfind1(ec, tab, "parseAskobj", &voc->voccxpask, FALSE, &err, warnlevel,
             cf);
    supfind1(ec, tab, "preparseCmd", &voc->voccxppc, FALSE, &err, warnlevel,
             cf);
    supfind1(ec, tab, "parseAskobjActor", &voc->voccxpask2, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "parseAskobjIndirect", &voc->voccxpask3, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "parseErrorParam", &voc->voccxperp, FALSE, &err,
             warnlevel, cf);
    supfind1(ec, tab, "commandAfterRead", &voc->voccxpostprom, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "initRestore", &voc->voccxinitrestore, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "parseUnknownVerb", &voc->voccxpuv, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "parseNounPhrase", &voc->voccxpnp, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "postAction", &voc->voccxpostact, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "endCommand", &voc->voccxendcmd, FALSE,
             &err, warnlevel, cf);
    supfind1(ec, tab, "preCommand", &voc->voccxprecmd, FALSE,
             &err, warnlevel, cf);

    /* "Me" is always the initial Me object */
    voc->voccxme_init = voc->voccxme;

    /* if we encountered any errors, signal the problem */
    if (err)
        errsig(ec, ERR_UNDEF);
}
