sourcepawn/compiler/sc1.c
author David Anderson <dvander@alliedmods.net>
Wed Feb 25 20:07:12 2009 -0500 (2009-02-25)
changeset 2607 f3b8bbe17f85
parent 2317 a7380514e63c
child 2665 f6e84c453287
child 2705 4bb5b5669919
permissions -rw-r--r--
Backed out changeset fa286305bd3f
dvander@27
     1
/*  Pawn compiler
dvander@27
     2
 *
dvander@27
     3
 *  Function and variable definition and declaration, statement parser.
dvander@27
     4
 *
dvander@27
     5
 *  Copyright (c) ITB CompuPhase, 1997-2006
dvander@27
     6
 *
dvander@27
     7
 *  This software is provided "as-is", without any express or implied warranty.
dvander@27
     8
 *  In no event will the authors be held liable for any damages arising from
dvander@27
     9
 *  the use of this software.
dvander@27
    10
 *
dvander@27
    11
 *  Permission is granted to anyone to use this software for any purpose,
dvander@27
    12
 *  including commercial applications, and to alter it and redistribute it
dvander@27
    13
 *  freely, subject to the following restrictions:
dvander@27
    14
 *
dvander@27
    15
 *  1.  The origin of this software must not be misrepresented; you must not
dvander@27
    16
 *      claim that you wrote the original software. If you use this software in
dvander@27
    17
 *      a product, an acknowledgment in the product documentation would be
dvander@27
    18
 *      appreciated but is not required.
dvander@27
    19
 *  2.  Altered source versions must be plainly marked as such, and must not be
dvander@27
    20
 *      misrepresented as being the original software.
dvander@27
    21
 *  3.  This notice may not be removed or altered from any source distribution.
dvander@27
    22
 *
dvander@365
    23
 *  Version: $Id$
dvander@27
    24
 */
dvander@27
    25
#include <assert.h>
dvander@27
    26
#include <ctype.h>
dvander@27
    27
#include <limits.h>
dvander@27
    28
#include <stdarg.h>
dvander@27
    29
#include <stdio.h>
dvander@27
    30
#include <stdlib.h>
dvander@27
    31
#include <string.h>
dvander@27
    32
dvander@27
    33
#if defined	__WIN32__ || defined _WIN32 || defined __MSDOS__
dvander@27
    34
  #include <conio.h>
dvander@27
    35
  #include <io.h>
dvander@27
    36
#endif
dvander@27
    37
dvander@27
    38
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
dvander@27
    39
  #include <sclinux.h>
dvander@27
    40
  #include <binreloc.h> /* from BinReloc, see www.autopackage.org */
dvander@27
    41
#endif
dvander@27
    42
dvander@27
    43
#if defined FORTIFY
dvander@27
    44
  #include <alloc/fortify.h>
dvander@27
    45
#endif
dvander@27
    46
dvander@27
    47
#if defined __BORLANDC__ || defined __WATCOMC__
dvander@27
    48
  #include <dos.h>
dvander@27
    49
  static unsigned total_drives; /* dummy variable */
dvander@27
    50
  #define dos_setdrive(i)       _dos_setdrive(i,&total_drives)
dvander@27
    51
#elif defined _MSC_VER && defined _WIN32
dvander@27
    52
  #include <direct.h>           /* for _chdrive() */
dvander@27
    53
  #define dos_setdrive(i)       _chdrive(i)
dvander@27
    54
#endif
dvander@27
    55
#if defined __BORLANDC__
dvander@27
    56
  #include <dir.h>              /* for chdir() */
dvander@27
    57
#elif defined __WATCOMC__
dvander@27
    58
  #include <direct.h>           /* for chdir() */
dvander@27
    59
#endif
dvander@27
    60
#if defined __WIN32__ || defined _WIN32 || defined _Windows
dvander@27
    61
  #include <windows.h>
dvander@27
    62
#endif
dvander@27
    63
dvander@27
    64
#include <time.h>
dvander@27
    65
dvander@27
    66
#include "lstring.h"
dvander@27
    67
#include "sc.h"
dvander@386
    68
#include "svn_version.h"
dvander@116
    69
#include "sctracker.h"
dvander@386
    70
#define VERSION_STR "3.2.3636"
dvander@27
    71
#define VERSION_INT 0x0302
dvander@27
    72
ds@632
    73
int pc_anytag = 0;
dvander@132
    74
int pc_functag = 0;
dvander@151
    75
int pc_tag_string = 0;
dvander@132
    76
dvander@27
    77
static void resetglobals(void);
dvander@27
    78
static void initglobals(void);
dvander@27
    79
static char *get_extension(char *filename);
dvander@27
    80
static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
dvander@27
    81
                   char *rname,char *codepage);
dvander@27
    82
static void setconfig(char *root);
dvander@27
    83
static void setcaption(void);
dvander@27
    84
static void about(void);
dvander@27
    85
static void setconstants(void);
dvander@27
    86
static void parse(void);
dvander@27
    87
static void dumplits(void);
dvander@27
    88
static void dumpzero(int count);
dvander@27
    89
static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst);
dvander@27
    90
static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
dvander@27
    91
                    int stock,int fconst);
dvander@167
    92
static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
dvander@27
    93
static int declloc(int fstatic);
dvander@27
    94
static void decl_const(int table);
dvander@167
    95
static void declstruct();
dvander@27
    96
static void decl_enum(int table);
dvander@27
    97
static cell needsub(int *tag,constvalue **enumroot);
dvander@27
    98
static void initials(int ident,int tag,cell *size,int dim[],int numdim,
dvander@27
    99
                     constvalue *enumroot);
dvander@27
   100
static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
dvander@27
   101
                      int startlit,int counteddim[],constvalue *lastdim,
dvander@27
   102
                      constvalue *enumroot,int *errorfound);
dvander@27
   103
static cell initvector(int ident,int tag,cell size,int fillzero,
dvander@27
   104
                       constvalue *enumroot,int *errorfound);
dvander@27
   105
static cell init(int ident,int *tag,int *errorfound);
dvander@27
   106
static int getstates(const char *funcname);
dvander@27
   107
static void attachstatelist(symbol *sym, int state_id);
dvander@27
   108
static void funcstub(int fnative);
dvander@27
   109
static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
dvander@27
   110
static int declargs(symbol *sym,int chkshadow);
dvander@27
   111
static void doarg(char *name,int ident,int offset,int tags[],int numtags,
dvander@27
   112
                  int fpublic,int fconst,int chkshadow,arginfo *arg);
dvander@27
   113
static void make_report(symbol *root,FILE *log,char *sourcefile);
dvander@27
   114
static void reduce_referrers(symbol *root);
dvander@27
   115
static long max_stacksize(symbol *root,int *recursion);
dvander@27
   116
static int testsymbols(symbol *root,int level,int testlabs,int testconst);
dvander@27
   117
static void destructsymbols(symbol *root,int level);
dvander@27
   118
static constvalue *find_constval_byval(constvalue *table,cell val);
faluco@62
   119
static symbol *fetchlab(char *name);
dvander@27
   120
static void statement(int *lastindent,int allow_decl);
faluco@62
   121
static void compound(int stmt_sameline,int starttok);
faluco@62
   122
static int test(int label,int parens,int invert);
dvander@27
   123
static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
dvander@27
   124
                  int *tag,symbol **symptr,int chkfuncresult);
dvander@152
   125
static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
dvander@152
   126
				  int *tag,symbol **symptr,int chkfuncresult,value *lval);
dvander@27
   127
static void doassert(void);
dvander@27
   128
static void doexit(void);
faluco@62
   129
static int doif(void);
faluco@62
   130
static int dowhile(void);
faluco@62
   131
static int dodo(void);
faluco@62
   132
static int dofor(void);
dvander@27
   133
static void doswitch(void);
dvander@27
   134
static void dogoto(void);
dvander@27
   135
static void dolabel(void);
dvander@27
   136
static void doreturn(void);
dvander@338
   137
static void dofuncenum(int listmode);
dvander@27
   138
static void dobreak(void);
dvander@27
   139
static void docont(void);
dvander@27
   140
static void dosleep(void);
dvander@27
   141
static void dostate(void);
dvander@27
   142
static void addwhile(int *ptr);
dvander@27
   143
static void delwhile(void);
dvander@27
   144
static int *readwhile(void);
dvander@27
   145
static void inst_datetime_defines(void);
dvander@1978
   146
static void inst_binary_name(char *binfname);
dvander@27
   147
faluco@62
   148
enum {
faluco@62
   149
  TEST_PLAIN,           /* no parentheses */
faluco@62
   150
  TEST_THEN,            /* '(' <expr> ')' or <expr> 'then' */
faluco@62
   151
  TEST_DO,              /* '(' <expr> ')' or <expr> 'do' */
faluco@62
   152
  TEST_OPT,             /* '(' <expr> ')' or <expr> */
faluco@62
   153
};
dvander@27
   154
static int norun      = 0;      /* the compiler never ran */
faluco@62
   155
static int autozero   = 1;      /* if 1 will zero out the variable, if 0 omit the zeroing */
dvander@27
   156
static int lastst     = 0;      /* last executed statement type */
dvander@27
   157
static int nestlevel  = 0;      /* number of active (open) compound statements */
faluco@62
   158
static int endlessloop= 0;      /* nesting level of endless loop */
dvander@27
   159
static int rettype    = 0;      /* the type that a "return" expression should have */
dvander@27
   160
static int skipinput  = 0;      /* number of lines to skip from the first input file */
dvander@27
   161
static int optproccall = TRUE;  /* support "procedure call" */
dvander@27
   162
static int verbosity  = 1;      /* verbosity level, 0=quiet, 1=normal, 2=verbose */
dvander@27
   163
static int sc_reparse = 0;      /* needs 3th parse because of changed prototypes? */
dvander@27
   164
static int sc_parsenum = 0;     /* number of the extra parses */
dvander@27
   165
static int wq[wqTABSZ];         /* "while queue", internal stack for nested loops */
dvander@27
   166
static int *wqptr;              /* pointer to next entry */
dvander@27
   167
#if !defined SC_LIGHT
dvander@27
   168
  static char sc_rootpath[_MAX_PATH];
dvander@27
   169
  static char *sc_documentation=NULL;/* main documentation */
dvander@27
   170
#endif
dvander@27
   171
#if defined	__WIN32__ || defined _WIN32 || defined _Windows
dvander@27
   172
  static HWND hwndFinish = 0;
dvander@27
   173
#endif
dvander@27
   174
dvander@510
   175
SC_VDECL int glbstringread = 0;
dvander@406
   176
char g_tmpfile[_MAX_PATH] = {0};
dvander@406
   177
dvander@27
   178
/*  "main" of the compiler
dvander@27
   179
 */
dvander@27
   180
#if defined __cplusplus
dvander@27
   181
  extern "C"
dvander@27
   182
#endif
dvander@27
   183
int pc_compile(int argc, char *argv[])
dvander@27
   184
{
dvander@27
   185
  int entry,i,jmpcode;
dvander@27
   186
  int retcode;
dvander@27
   187
  char incfname[_MAX_PATH];
dvander@27
   188
  char reportname[_MAX_PATH];
dvander@27
   189
  char codepage[MAXCODEPAGE+1];
dvander@27
   190
  FILE *binf;
dvander@27
   191
  void *inpfmark;
dvander@27
   192
  int lcl_packstr,lcl_needsemicolon,lcl_tabsize;
dvander@27
   193
  #if !defined SC_LIGHT
dvander@27
   194
    int hdrsize=0;
dvander@27
   195
  #endif
dvander@27
   196
  char *ptr;
dvander@27
   197
dvander@27
   198
  /* set global variables to their initial value */
dvander@27
   199
  binf=NULL;
dvander@27
   200
  initglobals();
dvander@27
   201
  errorset(sRESET,0);
dvander@27
   202
  errorset(sEXPRRELEASE,0);
dvander@27
   203
  lexinit();
dvander@27
   204
dvander@27
   205
  /* make sure that we clean up on a fatal error; do this before the first
dvander@27
   206
   * call to error(). */
dvander@27
   207
  if ((jmpcode=setjmp(errbuf))!=0)
dvander@27
   208
    goto cleanup;
dvander@27
   209
dvander@27
   210
  /* allocate memory for fixed tables */
dvander@27
   211
  inpfname=(char*)malloc(_MAX_PATH);
dvander@27
   212
  if (inpfname==NULL)
ds@841
   213
    error(123);         /* insufficient memory */
dvander@27
   214
  litq=(cell*)malloc(litmax*sizeof(cell));
dvander@27
   215
  if (litq==NULL)
ds@841
   216
    error(123);         /* insufficient memory */
dvander@27
   217
  if (!phopt_init())
ds@841
   218
    error(123);         /* insufficient memory */
dvander@27
   219
dvander@27
   220
  setopt(argc,argv,outfname,errfname,incfname,reportname,codepage);
dvander@27
   221
  strcpy(binfname,outfname);
dvander@27
   222
  ptr=get_extension(binfname);
dvander@27
   223
  if (ptr!=NULL && stricmp(ptr,".asm")==0)
dvander@27
   224
    set_extension(binfname,".smx",TRUE);
dvander@27
   225
  else
dvander@27
   226
    set_extension(binfname,".smx",FALSE);
dvander@27
   227
  /* set output names that depend on the input name */
dvander@27
   228
  if (sc_listing)
dvander@27
   229
    set_extension(outfname,".lst",TRUE);
dvander@27
   230
  else
dvander@27
   231
    set_extension(outfname,".asm",TRUE);
dvander@27
   232
  if (strlen(errfname)!=0)
dvander@27
   233
    remove(errfname);   /* delete file on startup */
dvander@27
   234
  else if (verbosity>0)
dvander@27
   235
    setcaption();
dvander@27
   236
  setconfig(argv[0]);   /* the path to the include and codepage files */
dvander@27
   237
  sc_ctrlchar_org=sc_ctrlchar;
dvander@27
   238
  lcl_packstr=sc_packstr;
dvander@27
   239
  lcl_needsemicolon=sc_needsemicolon;
dvander@27
   240
  lcl_tabsize=sc_tabsize;
dvander@27
   241
  #if !defined NO_CODEPAGE
dvander@27
   242
    if (!cp_set(codepage))      /* set codepage */
ds@841
   243
      error(128);               /* codepage mapping file not found */
dvander@27
   244
  #endif
dvander@27
   245
  /* optionally create a temporary input file that is a collection of all
dvander@27
   246
   * input files
dvander@27
   247
   */
dvander@27
   248
  assert(get_sourcefile(0)!=NULL);  /* there must be at least one source file */
dvander@27
   249
  if (get_sourcefile(1)!=NULL) {
dvander@27
   250
    /* there are at least two or more source files */
dvander@27
   251
    char *tname,*sname;
dvander@27
   252
    FILE *ftmp,*fsrc;
dvander@27
   253
    int fidx;
dvander@27
   254
    #if defined	__WIN32__ || defined _WIN32
dvander@27
   255
      tname=_tempnam(NULL,"pawn");
dvander@27
   256
    #elif defined __MSDOS__ || defined _Windows
dvander@27
   257
      tname=tempnam(NULL,"pawn");
dvander@27
   258
    #elif defined(MACOS) && !defined(__MACH__)
dvander@27
   259
      /* tempnam is not supported for the Macintosh CFM build. */
ds@841
   260
      error(124,get_sourcefile(1));
dvander@27
   261
      tname=NULL;
dvander@27
   262
      sname=NULL;
dvander@27
   263
    #else
dvander@27
   264
      tname=tempnam(NULL,"pawn");
dvander@27
   265
    #endif
dvander@27
   266
    ftmp=(FILE*)pc_createsrc(tname);
dvander@27
   267
    for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) {
dvander@27
   268
      unsigned char tstring[128];
dvander@27
   269
      fsrc=(FILE*)pc_opensrc(sname);
dvander@27
   270
      if (fsrc==NULL) {
dvander@406
   271
        pc_closesrc(ftmp);
dvander@406
   272
        remove(tname);
dvander@27
   273
        strcpy(inpfname,sname); /* avoid invalid filename */
ds@841
   274
        error(120,sname);
dvander@27
   275
      } /* if */
dvander@406
   276
      pc_writesrc(ftmp,(unsigned char*)"#file \"");
dvander@27
   277
      pc_writesrc(ftmp,(unsigned char*)sname);
dvander@406
   278
      pc_writesrc(ftmp,(unsigned char*)"\"\n");
dvander@406
   279
      while (!pc_eofsrc(fsrc) && pc_readsrc(fsrc,tstring,sizeof tstring)) {
dvander@27
   280
        pc_writesrc(ftmp,tstring);
dvander@27
   281
      } /* while */
dvander@27
   282
      pc_closesrc(fsrc);
dvander@27
   283
    } /* for */
dvander@27
   284
    pc_closesrc(ftmp);
dvander@27
   285
    strcpy(inpfname,tname);
dvander@406
   286
	strcpy(g_tmpfile,tname);
dvander@27
   287
    free(tname);
dvander@27
   288
  } else {
dvander@27
   289
    strcpy(inpfname,get_sourcefile(0));
dvander@27
   290
  } /* if */
dvander@27
   291
  inpf_org=(FILE*)pc_opensrc(inpfname);
dvander@27
   292
  if (inpf_org==NULL)
ds@841
   293
    error(120,inpfname);
dvander@27
   294
  freading=TRUE;
dvander@27
   295
  outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */
dvander@27
   296
  if (outf==NULL)
ds@841
   297
    error(121,outfname);
dvander@27
   298
  /* immediately open the binary file, for other programs to check */
dvander@27
   299
  if (sc_asmfile || sc_listing) {
dvander@27
   300
    binf=NULL;
dvander@27
   301
  } else {
dvander@27
   302
    binf=(FILE*)pc_openbin(binfname);
dvander@27
   303
    if (binf==NULL)
ds@841
   304
      error(121,binfname);
dvander@27
   305
  } /* if */
dvander@27
   306
  setconstants();               /* set predefined constants and tagnames */
dvander@27
   307
  for (i=0; i<skipinput; i++)   /* skip lines in the input file */
dvander@27
   308
    if (pc_readsrc(inpf_org,pline,sLINEMAX)!=NULL)
dvander@27
   309
      fline++;                  /* keep line number up to date */
dvander@27
   310
  skipinput=fline;
dvander@27
   311
  sc_status=statFIRST;
dvander@27
   312
  /* write starting options (from the command line or the configuration file) */
dvander@27
   313
  if (sc_listing) {
dvander@27
   314
    char string[150];
dvander@27
   315
    sprintf(string,"#pragma ctrlchar 0x%02x\n"
dvander@27
   316
                   "#pragma pack %s\n"
dvander@27
   317
                   "#pragma semicolon %s\n"
dvander@27
   318
                   "#pragma tabsize %d\n",
dvander@27
   319
            sc_ctrlchar,
dvander@27
   320
            sc_packstr ? "true" : "false",
dvander@27
   321
            sc_needsemicolon ? "true" : "false",
dvander@27
   322
            sc_tabsize);
dvander@27
   323
    pc_writeasm(outf,string);
dvander@27
   324
    setfiledirect(inpfname);
dvander@27
   325
  } /* if */
dvander@27
   326
  /* do the first pass through the file (or possibly two or more "first passes") */
dvander@27
   327
  sc_parsenum=0;
dvander@27
   328
  inpfmark=pc_getpossrc(inpf_org);
dvander@27
   329
  do {
dvander@27
   330
    /* reset "defined" flag of all functions and global variables */
dvander@27
   331
    reduce_referrers(&glbtab);
dvander@27
   332
    delete_symbols(&glbtab,0,TRUE,FALSE);
dvander@27
   333
    #if !defined NO_DEFINE
dvander@27
   334
      delete_substtable();
dvander@27
   335
      inst_datetime_defines();
dvander@1978
   336
      inst_binary_name(binfname);
dvander@27
   337
    #endif
dvander@27
   338
    resetglobals();
dvander@167
   339
    pstructs_free();
dvander@132
   340
	funcenums_free();
dvander@27
   341
    sc_ctrlchar=sc_ctrlchar_org;
dvander@27
   342
    sc_packstr=lcl_packstr;
dvander@27
   343
    sc_needsemicolon=lcl_needsemicolon;
dvander@27
   344
    sc_tabsize=lcl_tabsize;
dvander@27
   345
    errorset(sRESET,0);
dvander@27
   346
    /* reset the source file */
dvander@27
   347
    inpf=inpf_org;
dvander@27
   348
    freading=TRUE;
dvander@27
   349
    pc_resetsrc(inpf,inpfmark); /* reset file position */
dvander@27
   350
    fline=skipinput;            /* reset line number */
dvander@27
   351
    sc_reparse=FALSE;           /* assume no extra passes */
dvander@27
   352
    sc_status=statFIRST;        /* resetglobals() resets it to IDLE */
dvander@27
   353
dvander@27
   354
    if (strlen(incfname)>0) {
dvander@27
   355
      if (strcmp(incfname,sDEF_PREFIX)==0) {
dvander@27
   356
        plungefile(incfname,FALSE,TRUE);    /* parse "default.inc" */
dvander@27
   357
      } else {
dvander@27
   358
        if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */
ds@841
   359
          error(120,incfname);          /* cannot read from ... (fatal error) */
dvander@27
   360
      } /* if */
dvander@27
   361
    } /* if */
dvander@27
   362
    preprocess();                       /* fetch first line */
dvander@27
   363
    parse();                            /* process all input */
dvander@27
   364
    sc_parsenum++;
dvander@27
   365
  } while (sc_reparse);
dvander@27
   366
dvander@27
   367
  /* second (or third) pass */
dvander@27
   368
  sc_status=statWRITE;                  /* set, to enable warnings */
dvander@27
   369
  state_conflict(&glbtab);
dvander@27
   370
dvander@27
   371
  /* write a report, if requested */
dvander@27
   372
  #if !defined SC_LIGHT
dvander@27
   373
    if (sc_makereport) {
dvander@27
   374
      FILE *frep=stdout;
dvander@27
   375
      if (strlen(reportname)>0)
dvander@27
   376
        frep=fopen(reportname,"wb");    /* avoid translation of \n to \r\n in DOS/Windows */
dvander@27
   377
      if (frep!=NULL) {
dvander@27
   378
        make_report(&glbtab,frep,get_sourcefile(0));
dvander@27
   379
        if (strlen(reportname)>0)
dvander@27
   380
          fclose(frep);
dvander@27
   381
      } /* if */
dvander@27
   382
      if (sc_documentation!=NULL) {
dvander@27
   383
        free(sc_documentation);
dvander@27
   384
        sc_documentation=NULL;
dvander@27
   385
      } /* if */
dvander@27
   386
    } /* if */
dvander@27
   387
  #endif
dvander@27
   388
  if (sc_listing)
dvander@27
   389
    goto cleanup;
dvander@27
   390
dvander@27
   391
  /* ??? for re-parsing the listing file instead of the original source
dvander@27
   392
   * file (and doing preprocessing twice):
dvander@27
   393
   * - close input file, close listing file
dvander@27
   394
   * - re-open listing file for reading (inpf)
dvander@27
   395
   * - open assembler file (outf)
dvander@27
   396
   */
dvander@27
   397
dvander@27
   398
  /* reset "defined" flag of all functions and global variables */
dvander@27
   399
  reduce_referrers(&glbtab);
dvander@27
   400
  delete_symbols(&glbtab,0,TRUE,FALSE);
dvander@132
   401
  funcenums_free();
dvander@167
   402
  pstructs_free();
dvander@27
   403
  #if !defined NO_DEFINE
dvander@27
   404
    delete_substtable();
dvander@27
   405
    inst_datetime_defines();
dvander@1980
   406
    inst_binary_name(binfname);
dvander@27
   407
  #endif
dvander@27
   408
  resetglobals();
dvander@27
   409
  sc_ctrlchar=sc_ctrlchar_org;
dvander@27
   410
  sc_packstr=lcl_packstr;
dvander@27
   411
  sc_needsemicolon=lcl_needsemicolon;
dvander@27
   412
  sc_tabsize=lcl_tabsize;
dvander@27
   413
  errorset(sRESET,0);
dvander@27
   414
  /* reset the source file */
dvander@27
   415
  inpf=inpf_org;
dvander@27
   416
  freading=TRUE;
dvander@27
   417
  pc_resetsrc(inpf,inpfmark);   /* reset file position */
dvander@27
   418
  fline=skipinput;              /* reset line number */
dvander@27
   419
  lexinit();                    /* clear internal flags of lex() */
dvander@27
   420
  sc_status=statWRITE;          /* allow to write --this variable was reset by resetglobals() */
dvander@27
   421
  writeleader(&glbtab);
dvander@27
   422
  insert_dbgfile(inpfname);
dvander@27
   423
  if (strlen(incfname)>0) {
dvander@27
   424
    if (strcmp(incfname,sDEF_PREFIX)==0)
dvander@27
   425
      plungefile(incfname,FALSE,TRUE);  /* parse "default.inc" (again) */
dvander@27
   426
    else
dvander@27
   427
      plungequalifiedfile(incfname);    /* parse implicit include file (again) */
dvander@27
   428
  } /* if */
dvander@27
   429
  preprocess();                         /* fetch first line */
dvander@27
   430
  parse();                              /* process all input */
dvander@27
   431
  /* inpf is already closed when readline() attempts to pop of a file */
dvander@27
   432
  writetrailer();                       /* write remaining stuff */
dvander@27
   433
dvander@27
   434
  entry=testsymbols(&glbtab,0,TRUE,FALSE);  /* test for unused or undefined
dvander@27
   435
                                             * functions and variables */
dvander@27
   436
  if (!entry)
dvander@27
   437
    error(13);                  /* no entry point (no public functions) */
dvander@27
   438
dvander@27
   439
cleanup:
dvander@27
   440
  if (inpf!=NULL)               /* main source file is not closed, do it now */
dvander@27
   441
    pc_closesrc(inpf);
dvander@27
   442
  /* write the binary file (the file is already open) */
dvander@27
   443
  if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) {
dvander@27
   444
    assert(binf!=NULL);
dvander@27
   445
    pc_resetasm(outf);          /* flush and loop back, for reading */
dvander@27
   446
    #if !defined SC_LIGHT
dvander@27
   447
      hdrsize=
dvander@27
   448
    #endif
dvander@27
   449
    assemble(binf,outf);        /* assembler file is now input */
dvander@27
   450
  } /* if */
dvander@27
   451
  if (outf!=NULL) {
dvander@27
   452
    pc_closeasm(outf,!(sc_asmfile || sc_listing));
dvander@27
   453
    outf=NULL;
dvander@27
   454
  } /* if */
dvander@27
   455
  if (binf!=NULL) {
dvander@27
   456
    pc_closebin(binf,errnum!=0);
dvander@27
   457
    binf=NULL;
dvander@27
   458
  } /* if */
dvander@27
   459
dvander@27
   460
  #if !defined SC_LIGHT
dvander@27
   461
    if (errnum==0 && strlen(errfname)==0) {
dvander@1164
   462
#if 0	//bug in compiler -- someone's script caused this function to infrecurs
dvander@27
   463
      int recursion;
dvander@27
   464
      long stacksize=max_stacksize(&glbtab,&recursion);
dvander@1164
   465
#endif
dvander@27
   466
      int flag_exceed=0;
dvander@27
   467
      if (pc_amxlimit>0) {
dvander@27
   468
        long totalsize=hdrsize+code_idx;
dvander@27
   469
        if (pc_amxram==0)
dvander@27
   470
          totalsize+=(glb_declared+pc_stksize)*sizeof(cell);
dvander@27
   471
        if (totalsize>=pc_amxlimit)
dvander@27
   472
          flag_exceed=1;
dvander@27
   473
      } /* if */
dvander@27
   474
      if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram)
dvander@27
   475
        flag_exceed=1;
dvander@1164
   476
      if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || flag_exceed) {
dvander@27
   477
        pc_printf("Header size:       %8ld bytes\n", (long)hdrsize);
dvander@27
   478
        pc_printf("Code size:         %8ld bytes\n", (long)code_idx);
dvander@27
   479
        pc_printf("Data size:         %8ld bytes\n", (long)glb_declared*sizeof(cell));
dvander@27
   480
        pc_printf("Stack/heap size:   %8ld bytes; ", (long)pc_stksize*sizeof(cell));
dvander@1164
   481
#if 0
dvander@27
   482
        pc_printf("estimated max. usage");
dvander@27
   483
        if (recursion)
faluco@62
   484
          pc_printf(": unknown, due to recursion\n");
dvander@27
   485
        else if ((pc_memflags & suSLEEP_INSTR)!=0)
faluco@62
   486
          pc_printf(": unknown, due to the \"sleep\" instruction\n");
dvander@27
   487
        else
dvander@27
   488
          pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell));
dvander@1164
   489
#endif
dvander@27
   490
        pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell));
dvander@27
   491
      } /* if */
dvander@27
   492
      if (flag_exceed)
ds@841
   493
        error(126,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */
dvander@27
   494
    } /* if */
dvander@27
   495
  #endif
dvander@27
   496
dvander@406
   497
  if (g_tmpfile[0] != '\0') {
dvander@406
   498
    remove(g_tmpfile);
dvander@406
   499
  }
dvander@27
   500
  if (inpfname!=NULL) {
dvander@27
   501
    free(inpfname);
dvander@27
   502
  } /* if */
dvander@27
   503
  if (litq!=NULL)
dvander@27
   504
    free(litq);
dvander@27
   505
  phopt_cleanup();
dvander@27
   506
  stgbuffer_cleanup();
dvander@27
   507
  clearstk();
dvander@27
   508
  assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols
dvander@27
   509
                                           * should already have been deleted */
dvander@27
   510
  delete_symbols(&loctab,0,TRUE,TRUE);    /* delete local variables if not yet
dvander@27
   511
                                           * done (i.e. on a fatal error) */
dvander@27
   512
  delete_symbols(&glbtab,0,TRUE,TRUE);
dvander@27
   513
  delete_consttable(&tagname_tab);
dvander@27
   514
  delete_consttable(&libname_tab);
dvander@27
   515
  delete_consttable(&sc_automaton_tab);
dvander@27
   516
  delete_consttable(&sc_state_tab);
dvander@27
   517
  state_deletetable();
dvander@27
   518
  delete_aliastable();
dvander@27
   519
  delete_pathtable();
dvander@27
   520
  delete_sourcefiletable();
dvander@27
   521
  delete_dbgstringtable();
dvander@175
   522
  funcenums_free();
dvander@175
   523
  pstructs_free();
dvander@27
   524
  #if !defined NO_DEFINE
dvander@27
   525
    delete_substtable();
dvander@27
   526
  #endif
dvander@27
   527
  #if !defined SC_LIGHT
dvander@27
   528
    delete_docstringtable();
dvander@27
   529
    if (sc_documentation!=NULL)
dvander@27
   530
      free(sc_documentation);
dvander@27
   531
  #endif
dvander@27
   532
  delete_autolisttable();
dvander@27
   533
  if (errnum!=0) {
dvander@27
   534
    if (strlen(errfname)==0)
dvander@27
   535
      pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : "");
dvander@27
   536
    retcode=1;
dvander@27
   537
  } else if (warnnum!=0){
dvander@27
   538
    if (strlen(errfname)==0)
dvander@27
   539
      pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : "");
dvander@27
   540
    retcode=0;          /* use "0", so that MAKE and similar tools continue */
dvander@27
   541
  } else {
dvander@27
   542
    retcode=jmpcode;
dvander@27
   543
    if (retcode==0 && verbosity>=2)
dvander@27
   544
      pc_printf("\nDone.\n");
dvander@27
   545
  } /* if */
dvander@27
   546
  #if defined	__WIN32__ || defined _WIN32 || defined _Windows
dvander@27
   547
    if (IsWindow(hwndFinish))
dvander@27
   548
      PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L);
dvander@27
   549
  #endif
dvander@27
   550
  #if defined FORTIFY
dvander@27
   551
    Fortify_ListAllMemory();
dvander@27
   552
  #endif
dvander@27
   553
  return retcode;
dvander@27
   554
}
dvander@27
   555
dvander@27
   556
#if defined __cplusplus
dvander@27
   557
  extern "C"
dvander@27
   558
#endif
dvander@27
   559
int pc_addconstant(char *name,cell value,int tag)
dvander@27
   560
{
dvander@27
   561
  errorset(sFORCESET,0);        /* make sure error engine is silenced */
dvander@27
   562
  sc_status=statIDLE;
dvander@27
   563
  add_constant(name,value,sGLOBAL,tag);
dvander@27
   564
  return 1;
dvander@27
   565
}
dvander@27
   566
dvander@1978
   567
static void inst_binary_name(char *binfname)
dvander@1978
   568
{
dvander@1978
   569
  size_t i, len;
dvander@1978
   570
  char *binptr;
dvander@1980
   571
  char newpath[512], newname[512];
dvander@1978
   572
dvander@1978
   573
  binptr = NULL;
dvander@1978
   574
  len = strlen(binfname);
dvander@1978
   575
  for (i = len - 1; i >= 0 && i < len; i--)
dvander@1978
   576
  {
dvander@1978
   577
    if (binfname[i] == '/'
dvander@1978
   578
#if defined WIN32 || defined _WIN32
dvander@1978
   579
      || binfname[i] == '\\'
dvander@1978
   580
#endif
dvander@1978
   581
      )
dvander@1978
   582
    {
dvander@1978
   583
      binptr = &binfname[i + 1];
dvander@1978
   584
      break;
dvander@1978
   585
    }
dvander@1978
   586
  }
dvander@1978
   587
dvander@1978
   588
  if (binptr == NULL)
dvander@1978
   589
  {
dvander@1978
   590
	  binptr = binfname;
dvander@1978
   591
  }
dvander@1978
   592
dvander@1980
   593
  snprintf(newpath, sizeof(newpath), "\"%s\"", binfname);
dvander@1980
   594
  snprintf(newname, sizeof(newname), "\"%s\"", binptr);
dvander@1980
   595
dvander@1980
   596
  insert_subst("__BINARY_PATH__", newpath, 15);
dvander@1980
   597
  insert_subst("__BINARY_NAME__", newname, 15);
dvander@1978
   598
}
dvander@1978
   599
dvander@27
   600
static void inst_datetime_defines(void)
dvander@27
   601
{
dvander@27
   602
  char date[64];
dvander@27
   603
  char ltime[64];
dvander@27
   604
  time_t td;
dvander@27
   605
  struct tm *curtime;
dvander@27
   606
dvander@27
   607
  time(&td);
dvander@27
   608
  curtime = localtime(&td);
dvander@761
   609
  strftime(date, 31, "\"%m/%d/%Y\"", curtime);
dvander@27
   610
  strftime(ltime, 31, "\"%H:%M:%S\"", curtime);
dvander@27
   611
dvander@27
   612
  insert_subst("__DATE__", date, 8);
dvander@27
   613
  insert_subst("__TIME__", ltime, 8);
dvander@27
   614
}
dvander@27
   615
dvander@27
   616
#if defined __cplusplus
dvander@27
   617
  extern "C"
dvander@27
   618
#endif
dvander@27
   619
int pc_addtag(char *name)
dvander@27
   620
{
dvander@27
   621
  cell val;
dvander@27
   622
  constvalue *ptr;
dvander@27
   623
  int last,tag;
dvander@27
   624
dvander@27
   625
  if (name==NULL) {
dvander@27
   626
    /* no tagname was given, check for one */
dvander@27
   627
    if (lex(&val,&name)!=tLABEL) {
dvander@27
   628
      lexpush();
dvander@27
   629
      return 0;         /* untagged */
dvander@27
   630
    } /* if */
dvander@27
   631
  } /* if */
dvander@27
   632
dvander@27
   633
  assert(strchr(name,':')==NULL); /* colon should already have been stripped */
dvander@27
   634
  last=0;
dvander@27
   635
  ptr=tagname_tab.next;
dvander@27
   636
  while (ptr!=NULL) {
dvander@27
   637
    tag=(int)(ptr->value & TAGMASK);
dvander@27
   638
    if (strcmp(name,ptr->name)==0)
dvander@27
   639
      return tag;       /* tagname is known, return its sequence number */
dvander@27
   640
    tag &= (int)~FIXEDTAG;
dvander@132
   641
    tag &= (int)~FUNCTAG;
dvander@27
   642
    if (tag>last)
dvander@27
   643
      last=tag;
dvander@27
   644
    ptr=ptr->next;
dvander@27
   645
  } /* while */
dvander@27
   646
dvander@27
   647
  /* tagname currently unknown, add it */
dvander@27
   648
  tag=last+1;           /* guaranteed not to exist already */
dvander@27
   649
  if (isupper(*name))
dvander@27
   650
    tag |= (int)FIXEDTAG;
dvander@27
   651
  append_constval(&tagname_tab,name,(cell)tag,0);
dvander@27
   652
  return tag;
dvander@27
   653
}
dvander@27
   654
dvander@132
   655
int pc_addfunctag(char *name)
dvander@132
   656
{
dvander@132
   657
  cell val;
dvander@132
   658
  constvalue *ptr;
dvander@132
   659
  int last,tag;
dvander@132
   660
dvander@132
   661
  if (name==NULL) {
dvander@132
   662
    /* no tagname was given, check for one */
dvander@132
   663
    if (lex(&val,&name)!=tLABEL) {
dvander@132
   664
      lexpush();
dvander@132
   665
      return 0;         /* untagged */
dvander@132
   666
    } /* if */
dvander@132
   667
  } /* if */
dvander@132
   668
dvander@132
   669
  assert(strchr(name,':')==NULL); /* colon should already have been stripped */
dvander@132
   670
  last=0;
dvander@132
   671
  ptr=tagname_tab.next;
dvander@132
   672
  while (ptr!=NULL) {
dvander@132
   673
    tag=(int)(ptr->value & TAGMASK);
dvander@132
   674
    if (strcmp(name,ptr->name)==0)
dvander@132
   675
      return tag;       /* tagname is known, return its sequence number */
dvander@132
   676
    tag &= (int)~FIXEDTAG;
dvander@132
   677
    tag &= (int)~FUNCTAG;
dvander@132
   678
    if (tag>last)
dvander@132
   679
      last=tag;
dvander@132
   680
    ptr=ptr->next;
dvander@132
   681
  } /* while */
dvander@132
   682
dvander@132
   683
  /* tagname currently unknown, add it */
dvander@132
   684
  tag=last+1;           /* guaranteed not to exist already */
dvander@132
   685
  if (isupper(*name))
dvander@132
   686
    tag |= (int)FIXEDTAG;
dvander@132
   687
  tag |= (int)FUNCTAG;
dvander@132
   688
  append_constval(&tagname_tab,name,(cell)tag,0);
dvander@132
   689
  return tag;
dvander@132
   690
}
dvander@132
   691
dvander@27
   692
static void resetglobals(void)
dvander@27
   693
{
dvander@27
   694
  /* reset the subset of global variables that is modified by the first pass */
dvander@27
   695
  curfunc=NULL;         /* pointer to current function */
dvander@27
   696
  lastst=0;             /* last executed statement type */
dvander@27
   697
  nestlevel=0;          /* number of active (open) compound statements */
dvander@27
   698
  rettype=0;            /* the type that a "return" expression should have */
dvander@27
   699
  litidx=0;             /* index to literal table */
dvander@27
   700
  stgidx=0;             /* index to the staging buffer */
dvander@27
   701
  sc_labnum=0;          /* top value of (internal) labels */
dvander@27
   702
  staging=0;            /* true if staging output */
dvander@27
   703
  declared=0;           /* number of local cells declared */
dvander@27
   704
  glb_declared=0;       /* number of global cells declared */
dvander@27
   705
  code_idx=0;           /* number of bytes with generated code */
dvander@27
   706
  ntv_funcid=0;         /* incremental number of native function */
dvander@27
   707
  curseg=0;             /* 1 if currently parsing CODE, 2 if parsing DATA */
dvander@27
   708
  freading=FALSE;       /* no input file ready yet */
dvander@27
   709
  fline=0;              /* the line number in the current file */
dvander@27
   710
  fnumber=0;            /* the file number in the file table (debugging) */
dvander@27
   711
  fcurrent=0;           /* current file being processed (debugging) */
dvander@27
   712
  sc_intest=FALSE;      /* true if inside a test */
dvander@27
   713
  sideeffect=0;         /* true if an expression causes a side-effect */
dvander@27
   714
  stmtindent=0;         /* current indent of the statement */
dvander@27
   715
  indent_nowarn=FALSE;  /* do not skip warning "217 loose indentation" */
dvander@27
   716
  sc_allowtags=TRUE;    /* allow/detect tagnames */
dvander@27
   717
  sc_status=statIDLE;
dvander@27
   718
  sc_allowproccall=FALSE;
dvander@27
   719
  pc_addlibtable=TRUE;  /* by default, add a "library table" to the output file */
dvander@27
   720
  sc_alignnext=FALSE;
dvander@27
   721
  pc_docexpr=FALSE;
ds@52
   722
  pc_deprecate=NULL;
dvander@27
   723
  sc_curstates=0;
dvander@27
   724
  pc_memflags=0;
dvander@27
   725
}
dvander@27
   726
dvander@27
   727
static void initglobals(void)
dvander@27
   728
{
dvander@27
   729
  resetglobals();
dvander@27
   730
dvander@27
   731
  sc_asmfile=FALSE;     /* do not create .ASM file */
dvander@27
   732
  sc_listing=FALSE;     /* do not create .LST file */
dvander@27
   733
  skipinput=0;          /* number of lines to skip from the first input file */
dvander@27
   734
  sc_ctrlchar=CTRL_CHAR;/* the escape character */
dvander@27
   735
  litmax=sDEF_LITMAX;   /* current size of the literal table */
dvander@27
   736
  errnum=0;             /* number of errors */
dvander@27
   737
  warnnum=0;            /* number of warnings */
dvander@27
   738
  optproccall=FALSE;    /* sourcemod: do not support "procedure call" */
dvander@27
   739
  verbosity=1;          /* verbosity level, no copyright banner */
dvander@27
   740
  sc_debug=sCHKBOUNDS|sSYMBOLIC;   /* sourcemod: full debug stuff */
dvander@27
   741
  pc_optimize=sOPTIMIZE_DEFAULT;   /* sourcemod: full optimization */
dvander@151
   742
  sc_packstr=TRUE;     /* strings are packed by default */
dvander@27
   743
  sc_compress=FALSE;	/* always disable compact encoding! */
dvander@27
   744
  sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */
dvander@27
   745
  sc_dataalign=sizeof(cell);
dvander@27
   746
  pc_stksize=sDEF_AMXSTACK;/* default stack size */
dvander@27
   747
  pc_amxlimit=0;        /* no limit on size of the abstract machine */
dvander@27
   748
  pc_amxram=0;          /* no limit on data size of the abstract machine */
dvander@27
   749
  sc_tabsize=8;         /* assume a TAB is 8 spaces */
dvander@27
   750
  sc_rationaltag=0;     /* assume no support for rational numbers */
dvander@27
   751
  rational_digits=0;    /* number of fractional digits */
dvander@27
   752
dvander@27
   753
  outfname[0]='\0';     /* output file name */
dvander@27
   754
  errfname[0]='\0';     /* error file name */
dvander@27
   755
  inpf=NULL;            /* file read from */
dvander@27
   756
  inpfname=NULL;        /* pointer to name of the file currently read from */
dvander@27
   757
  outf=NULL;            /* file written to */
dvander@27
   758
  litq=NULL;            /* the literal queue */
dvander@27
   759
  glbtab.next=NULL;     /* clear global variables/constants table */
dvander@27
   760
  loctab.next=NULL;     /*   "   local      "    /    "       "   */
dvander@27
   761
  tagname_tab.next=NULL;/* tagname table */
dvander@27
   762
  libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */
dvander@27
   763
dvander@27
   764
  pline[0]='\0';        /* the line read from the input file */
dvander@27
   765
  lptr=NULL;            /* points to the current position in "pline" */
dvander@27
   766
  curlibrary=NULL;      /* current library */
dvander@27
   767
  inpf_org=NULL;        /* main source file */
dvander@27
   768
dvander@27
   769
  wqptr=wq;             /* initialize while queue pointer */
dvander@27
   770
dvander@27
   771
#if !defined SC_LIGHT
dvander@27
   772
  sc_documentation=NULL;
dvander@27
   773
  sc_makereport=FALSE;  /* do not generate a cross-reference report */
dvander@27
   774
#endif
dvander@27
   775
}
dvander@27
   776
dvander@27
   777
static char *get_extension(char *filename)
dvander@27
   778
{
dvander@27
   779
  char *ptr;
dvander@27
   780
dvander@27
   781
  assert(filename!=NULL);
dvander@27
   782
  ptr=strrchr(filename,'.');
dvander@27
   783
  if (ptr!=NULL) {
dvander@27
   784
    /* ignore extension on a directory or at the start of the filename */
dvander@27
   785
    if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR)
dvander@27
   786
      ptr=NULL;
dvander@27
   787
  } /* if */
dvander@27
   788
  return ptr;
dvander@27
   789
}
dvander@27
   790
dvander@27
   791
/* set_extension
dvander@27
   792
 * Set the default extension, or force an extension. To erase the
dvander@27
   793
 * extension of a filename, set "extension" to an empty string.
dvander@27
   794
 */
dvander@27
   795
SC_FUNC void set_extension(char *filename,char *extension,int force)
dvander@27
   796
{
dvander@27
   797
  char *ptr;
dvander@27
   798
dvander@27
   799
  assert(extension!=NULL && (*extension=='\0' || *extension=='.'));
dvander@27
   800
  assert(filename!=NULL);
dvander@27
   801
  ptr=get_extension(filename);
dvander@27
   802
  if (force && ptr!=NULL)
dvander@27
   803
    *ptr='\0';          /* set zero terminator at the position of the period */
dvander@27
   804
  if (force || ptr==NULL)
dvander@27
   805
    strcat(filename,extension);
dvander@27
   806
}
dvander@27
   807
dvander@27
   808
static const char *option_value(const char *optptr)
dvander@27
   809
{
dvander@27
   810
  return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1;
dvander@27
   811
}
dvander@27
   812
dvander@27
   813
static int toggle_option(const char *optptr, int option)
dvander@27
   814
{
dvander@27
   815
  switch (*option_value(optptr)) {
dvander@27
   816
  case '\0':
dvander@27
   817
    option=!option;
dvander@27
   818
    break;
dvander@27
   819
  case '-':
dvander@27
   820
    option=FALSE;
dvander@27
   821
    break;
dvander@27
   822
  case '+':
dvander@27
   823
    option=TRUE;
dvander@27
   824
    break;
dvander@27
   825
  default:
dvander@27
   826
    about();
dvander@27
   827
  } /* switch */
dvander@27
   828
  return option;
dvander@27
   829
}
dvander@27
   830
dvander@27
   831
/* Parsing command line options is indirectly recursive: parseoptions()
dvander@27
   832
 * calls parserespf() to handle options in a a response file and
dvander@27
   833
 * parserespf() calls parseoptions() at its turn after having created
dvander@27
   834
 * an "option list" from the contents of the file.
dvander@27
   835
 */
dvander@27
   836
static void parserespf(char *filename,char *oname,char *ename,char *pname,
dvander@27
   837
                       char *rname, char *codepage);
dvander@27
   838
dvander@27
   839
static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname,
dvander@27
   840
                         char *rname, char *codepage)
dvander@27
   841
{
dvander@27
   842
  char str[_MAX_PATH],*name;
dvander@27
   843
  const char *ptr;
dvander@27
   844
  int arg,i,isoption;
dvander@27
   845
dvander@27
   846
  for (arg=1; arg<argc; arg++) {
dvander@27
   847
    #if DIRSEP_CHAR=='/'
dvander@27
   848
      isoption= argv[arg][0]=='-';
dvander@27
   849
    #else
dvander@27
   850
      isoption= argv[arg][0]=='/' || argv[arg][0]=='-';
dvander@27
   851
    #endif
dvander@27
   852
    if (isoption) {
dvander@27
   853
      ptr=&argv[arg][1];
dvander@27
   854
      switch (*ptr) {
dvander@27
   855
      case 'A':
dvander@27
   856
        i=atoi(option_value(ptr));
dvander@27
   857
        if ((i % sizeof(cell))==0)
dvander@27
   858
          sc_dataalign=i;
dvander@27
   859
        else
dvander@27
   860
          about();
dvander@27
   861
        break;
dvander@27
   862
      case 'a':
dvander@27
   863
        if (*(ptr+1)!='\0')
dvander@27
   864
          about();
dvander@27
   865
        sc_asmfile=TRUE;        /* skip last pass of making binary file */
dvander@27
   866
        if (verbosity>1)
dvander@27
   867
          verbosity=1;
dvander@27
   868
        break;
dvander@27
   869
#if 0	/* not allowed in SourceMod */
dvander@27
   870
      case 'C':
dvander@27
   871
        #if AMX_COMPACTMARGIN > 2
dvander@27
   872
          sc_compress=toggle_option(ptr,sc_compress);
dvander@27
   873
        #else
dvander@27
   874
          about();
dvander@27
   875
        #endif
dvander@27
   876
        break;
dvander@27
   877
#endif
dvander@27
   878
      case 'c':
dvander@27
   879
        strlcpy(codepage,option_value(ptr),MAXCODEPAGE);  /* set name of codepage */
dvander@27
   880
        break;
dvander@27
   881
#if defined dos_setdrive
dvander@27
   882
      case 'D':                 /* set active directory */
dvander@27
   883
        ptr=option_value(ptr);
dvander@27
   884
        if (ptr[1]==':')
dvander@27
   885
          dos_setdrive(toupper(*ptr)-'A'+1);    /* set active drive */
dvander@27
   886
        chdir(ptr);
dvander@27
   887
        break;
dvander@27
   888
#endif
dvander@27
   889
#if 0 /* not allowed to change for SourceMod */
dvander@27
   890
      case 'd':
dvander@27
   891
        switch (*option_value(ptr)) {
dvander@27
   892
        case '0':
dvander@27
   893
          sc_debug=0;
dvander@27
   894
          break;
dvander@27
   895
        case '1':
dvander@27
   896
          sc_debug=sCHKBOUNDS;  /* assertions and bounds checking */
dvander@27
   897
          break;
dvander@27
   898
        case '2':
dvander@27
   899
          sc_debug=sCHKBOUNDS | sSYMBOLIC;  /* also symbolic info */
dvander@27
   900
          break;
dvander@27
   901
        case '3':
dvander@27
   902
          sc_debug=sCHKBOUNDS | sSYMBOLIC;
dvander@27
   903
          pc_optimize=sOPTIMIZE_NONE;
dvander@27
   904
          /* also avoid peephole optimization */
dvander@27
   905
          break;
dvander@27
   906
        default:
dvander@27
   907
          about();
dvander@27
   908
        } /* switch */
dvander@27
   909
        break;
dvander@27
   910
#endif
dvander@27
   911
      case 'e':
dvander@27
   912
        strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */
dvander@27
   913
        break;
dvander@27
   914
#if defined	__WIN32__ || defined _WIN32 || defined _Windows
dvander@27
   915
      case 'H':
dvander@27
   916
        hwndFinish=(HWND)atoi(option_value(ptr));
dvander@27
   917
        if (!IsWindow(hwndFinish))
dvander@27
   918
          hwndFinish=(HWND)0;
dvander@27
   919
        break;
dvander@27
   920
#endif
dvander@27
   921
      case 'i':
dvander@27
   922
        strlcpy(str,option_value(ptr),sizeof str);  /* set name of include directory */
dvander@27
   923
        i=strlen(str);
dvander@27
   924
        if (i>0) {
dvander@27
   925
          if (str[i-1]!=DIRSEP_CHAR) {
dvander@27
   926
            str[i]=DIRSEP_CHAR;
dvander@27
   927
            str[i+1]='\0';
dvander@27
   928
          } /* if */
dvander@27
   929
          insert_path(str);
dvander@27
   930
        } /* if */
dvander@27
   931
        break;
dvander@27
   932
      case 'l':
dvander@27
   933
        if (*(ptr+1)!='\0')
dvander@27
   934
          about();
dvander@27
   935
        sc_listing=TRUE;        /* skip second pass & code generation */
dvander@27
   936
        break;
dvander@27
   937
      case 'o':
dvander@27
   938
        strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */
dvander@27
   939
        break;
dvander@27
   940
      case 'O':
dvander@27
   941
        pc_optimize=*option_value(ptr) - '0';
dvander@27
   942
        if (pc_optimize<sOPTIMIZE_NONE || pc_optimize>=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO)
dvander@27
   943
          about();
dvander@27
   944
        break;
dvander@27
   945
      case 'p':
dvander@27
   946
        strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */
dvander@27
   947
        break;
dvander@27
   948
#if !defined SC_LIGHT
dvander@27
   949
      case 'r':
dvander@27
   950
        strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */
dvander@27
   951
        sc_makereport=TRUE;
dvander@27
   952
        if (strlen(rname)>0) {
dvander@27
   953
          set_extension(rname,".xml",FALSE);
dvander@27
   954
        } else if ((name=get_sourcefile(0))!=NULL) {
dvander@27
   955
          assert(strlen(rname)==0);
dvander@27
   956
          assert(strlen(name)<_MAX_PATH);
dvander@27
   957
          if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
dvander@27
   958
            ptr++;          /* strip path */
dvander@27
   959
          else
dvander@27
   960
            ptr=name;
dvander@27
   961
          assert(strlen(ptr)<_MAX_PATH);
dvander@27
   962
          strcpy(rname,ptr);
dvander@27
   963
          set_extension(rname,".xml",TRUE);
dvander@27
   964
        } /* if */
dvander@27
   965
        break;
dvander@27
   966
#endif
dvander@27
   967
      case 'S':
dvander@27
   968
        i=atoi(option_value(ptr));
dvander@27
   969
        if (i>32)
dvander@27
   970
          pc_stksize=(cell)i;   /* stack size has minimum size */
dvander@27
   971
        else
dvander@27
   972
          about();
dvander@27
   973
        break;
dvander@27
   974
      case 's':
dvander@27
   975
        skipinput=atoi(option_value(ptr));
dvander@27
   976
        break;
dvander@27
   977
      case 't':
dvander@27
   978
        sc_tabsize=atoi(option_value(ptr));
dvander@27
   979
        break;
dvander@27
   980
      case 'v':
dvander@27
   981
        verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2;
dvander@27
   982
        if (sc_asmfile && verbosity>1)
dvander@27
   983
          verbosity=1;
dvander@27
   984
        break;
dvander@27
   985
      case 'w':
dvander@27
   986
        i=(int)strtol(option_value(ptr),(char **)&ptr,10);
dvander@27
   987
        if (*ptr=='-')
dvander@27
   988
          pc_enablewarning(i,0);
dvander@27
   989
        else if (*ptr=='+')
dvander@27
   990
          pc_enablewarning(i,1);
dvander@27
   991
        else if (*ptr=='\0')
dvander@27
   992
          pc_enablewarning(i,2);
dvander@27
   993
        break;
dvander@27
   994
      case 'X':
dvander@27
   995
        if (*(ptr+1)=='D') {
dvander@27
   996
          i=atoi(option_value(ptr+1));
dvander@27
   997
          if (i>64)
dvander@27
   998
            pc_amxram=(cell)i;  /* abstract machine data/stack has minimum size */
dvander@27
   999
          else
dvander@27
  1000
            about();
dvander@27
  1001
        } else {
dvander@27
  1002
          i=atoi(option_value(ptr));
dvander@27
  1003
          if (i>64)
dvander@27
  1004
            pc_amxlimit=(cell)i;/* abstract machine has minimum size */
dvander@27
  1005
          else
dvander@27
  1006
            about();
dvander@27
  1007
        } /* if */
dvander@27
  1008
        break;
dvander@27
  1009
      case '\\':                /* use \ instead for escape characters */
dvander@27
  1010
        sc_ctrlchar='\\';
dvander@27
  1011
        break;
dvander@27
  1012
      case '^':                 /* use ^ instead for escape characters */
dvander@27
  1013
        sc_ctrlchar='^';
dvander@27
  1014
        break;
dvander@27
  1015
      case ';':
dvander@27
  1016
        sc_needsemicolon=toggle_option(ptr,sc_needsemicolon);
dvander@27
  1017
        break;
dvander@27
  1018
#if 0 /* not allowed to change in SourceMod */
dvander@27
  1019
      case '(':
dvander@27
  1020
        optproccall=!toggle_option(ptr,!optproccall);
dvander@27
  1021
        break;
dvander@27
  1022
#endif
dvander@27
  1023
      default:                  /* wrong option */
dvander@27
  1024
        about();
dvander@27
  1025
      } /* switch */
dvander@27
  1026
    } else if (argv[arg][0]=='@') {
dvander@27
  1027
      #if !defined SC_LIGHT
dvander@27
  1028
        parserespf(&argv[arg][1],oname,ename,pname,rname,codepage);
dvander@27
  1029
      #endif
dvander@27
  1030
    } else if ((ptr=strchr(argv[arg],'='))!=NULL) {
dvander@27
  1031
      i=(int)(ptr-argv[arg]);
dvander@27
  1032
      if (i>sNAMEMAX) {
dvander@27
  1033
        i=sNAMEMAX;
dvander@27
  1034
        error(200,argv[arg],sNAMEMAX);  /* symbol too long, truncated to sNAMEMAX chars */
dvander@27
  1035
      } /* if */
dvander@27
  1036
      strlcpy(str,argv[arg],i+1);       /* str holds symbol name */
dvander@27
  1037
      i=atoi(ptr+1);
dvander@27
  1038
      add_constant(str,i,sGLOBAL,0);
dvander@27
  1039
    } else {
dvander@395
  1040
      strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".sp" */
dvander@395
  1041
      set_extension(str,".sp",FALSE);
dvander@27
  1042
      insert_sourcefile(str);
dvander@27
  1043
      /* The output name is the first input name with a different extension,
dvander@27
  1044
       * but it is stored in a different directory
dvander@27
  1045
       */
dvander@27
  1046
      if (strlen(oname)==0) {
dvander@27
  1047
        if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
dvander@27
  1048
          ptr++;          /* strip path */
dvander@27
  1049
        else
dvander@27
  1050
          ptr=str;
dvander@27
  1051
        assert(strlen(ptr)<_MAX_PATH);
dvander@27
  1052
        strcpy(oname,ptr);
dvander@27
  1053
      } /* if */
dvander@27
  1054
      set_extension(oname,".asm",TRUE);
dvander@27
  1055
#if !defined SC_LIGHT
dvander@27
  1056
      if (sc_makereport && strlen(rname)==0) {
dvander@27
  1057
        if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
dvander@27
  1058
          ptr++;          /* strip path */
dvander@27
  1059
        else
dvander@27
  1060
          ptr=str;
dvander@27
  1061
        assert(strlen(ptr)<_MAX_PATH);
dvander@27
  1062
        strcpy(rname,ptr);
dvander@27
  1063
        set_extension(rname,".xml",TRUE);
dvander@27
  1064
      } /* if */
dvander@27
  1065
#endif
dvander@27
  1066
    } /* if */
dvander@27
  1067
  } /* for */
dvander@27
  1068
}
dvander@27
  1069
dvander@27
  1070
#if !defined SC_LIGHT
dvander@27
  1071
static void parserespf(char *filename,char *oname,char *ename,char *pname,
dvander@27
  1072
                       char *rname,char *codepage)
dvander@27
  1073
{
dvander@27
  1074
#define MAX_OPTIONS     100
dvander@27
  1075
  FILE *fp;
dvander@27
  1076
  char *string, *ptr, **argv;
dvander@27
  1077
  int argc;
dvander@27
  1078
  long size;
dvander@27
  1079
dvander@27
  1080
  if ((fp=fopen(filename,"r"))==NULL)
ds@841
  1081
    error(120,filename);        /* error reading input file */
dvander@27
  1082
  /* load the complete file into memory */
dvander@27
  1083
  fseek(fp,0L,SEEK_END);
dvander@27
  1084
  size=ftell(fp);
dvander@27
  1085
  fseek(fp,0L,SEEK_SET);
dvander@27
  1086
  assert(size<INT_MAX);
dvander@27
  1087
  if ((string=(char *)malloc((int)size+1))==NULL)
ds@841
  1088
    error(123);                 /* insufficient memory */
dvander@27
  1089
  /* fill with zeros; in MS-DOS, fread() may collapse CR/LF pairs to
dvander@27
  1090
   * a single '\n', so the string size may be smaller than the file
dvander@27
  1091
   * size. */
dvander@27
  1092
  memset(string,0,(int)size+1);
dvander@27
  1093
  fread(string,1,(int)size,fp);
dvander@27
  1094
  fclose(fp);
dvander@27
  1095
  /* allocate table for option pointers */
dvander@27
  1096
  if ((argv=(char **)malloc(MAX_OPTIONS*sizeof(char*)))==NULL)
ds@841
  1097
    error(123);                 /* insufficient memory */
dvander@27
  1098
  /* fill the options table */
dvander@27
  1099
  ptr=strtok(string," \t\r\n");
dvander@27
  1100
  for (argc=1; argc<MAX_OPTIONS && ptr!=NULL; argc++) {
dvander@27
  1101
    /* note: the routine skips argv[0], for compatibility with main() */
dvander@27
  1102
    argv[argc]=ptr;
dvander@27
  1103
    ptr=strtok(NULL," \t\r\n");
dvander@27
  1104
  } /* for */
dvander@27
  1105
  if (ptr!=NULL)
ds@841
  1106
    error(122,"option table");   /* table overflow */
dvander@27
  1107
  /* parse the option table */
dvander@27
  1108
  parseoptions(argc,argv,oname,ename,pname,rname,codepage);
dvander@27
  1109
  /* free allocated memory */
dvander@27
  1110
  free(argv);
dvander@27
  1111
  free(string);
dvander@27
  1112
}
dvander@27
  1113
#endif
dvander@27
  1114
dvander@27
  1115
static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
dvander@27
  1116
                   char *rname,char *codepage)
dvander@27
  1117
{
dvander@27
  1118
  delete_sourcefiletable(); /* make sure it is empty */
dvander@27
  1119
  *oname='\0';
dvander@27
  1120
  *ename='\0';
dvander@27
  1121
  *pname='\0';
dvander@27
  1122
  *rname='\0';
dvander@27
  1123
  *codepage='\0';
dvander@27
  1124
  strcpy(pname,sDEF_PREFIX);
dvander@27
  1125
dvander@27
  1126
  #if 0 /* needed to test with BoundsChecker for DOS (it does not pass
dvander@27
  1127
         * through arguments) */
dvander@27
  1128
    insert_sourcefile("test.p");
dvander@27
  1129
    strcpy(oname,"test.asm");
dvander@27
  1130
  #endif
dvander@27
  1131
dvander@27
  1132
  #if !defined SC_LIGHT
dvander@27
  1133
    /* first parse a "config" file with default options */
dvander@27
  1134
    if (argv[0]!=NULL) {
dvander@27
  1135
      char cfgfile[_MAX_PATH];
dvander@27
  1136
      char *ext;
dvander@27
  1137
      strcpy(cfgfile,argv[0]);
dvander@27
  1138
      if ((ext=strrchr(cfgfile,DIRSEP_CHAR))!=NULL) {
dvander@27
  1139
        *(ext+1)='\0';          /* strip the program filename */
dvander@27
  1140
        strcat(cfgfile,"pawn.cfg");
dvander@27
  1141
      } else {
dvander@27
  1142
        strcpy(cfgfile,"pawn.cfg");
dvander@27
  1143
      } /* if */
dvander@27
  1144
      if (access(cfgfile,4)==0)
dvander@27
  1145
        parserespf(cfgfile,oname,ename,pname,rname,codepage);
dvander@27
  1146
    } /* if */
dvander@27
  1147
  #endif
dvander@27
  1148
  parseoptions(argc,argv,oname,ename,pname,rname,codepage);
dvander@27
  1149
  if (get_sourcefile(0)==NULL)
dvander@27
  1150
    about();
dvander@27
  1151
}
dvander@27
  1152
dvander@27
  1153
#if defined __BORLANDC__ || defined __WATCOMC__
dvander@27
  1154
  #pragma argsused
dvander@27
  1155
#endif
dvander@27
  1156
static void setconfig(char *root)
dvander@27
  1157
{
dvander@27
  1158
  #if defined macintosh
dvander@27
  1159
    insert_path(":include:");
dvander@27
  1160
  #else
dvander@27
  1161
    char path[_MAX_PATH];
dvander@27
  1162
    char *ptr,*base;
dvander@27
  1163
    int len;
dvander@27
  1164
dvander@27
  1165
    /* add the default "include" directory */
dvander@27
  1166
    #if defined __WIN32__ || defined _WIN32
dvander@27
  1167
      GetModuleFileNameA(NULL,path,_MAX_PATH);
ds@1312
  1168
    #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
ds@1312
  1169
      /* see www.autopackage.org for the BinReloc module */
ds@1312
  1170
      br_init_lib(NULL);
ds@1312
  1171
      ptr=br_find_exe("spcomp");
ds@1312
  1172
      strlcpy(path,ptr,sizeof path);
ds@1312
  1173
      free(ptr);
dvander@27
  1174
    #else
dvander@27
  1175
      if (root!=NULL)
dvander@27
  1176
        strlcpy(path,root,sizeof path); /* path + filename (hopefully) */
dvander@27
  1177
    #endif
dvander@27
  1178
    #if defined __MSDOS__
dvander@27
  1179
      /* strip the options (appended to the path + filename) */
dvander@27
  1180
      if ((ptr=strpbrk(path," \t/"))!=NULL)
dvander@27
  1181
        *ptr='\0';
dvander@27
  1182
    #endif
dvander@27
  1183
    /* terminate just behind last \ or : */
dvander@27
  1184
    if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL || (ptr=strchr(path,':'))!=NULL) {
dvander@27
  1185
      /* If there is no "\" or ":", the string probably does not contain the
dvander@27
  1186
       * path; so we just don't add it to the list in that case
dvander@27
  1187
       */
dvander@27
  1188
      *(ptr+1)='\0';
dvander@27
  1189
      base=ptr;
dvander@27
  1190
      strcat(path,"include");
dvander@27
  1191
      len=strlen(path);
dvander@27
  1192
      path[len]=DIRSEP_CHAR;
dvander@27
  1193
      path[len+1]='\0';
dvander@27
  1194
      /* see if it exists */
dvander@27
  1195
      if (access(path,0)!=0 && *base==DIRSEP_CHAR) {
dvander@27
  1196
        /* There is no "include" directory below the directory where the compiler
dvander@27
  1197
         * is found. This typically means that the compiler is in a "bin" sub-directory
dvander@27
  1198
         * and the "include" is below the *parent*. So find the parent...
dvander@27
  1199
         */
dvander@27
  1200
        *base='\0';
dvander@27
  1201
        if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL) {
dvander@27
  1202
          *(ptr+1)='\0';
dvander@27
  1203
          strcat(path,"include");
dvander@27
  1204
          len=strlen(path);
dvander@27
  1205
          path[len]=DIRSEP_CHAR;
dvander@27
  1206
          path[len+1]='\0';
dvander@27
  1207
        } else {
dvander@27
  1208
          *base=DIRSEP_CHAR;
dvander@27
  1209
        } /* if */
dvander@27
  1210
      } /* if */
dvander@27
  1211
      insert_path(path);
dvander@27
  1212
      /* same for the codepage root */
dvander@27
  1213
      #if !defined NO_CODEPAGE
dvander@27
  1214
        *ptr='\0';
dvander@27
  1215
        if (!cp_path(path,"codepage"))
ds@841
  1216
          error(129,path);        /* codepage path */
dvander@27
  1217
      #endif
dvander@27
  1218
      /* also copy the root path (for the XML documentation) */
dvander@27
  1219
      #if !defined SC_LIGHT
dvander@27
  1220
        *ptr='\0';
dvander@27
  1221
        strcpy(sc_rootpath,path);
dvander@27
  1222
      #endif
dvander@27
  1223
    } /* if */
dvander@27
  1224
  #endif /* macintosh */
dvander@27
  1225
}
dvander@27
  1226
dvander@27
  1227
static void setcaption(void)
dvander@27
  1228
{
dvander@799
  1229
  pc_printf("SourcePawn Compiler " SVN_FULL_VERSION "\n");
ds@1947
  1230
  pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2008 AlliedModders, LLC\n\n");
dvander@27
  1231
}
dvander@27
  1232
dvander@27
  1233
static void about(void)
dvander@27
  1234
{
dvander@27
  1235
  if (strlen(errfname)==0) {
dvander@27
  1236
    setcaption();
dvander@1654
  1237
    pc_printf("Usage:   spcomp <filename> [filename...] [options]\n\n");
dvander@27
  1238
    pc_printf("Options:\n");
dvander@27
  1239
    pc_printf("         -A<num>  alignment in bytes of the data segment and the stack\n");
dvander@27
  1240
    pc_printf("         -a       output assembler code\n");
dvander@27
  1241
#if 0	/* not toggleable in SourceMod */
dvander@27
  1242
    pc_printf("         -C[+/-]  compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-');
dvander@27
  1243
#endif
dvander@27
  1244
    pc_printf("         -c<name> codepage name or number; e.g. 1252 for Windows Latin-1\n");
dvander@27
  1245
#if defined dos_setdrive
dvander@27
  1246
    pc_printf("         -Dpath   active directory path\n");
dvander@27
  1247
#endif
dvander@27
  1248
#if 0 /* not used for SourceMod */
dvander@27
  1249
    pc_printf("         -d<num>  debugging level (default=-d%d)\n",sc_debug);
dvander@27
  1250
    pc_printf("             0    no symbolic information, no run-time checks\n");
dvander@27
  1251
    pc_printf("             1    run-time checks, no symbolic information\n");
dvander@27
  1252
    pc_printf("             2    full debug information and dynamic checking\n");
dvander@27
  1253
    pc_printf("             3    same as -d2, but implies -O0\n");
dvander@27
  1254
#endif
dvander@27
  1255
    pc_printf("         -e<name> set name of error file (quiet compile)\n");
dvander@27
  1256
#if defined	__WIN32__ || defined _WIN32 || defined _Windows
dvander@27
  1257
    pc_printf("         -H<hwnd> window handle to send a notification message on finish\n");
dvander@27
  1258
#endif
dvander@27
  1259
    pc_printf("         -i<name> path for include files\n");
dvander@27
  1260
    pc_printf("         -l       create list file (preprocess only)\n");
dvander@27
  1261
    pc_printf("         -o<name> set base name of (P-code) output file\n");
dvander@27
  1262
    pc_printf("         -O<num>  optimization level (default=-O%d)\n",pc_optimize);
dvander@27
  1263
    pc_printf("             0    no optimization\n");
dvander@27
  1264
#if 0 /* not used for SourceMod */
dvander@27
  1265
    pc_printf("             1    JIT-compatible optimizations only\n");
dvander@27
  1266
#endif
dvander@27
  1267
    pc_printf("             2    full optimizations\n");
dvander@27
  1268
    pc_printf("         -p<name> set name of \"prefix\" file\n");
dvander@27
  1269
#if !defined SC_LIGHT
dvander@27
  1270
    pc_printf("         -r[name] write cross reference report to console or to specified file\n");
dvander@27
  1271
#endif
dvander@27
  1272
    pc_printf("         -S<num>  stack/heap size in cells (default=%d)\n",(int)pc_stksize);
dvander@27
  1273
    pc_printf("         -s<num>  skip lines from the input file\n");
dvander@27
  1274
    pc_printf("         -t<num>  TAB indent size (in character positions, default=%d)\n",sc_tabsize);
dvander@27
  1275
    pc_printf("         -v<num>  verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity);
dvander@27
  1276
    pc_printf("         -w<num>  disable a specific warning by its number\n");
dvander@27
  1277
    pc_printf("         -X<num>  abstract machine size limit in bytes\n");
dvander@27
  1278
    pc_printf("         -XD<num> abstract machine data/stack size limit in bytes\n");
dvander@27
  1279
    pc_printf("         -\\       use '\\' for escape characters\n");
dvander@27
  1280
    pc_printf("         -^       use '^' for escape characters\n");
dvander@27
  1281
    pc_printf("         -;[+/-]  require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-');
dvander@27
  1282
#if 0 /* not allowed in SourceMod */
dvander@27
  1283
    pc_printf("         -([+/-]  require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+');
dvander@27
  1284
#endif
dvander@27
  1285
    pc_printf("         sym=val  define constant \"sym\" with value \"val\"\n");
dvander@27
  1286
    pc_printf("         sym=     define constant \"sym\" with value 0\n");
dvander@27
  1287
#if defined	__WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__
dvander@27
  1288
    pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n");
dvander@27
  1289
    pc_printf("equivalent.\n");
dvander@27
  1290
#endif
dvander@27
  1291
    pc_printf("\nOptions with a value may optionally separate the value from the option letter\n");
dvander@27
  1292
    pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n");
dvander@27
  1293
    pc_printf("and \"-d:0\" are all equivalent.\n");
dvander@27
  1294
  } /* if */
dvander@27
  1295
  norun = 1;
dvander@27
  1296
  longjmp(errbuf,3);        /* user abort */
dvander@27
  1297
}
dvander@27
  1298
dvander@27
  1299
static void setconstants(void)
dvander@27
  1300
{
dvander@27
  1301
  int debug;
dvander@27
  1302
dvander@27
  1303
  assert(sc_status==statIDLE);
dvander@27
  1304
  append_constval(&tagname_tab,"_",0,0);/* "untagged" */
dvander@27
  1305
  append_constval(&tagname_tab,"bool",1,0);
ds@632
  1306
ds@632
  1307
  pc_anytag = pc_addtag("any");
dvander@132
  1308
  pc_functag = pc_addfunctag("Function");
dvander@151
  1309
  pc_tag_string = pc_addtag("String");
dvander@27
  1310
dvander@27
  1311
  add_constant("true",1,sGLOBAL,1);     /* boolean flags */
dvander@27
  1312
  add_constant("false",0,sGLOBAL,1);
dvander@27
  1313
  add_constant("EOS",0,sGLOBAL,0);      /* End Of String, or '\0' */
dvander@27
  1314
  #if PAWN_CELL_SIZE==16
dvander@27
  1315
    add_constant("cellbits",16,sGLOBAL,0);
dvander@27
  1316
    #if defined _I16_MAX
dvander@27
  1317
      add_constant("cellmax",_I16_MAX,sGLOBAL,0);
dvander@27
  1318
      add_constant("cellmin",_I16_MIN,sGLOBAL,0);
dvander@27
  1319
    #else
dvander@27
  1320
      add_constant("cellmax",SHRT_MAX,sGLOBAL,0);
dvander@27
  1321
      add_constant("cellmin",SHRT_MIN,sGLOBAL,0);
dvander@27
  1322
    #endif
dvander@27
  1323
  #elif PAWN_CELL_SIZE==32
dvander@27
  1324
    add_constant("cellbits",32,sGLOBAL,0);
dvander@27
  1325
    #if defined _I32_MAX
dvander@27
  1326
      add_constant("cellmax",_I32_MAX,sGLOBAL,0);
dvander@27
  1327
      add_constant("cellmin",_I32_MIN,sGLOBAL,0);
dvander@27
  1328
    #else
dvander@27
  1329
      add_constant("cellmax",LONG_MAX,sGLOBAL,0);
dvander@27
  1330
      add_constant("cellmin",LONG_MIN,sGLOBAL,0);
dvander@27
  1331
    #endif
dvander@27
  1332
  #elif PAWN_CELL_SIZE==64
dvander@27
  1333
    #if !defined _I64_MIN
dvander@27
  1334
      #define _I64_MIN  (-9223372036854775807ULL - 1)
dvander@27
  1335
      #define _I64_MAX    9223372036854775807ULL
dvander@27
  1336
    #endif
dvander@27
  1337
    add_constant("cellbits",64,sGLOBAL,0);
dvander@27
  1338
    add_constant("cellmax",_I64_MAX,sGLOBAL,0);
dvander@27
  1339
    add_constant("cellmin",_I64_MIN,sGLOBAL,0);
dvander@27
  1340
  #else
dvander@27
  1341
    #error Unsupported cell size
dvander@27
  1342
  #endif
dvander@27
  1343
  add_constant("charbits",sCHARBITS,sGLOBAL,0);
dvander@27
  1344
  add_constant("charmin",0,sGLOBAL,0);
dvander@27
  1345
  add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0);
dvander@27
  1346
  add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0);
dvander@27
  1347
dvander@27
  1348
  add_constant("__Pawn",VERSION_INT,sGLOBAL,0);
dvander@27
  1349
dvander@27
  1350
  debug=0;
dvander@27
  1351
  if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC))
dvander@27
  1352
    debug=2;
dvander@27
  1353
  else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS)
dvander@27
  1354
    debug=1;
dvander@27
  1355
  add_constant("debug",debug,sGLOBAL,0);
dvander@27
  1356
dvander@27
  1357
  append_constval(&sc_automaton_tab,"",0,0);    /* anonymous automaton */
dvander@27
  1358
}
dvander@27
  1359
dvander@27
  1360
static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst)
dvander@27
  1361
{
dvander@27
  1362
  int tok,err;
dvander@27
  1363
  cell val;
dvander@27
  1364
  char *str;
dvander@27
  1365
dvander@27
  1366
  assert(fconst!=NULL);
dvander@27
  1367
  assert(fstock!=NULL);
dvander@27
  1368
  assert(fstatic!=NULL);
dvander@27
  1369
  assert(fpublic!=NULL);
dvander@27
  1370
  *fconst=FALSE;
dvander@27
  1371
  *fstock=FALSE;
dvander@27
  1372
  *fstatic=FALSE;
dvander@27
  1373
  *fpublic=FALSE;
dvander@27
  1374
  switch (initialtok) {
dvander@27
  1375
  case tCONST:
dvander@27
  1376
    *fconst=TRUE;
dvander@27
  1377
    break;
dvander@27
  1378
  case tSTOCK:
dvander@27
  1379
    *fstock=TRUE;
dvander@27
  1380
    break;
dvander@27
  1381
  case tSTATIC:
dvander@27
  1382
    *fstatic=TRUE;
dvander@27
  1383
    break;
dvander@27
  1384
  case tPUBLIC:
dvander@27
  1385
    *fpublic=TRUE;
dvander@27
  1386
    break;
dvander@27
  1387
  } /* switch */
dvander@27
  1388
dvander@27
  1389
  err=0;
dvander@27
  1390
  do {
dvander@27
  1391
    tok=lex(&val,&str);  /* read in (new) token */
dvander@27
  1392
    switch (tok) {
dvander@27
  1393
    case tCONST:
dvander@27
  1394
      if (*fconst)
dvander@27
  1395
        err=42;          /* invalid combination of class specifiers */
dvander@27
  1396
      *fconst=TRUE;
dvander@27
  1397
      break;
dvander@27
  1398
    case tSTOCK:
dvander@27
  1399
      if (*fstock)
dvander@27
  1400
        err=42;          /* invalid combination of class specifiers */
dvander@27
  1401
      *fstock=TRUE;
dvander@27
  1402
      break;
dvander@27
  1403
    case tSTATIC:
dvander@27
  1404
      if (*fstatic)
dvander@27
  1405
        err=42;          /* invalid combination of class specifiers */
dvander@27
  1406
      *fstatic=TRUE;
dvander@27
  1407
      break;
dvander@27
  1408
    case tPUBLIC:
dvander@27
  1409
      if (*fpublic)
dvander@27
  1410
        err=42;          /* invalid combination of class specifiers */
dvander@27
  1411
      *fpublic=TRUE;
dvander@27
  1412
      break;
dvander@27
  1413
    default:
dvander@27
  1414
      lexpush();
dvander@27
  1415
      tok=0;             /* force break out of loop */
dvander@27
  1416
    } /* switch */
dvander@27
  1417
  } while (tok && err==0);
dvander@27
  1418
dvander@27
  1419
  /* extra checks */
dvander@27
  1420
  if (*fstatic && *fpublic) {
dvander@27
  1421
    err=42;              /* invalid combination of class specifiers */
dvander@27
  1422
    *fstatic=*fpublic=FALSE;
dvander@27
  1423
  } /* if */
dvander@27
  1424
dvander@27
  1425
  if (err)
dvander@27
  1426
    error(err);
dvander@27
  1427
  return err==0;
dvander@27
  1428
}
dvander@27
  1429
dvander@27
  1430
/*  parse       - process all input text
dvander@27
  1431
 *
dvander@27
  1432
 *  At this level, only static declarations and function definitions are legal.
dvander@27
  1433
 */
dvander@27
  1434
static void parse(void)
dvander@27
  1435
{
dvander@27
  1436
  int tok,fconst,fstock,fstatic,fpublic;
dvander@27
  1437
  cell val;
dvander@27
  1438
  char *str;
dvander@27
  1439
dvander@27
  1440
  while (freading){
dvander@27
  1441
    /* first try whether a declaration possibly is native or public */
dvander@27
  1442
    tok=lex(&val,&str);  /* read in (new) token */
dvander@27
  1443
    switch (tok) {
dvander@27
  1444
    case 0:
dvander@27
  1445
      /* ignore zero's */
dvander@27
  1446
      break;
dvander@27
  1447
    case tNEW:
dvander@27
  1448
      if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst))
dvander@27
  1449
        declglb(NULL,0,fpublic,fstatic,fstock,fconst);
dvander@27
  1450
      break;
dvander@27
  1451
    case tSTATIC:
dvander@27
  1452
      /* This can be a static function or a static global variable; we know
dvander@27
  1453
       * which of the two as soon as we have parsed up to the point where an
dvander@27
  1454
       * opening paranthesis of a function would be expected. To back out after
dvander@27
  1455
       * deciding it was a declaration of a static variable after all, we have
dvander@27
  1456
       * to store the symbol name and tag.
dvander@27
  1457
       */
dvander@27
  1458
      if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
dvander@27
  1459
        assert(!fpublic);
dvander@27
  1460
        declfuncvar(fpublic,fstatic,fstock,fconst);
dvander@27
  1461
      } /* if */
dvander@27
  1462
      break;
dvander@27
  1463
    case tCONST:
dvander@27
  1464
      decl_const(sGLOBAL);
dvander@27
  1465
      break;
dvander@27
  1466
    case tENUM:
dvander@27
  1467
      decl_enum(sGLOBAL);
dvander@27
  1468
      break;
dvander@132
  1469
    case tFUNCENUM:
dvander@338
  1470
      dofuncenum(TRUE);
dvander@338
  1471
      break;
dvander@338
  1472
    case tFUNCTAG:
dvander@338
  1473
      dofuncenum(FALSE);
dvander@132
  1474
      break;
dvander@167
  1475
    case tSTRUCT:
dvander@167
  1476
      declstruct();
dvander@167
  1477
      break;
dvander@27
  1478
    case tPUBLIC:
dvander@27
  1479
      /* This can be a public function or a public variable; see the comment
dvander@27
  1480
       * above (for static functions/variables) for details.
dvander@27
  1481
       */
dvander@27
  1482
      if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
dvander@27
  1483
        assert(!fstatic);
dvander@27
  1484
        declfuncvar(fpublic,fstatic,fstock,fconst);
dvander@27
  1485
      } /* if */
dvander@27
  1486
      break;
dvander@27
  1487
    case tSTOCK:
dvander@27
  1488
      /* This can be a stock function or a stock *global*) variable; see the
dvander@27
  1489
       * comment above (for static functions/variables) for details.
dvander@27
  1490
       */
dvander@27
  1491
      if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
dvander@27
  1492
        assert(fstock);
dvander@27
  1493
        declfuncvar(fpublic,fstatic,fstock,fconst);
dvander@27
  1494
      } /* if */
dvander@27
  1495
      break;
dvander@27
  1496
    case tLABEL:
dvander@27
  1497
    case tSYMBOL:
dvander@27
  1498
    case tOPERATOR:
dvander@27
  1499
      lexpush();
dvander@27
  1500
      if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) {
dvander@27
  1501
        error(10);              /* illegal function or declaration */
dvander@27
  1502
        lexclr(TRUE);           /* drop the rest of the line */
dvander@27
  1503
        litidx=0;               /* drop the literal queue too */
dvander@27
  1504
      } /* if */
dvander@27
  1505
      break;
dvander@27
  1506
    case tNATIVE:
dvander@27
  1507
      funcstub(TRUE);           /* create a dummy function */
dvander@27
  1508
      break;
dvander@27
  1509
    case tFORWARD:
dvander@27
  1510
      funcstub(FALSE);
dvander@27
  1511
      break;
dvander@27
  1512
    case '}':
dvander@27
  1513
      error(54);                /* unmatched closing brace */
dvander@27
  1514
      break;
dvander@27
  1515
    case '{':
dvander@27
  1516
      error(55);                /* start of function body without function header */
dvander@27
  1517
      break;
dvander@27
  1518
    default:
dvander@27
  1519
      if (freading) {
dvander@27
  1520
        error(10);              /* illegal function or declaration */
dvander@27
  1521
        lexclr(TRUE);           /* drop the rest of the line */
dvander@27
  1522
        litidx=0;               /* drop any literal arrays (strings) */
dvander@27
  1523
      } /* if */
dvander@27
  1524
    } /* switch */
dvander@27
  1525
  } /* while */
dvander@27
  1526
}
dvander@27
  1527
dvander@27
  1528
/*  dumplits
dvander@27
  1529
 *
dvander@27
  1530
 *  Dump the literal pool (strings etc.)
dvander@27
  1531
 *
dvander@27
  1532
 *  Global references: litidx (referred to only)
dvander@27
  1533
 */
dvander@27
  1534
static void dumplits(void)
dvander@27
  1535
{
dvander@27
  1536
  int j,k;
dvander@27
  1537
faluco@62
  1538
  if (sc_status==statSKIP)
faluco@62
  1539
    return;
faluco@62
  1540
dvander@27
  1541
  k=0;
dvander@27
  1542
  while (k<litidx){
dvander@27
  1543
    /* should be in the data segment */
dvander@27
  1544
    assert(curseg==2);
dvander@27
  1545
    defstorage();
dvander@27
  1546
    j=16;       /* 16 values per line */
dvander@27
  1547
    while (j && k<litidx){
dvander@27
  1548
      outval(litq[k], FALSE);
dvander@27
  1549
      stgwrite(" ");
dvander@27
  1550
      k++;
dvander@27
  1551
      j--;
dvander@27
  1552
      if (j==0 || k>=litidx)
dvander@27
  1553
        stgwrite("\n");         /* force a newline after 10 dumps */
dvander@27
  1554
      /* Note: stgwrite() buffers a line until it is complete. It recognizes
dvander@27
  1555
       * the end of line as a sequence of "\n\0", so something like "\n\t"
dvander@27
  1556
       * so should not be passed to stgwrite().
dvander@27
  1557
       */
dvander@27
  1558
    } /* while */
dvander@27
  1559
  } /* while */
dvander@27
  1560
}
dvander@27
  1561
dvander@27
  1562
/*  dumpzero
dvander@27
  1563
 *
dvander@27
  1564
 *  Dump zero's for default initial values
dvander@27
  1565
 */
dvander@27
  1566
static void dumpzero(int count)
dvander@27
  1567
{
dvander@27
  1568
  int i;
dvander@27
  1569
faluco@62
  1570
  if (sc_status==statSKIP || count<=0)
dvander@27
  1571
    return;
dvander@27
  1572
  assert(curseg==2);
dvander@27
  1573
  defstorage();
dvander@27
  1574
  i=0;
dvander@27
  1575
  while (count-- > 0) {
dvander@27
  1576
    outval(0, FALSE);
dvander@27
  1577
    i=(i+1) % 16;
dvander@27
  1578
    stgwrite((i==0 || count==0) ? "\n" : " ");
dvander@27
  1579
    if (i==0 && count>0)
dvander@27
  1580
      defstorage();
dvander@27
  1581
  } /* while */
dvander@27
  1582
}
dvander@27
  1583
dvander@27
  1584
static void aligndata(int numbytes)
dvander@27
  1585
{
dvander@27
  1586
  assert(numbytes % sizeof(cell) == 0);   /* alignment must be a multiple of
dvander@27
  1587
                                           * the cell size */
dvander@27
  1588
  assert(numbytes!=0);
dvander@27
  1589
dvander@27
  1590
  if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) {
dvander@27
  1591
    while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0)
dvander@27
  1592
      litadd(0);
dvander@27
  1593
  } /* if */
dvander@27
  1594
dvander@27
  1595
}
dvander@27
  1596
dvander@27
  1597
#if !defined SC_LIGHT
dvander@27
  1598
/* sc_attachdocumentation()
dvander@27
  1599
 * appends documentation comments to the passed-in symbol, or to a global
dvander@27
  1600
 * string if "sym" is NULL.
dvander@27
  1601
 */
dvander@27
  1602
void sc_attachdocumentation(symbol *sym)
dvander@27
  1603
{
dvander@27
  1604
  int line;
dvander@27
  1605
  size_t length;
dvander@27
  1606
  char *str,*doc;
dvander@27
  1607
dvander@27
  1608
  if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) {
dvander@27
  1609
    /* just clear the entire table */
dvander@27
  1610
    delete_docstringtable();
dvander@27
  1611
    return;
dvander@27
  1612
  } /* if */
dvander@27
  1613
  /* in the case of state functions, multiple documentation sections may
dvander@27
  1614
   * appear; we should concatenate these
dvander@27
  1615
   * (with forward declarations, this is also already the case, so the assertion
dvander@27
  1616
   * below is invalid)
dvander@27
  1617
   */
dvander@27
  1618
  // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL);
dvander@27
  1619
dvander@27
  1620
  /* first check the size */
dvander@27
  1621
  length=0;
dvander@27
  1622
  for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
dvander@27
  1623
    if (length>0)
dvander@27
  1624
      length++;   /* count 1 extra for a separating space */
dvander@27
  1625
    length+=strlen(str);
dvander@27
  1626
  } /* for */
dvander@27
  1627
  if (sym==NULL && sc_documentation!=NULL) {
dvander@27
  1628
    length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
dvander@27
  1629
    assert(length>strlen(sc_documentation));
dvander@27
  1630
  } /* if */
dvander@27
  1631
dvander@27
  1632
  if (length>0) {
dvander@27
  1633
    /* allocate memory for the documentation */
dvander@27
  1634
    if (sym!=NULL && sym->documentation!=NULL)
dvander@27
  1635
      length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
dvander@27
  1636
    doc=(char*)malloc((length+1)*sizeof(char));
dvander@27
  1637
    if (doc!=NULL) {
dvander@27
  1638
      /* initialize string or concatenate */
dvander@27
  1639
      if (sym==NULL && sc_documentation!=NULL) {
dvander@27
  1640
        strcpy(doc,sc_documentation);
dvander@27
  1641
        strcat(doc,"<p/>");
dvander@27
  1642
      } else if (sym!=NULL && sym->documentation!=NULL) {
dvander@27
  1643
        strcpy(doc,sym->documentation);
dvander@27
  1644
        strcat(doc,"<p/>");
dvander@27
  1645
        free(sym->documentation);
dvander@27
  1646
        sym->documentation=NULL;
dvander@27
  1647
      } else {
dvander@27
  1648
        doc[0]='\0';
dvander@27
  1649
      } /* if */
dvander@27
  1650
      /* collect all documentation */
dvander@27
  1651
      while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
dvander@27
  1652
        if (doc[0]!='\0')
dvander@27
  1653
          strcat(doc," ");
dvander@27
  1654
        strcat(doc,str);
dvander@27
  1655
        delete_docstring(0);
dvander@27
  1656
      } /* while */
dvander@27
  1657
      if (str!=NULL) {
dvander@27
  1658
        /* also delete the separator */
dvander@27
  1659
        assert(*str==sDOCSEP);
dvander@27
  1660
        delete_docstring(0);
dvander@27
  1661
      } /* if */
dvander@27
  1662
      if (sym!=NULL) {
dvander@27
  1663
        assert(sym->documentation==NULL);
dvander@27
  1664
        sym->documentation=doc;
dvander@27
  1665
      } else {
dvander@27
  1666
        if (sc_documentation!=NULL)
dvander@27
  1667
          free(sc_documentation);
dvander@27
  1668
        sc_documentation=doc;
dvander@27
  1669
      } /* if */
dvander@27
  1670
    } /* if */
dvander@27
  1671
  } else {
dvander@27
  1672
    /* delete an empty separator, if present */
dvander@27
  1673
    if ((str=get_docstring(0))!=NULL && *str==sDOCSEP)
dvander@27
  1674
      delete_docstring(0);
dvander@27
  1675
  } /* if */
dvander@27
  1676
}
dvander@27
  1677
dvander@27
  1678
static void insert_docstring_separator(void)
dvander@27
  1679
{
dvander@27
  1680
  char sep[2]={sDOCSEP,'\0'};
dvander@27
  1681
  insert_docstring(sep);
dvander@27
  1682
}
dvander@27
  1683
#else
dvander@27
  1684
  #define sc_attachdocumentation(s)      (void)(s)
dvander@27
  1685
  #define insert_docstring_separator()
dvander@27
  1686
#endif
dvander@27
  1687
dvander@27
  1688
static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst)
dvander@27
  1689
{
dvander@27
  1690
  char name[sNAMEMAX+11];
dvander@167
  1691
  int tok,tag=0;
dvander@27
  1692
  char *str;
dvander@27
  1693
  cell val;
dvander@27
  1694
  int invalidfunc;
dvander@167
  1695
  pstruct_t *pstruct = NULL;
dvander@167
  1696
dvander@167
  1697
  tok=lex(&val,&str);
dvander@167
  1698
  if (tok == tLABEL) {
dvander@167
  1699
    pstruct=pstructs_find(str);
dvander@167
  1700
    tag=pc_addtag(str);
dvander@167
  1701
  } else {
dvander@167
  1702
    lexpush();
dvander@167
  1703
  }
dvander@27
  1704
  tok=lex(&val,&str);
dvander@27
  1705
  /* if we arrived here, this may not be a declaration of a native function
dvander@27
  1706
   * or variable
dvander@27
  1707
   */
dvander@27
  1708
  if (tok==tNATIVE) {
dvander@27
  1709
    error(42);          /* invalid combination of class specifiers */
dvander@27
  1710
    return;
dvander@27
  1711
  } /* if */
dvander@27
  1712
dvander@27
  1713
  if (tok!=tSYMBOL && tok!=tOPERATOR) {
dvander@27
  1714
    lexpush();
dvander@27
  1715
    needtoken(tSYMBOL);
dvander@27
  1716
    lexclr(TRUE);       /* drop the rest of the line */
dvander@27
  1717
    litidx=0;           /* drop the literal queue too */
dvander@27
  1718
    return;
dvander@27
  1719
  } /* if */
dvander@27
  1720
  if (tok==tOPERATOR) {
dvander@27
  1721
    lexpush();          /* push "operator" keyword back (for later analysis) */
dvander@27
  1722
    if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) {
dvander@27
  1723
      error(10);        /* illegal function or declaration */
dvander@27
  1724
      lexclr(TRUE);     /* drop the rest of the line */
dvander@27
  1725
      litidx=0;         /* drop the literal queue too */
dvander@27
  1726
    } /* if */
dvander@27
  1727
  } else {
dvander@27
  1728
    /* so tok is tSYMBOL */
dvander@27
  1729
    assert(strlen(str)<=sNAMEMAX);
dvander@27
  1730
    strcpy(name,str);
dvander@27
  1731
    /* only variables can be "const" or both "public" and "stock" */
dvander@27
  1732
    invalidfunc= fconst || (fpublic && fstock);
dvander@27
  1733
    if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) {
dvander@27
  1734
      /* if not a function, try a global variable */
dvander@167
  1735
      if (pstruct) {
dvander@167
  1736
        declstructvar(name,fpublic,pstruct);
dvander@167
  1737
      } else {
dvander@167
  1738
        declglb(name,tag,fpublic,fstatic,fstock,fconst);
dvander@167
  1739
      }
dvander@27
  1740
    } /* if */
dvander@27
  1741
  } /* if */
dvander@27
  1742
}
dvander@27
  1743
dvander@167
  1744
/* declstruct	- declare global struct symbols
dvander@167
  1745
 * 
dvander@167
  1746
 * global references: glb_declared		(altered)
dvander@167
  1747
 */
dvander@167
  1748
static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
dvander@167
  1749
{
dvander@167
  1750
	char name[sNAMEMAX+1];
dvander@167
  1751
	int tok,i;
dvander@167
  1752
	cell val;
dvander@167
  1753
	char *str;
dvander@167
  1754
	int cur_litidx = 0;
dvander@167
  1755
	cell *values, *found;
dvander@167
  1756
	int usage;
dvander@169
  1757
	symbol *mysym,*sym;
dvander@167
  1758
dvander@167
  1759
	strcpy(name, firstname);
dvander@167
  1760
dvander@167
  1761
	values = (cell *)malloc(pstruct->argcount * sizeof(cell));
dvander@167
  1762
	found = (cell *)malloc(pstruct->argcount * sizeof(cell));
dvander@167
  1763
	
dvander@167
  1764
	memset(found, 0, sizeof(cell) * pstruct->argcount);
dvander@167
  1765
dvander@169
  1766
	//:TODO: Make this work with stock
dvander@167
  1767
dvander@167
  1768
	/**
dvander@167
  1769
	 * Lastly, very lastly, we will insert a copy of this variable.
dvander@167
  1770
	 * This is soley to expose the pubvar.
dvander@167
  1771
	 */
dvander@169
  1772
	usage = uREAD|uCONST|uSTRUCT;
dvander@167
  1773
	if (fpublic)
dvander@167
  1774
	{
dvander@167
  1775
		usage |= uPUBLIC;
dvander@167
  1776
	}
dvander@169
  1777
	mysym = NULL;
dvander@169
  1778
	for (sym=glbtab.next; sym!=NULL; sym=sym->next)
dvander@169
  1779
	{
dvander@169
  1780
		if (strcmp(name, sym->name) == 0)
dvander@169
  1781
		{
dvander@169
  1782
			if ((sym->usage & uSTRUCT) && sym->vclass == sGLOBAL)
dvander@169
  1783
			{
dvander@169
  1784
				if (sym->usage & uDEFINE)
dvander@169
  1785
				{
dvander@169
  1786
					error(21, name);
dvander@169
  1787
				} else {
dvander@169
  1788
					if (sym->usage & uPUBLIC && !fpublic)
dvander@169
  1789
					{
dvander@169
  1790
 						error(42);
dvander@169
  1791
					}
dvander@169
  1792
				}
dvander@169
  1793
			} else {
dvander@169
  1794
				error(21, name);
dvander@169
  1795
			}
dvander@169
  1796
			mysym = sym;
dvander@169
  1797
			break;
dvander@169
  1798
		}
dvander@169
  1799
	}
dvander@169
  1800
	if (!mysym)
dvander@169
  1801
	{
dvander@169
  1802
		mysym=addsym(name, 0, iVARIABLE, sGLOBAL, pc_addtag(pstruct->name), usage);
dvander@169
  1803
	}
dvander@169
  1804
dvander@169
  1805
	if (!matchtoken('='))
dvander@167
  1806
	{
dvander@167
  1807
		matchtoken(';');
dvander@169
  1808
		/* Mark it as undefined instead */
dvander@169
  1809
		mysym->usage = uSTOCK|uSTRUCT;
dvander@167
  1810
		return;
dvander@169
  1811
	} else {
dvander@169
  1812
		mysym->usage = usage;
dvander@167
  1813
	}
dvander@167
  1814
	needtoken('{');
dvander@167
  1815
	do
dvander@167
  1816
	{
dvander@167
  1817
		structarg_t *arg;
dvander@167
  1818
		/* Detect early exit */
dvander@167
  1819
		if (matchtoken('}'))
dvander@167
  1820
		{
dvander@167
  1821
			lexpush();
dvander@167
  1822
			break;
dvander@167
  1823
		}
dvander@167
  1824
		tok=lex(&val,&str);
dvander@167
  1825
		if (tok != tSYMBOL)
dvander@167
  1826
		{
dvander@167
  1827
			error(1, "-identifier-", str);
dvander@167
  1828
			continue;
dvander@167
  1829
		}
dvander@167
  1830
		arg=pstructs_getarg(pstruct,str);
dvander@167
  1831
		if (arg == NULL)
dvander@167
  1832
		{
ds@841
  1833
			error(96, str, sym->name);
dvander@167
  1834
		}
dvander@167
  1835
		needtoken('=');
dvander@167
  1836
		cur_litidx = litidx;
dvander@167
  1837
		tok=lex(&val,&str);
dvander@167
  1838
		if (!arg)
dvander@167
  1839
		{
dvander@167
  1840
			continue;
dvander@167
  1841
		}
dvander@167
  1842
		if (tok == tSTRING)
dvander@167
  1843
		{
dvander@167
  1844
			assert(litidx != 0);
dvander@167
  1845
			if (arg->dimcount != 1)
dvander@167
  1846
			{
dvander@167
  1847
				error(48);
dvander@167
  1848
			} else if (arg->tag != pc_tag_string) {
dvander@167
  1849
				error(213);
dvander@167
  1850
			}
dvander@167
  1851
			values[arg->index] = glb_declared * sizeof(cell);
dvander@167
  1852
			glb_declared += (litidx-cur_litidx);
dvander@167
  1853
			found[arg->index] = 1;
dvander@167
  1854
		} else if (tok == tNUMBER || tok == tRATIONAL) {
dvander@167
  1855
			/* eat optional 'f' */
dvander@167
  1856
			matchtoken('f');
dvander@167
  1857
			if (arg->ident != iVARIABLE && arg->ident != iREFERENCE)
dvander@167
  1858
			{
dvander@167
  1859
				error(23);
dvander@167
  1860
			} else {
dvander@167
  1861
				if (arg->tag == pc_addtag("Float") && tok == tNUMBER ||
dvander@167
  1862
					arg->tag == 0 && tok == tRATIONAL)
dvander@167
  1863
				{
dvander@167
  1864
					error(213);
dvander@167
  1865
				}
dvander@167
  1866
				if (arg->ident == iVARIABLE)
dvander@167
  1867
				{
dvander@167
  1868
					values[arg->index] = val;
dvander@167
  1869
				} else if (arg->ident == iREFERENCE) {
dvander@167
  1870
					values[arg->index] = glb_declared * sizeof(cell);
dvander@167
  1871
					glb_declared += 1;
dvander@167
  1872
					litadd(val);
dvander@167
  1873
					cur_litidx = litidx;
dvander@167
  1874
				}
dvander@167
  1875
				found[arg->index] = 1;
dvander@167
  1876
			}
dvander@167
  1877
		} else if (tok == tSYMBOL) {
dvander@167
  1878
			for (sym=glbtab.next; sym!=NULL; sym=sym->next)
dvander@167
  1879
			{
dvander@167
  1880
				if (sym->vclass != sGLOBAL)
dvander@167
  1881
				{
dvander@167
  1882
					continue;
dvander@167
  1883
				}
dvander@167
  1884
				if (strcmp(sym->name, str) == 0)
dvander@167
  1885
				{
dvander@167
  1886
					if (arg->ident == iREFERENCE && sym->ident != iVARIABLE)
dvander@167
  1887
					{
ds@841
  1888
						error(97, str);
dvander@167
  1889
					} else if (arg->ident == iARRAY) {
dvander@167
  1890
						if (sym->ident != iARRAY)
dvander@167
  1891
						{
ds@841
  1892
							error(97, str);
dvander@167
  1893
						} else {
dvander@167
  1894
							/* :TODO: We should check dimension sizes here... */
dvander@167
  1895
						}
dvander@167
  1896
					} else if (arg->ident == iREFARRAY) {
dvander@167
  1897
						if (sym->ident != iARRAY)
dvander@167
  1898
						{
ds@841
  1899
							error(97, str);
dvander@167
  1900
						}
dvander@167
  1901
						/* :TODO: Check dimension sizes! */
dvander@167
  1902
					} else {
ds@841
  1903
						error(97, str);
dvander@167
  1904
					}
dvander@167
  1905
					if (sym->tag != arg->tag)
dvander@167
  1906
					{
dvander@167
  1907
						error(213);
dvander@167
  1908
					}
dvander@167
  1909
					sym->usage |= uREAD;
dvander@167
  1910
					values[arg->index] = sym->addr;
dvander@167
  1911
					found[arg->index] = 1;
dvander@167
  1912
					refer_symbol(sym, mysym);
dvander@167
  1913
					break;
dvander@167
  1914
				}
dvander@167
  1915
			}
dvander@167
  1916
			if (!sym)
dvander@167
  1917
			{
dvander@167
  1918
				error(17, str);
dvander@167
  1919
			}
dvander@167
  1920
		} else {
dvander@167
  1921
			error(1, "-identifier-", str);
dvander@167
  1922
		}
dvander@167
  1923
	} while (matchtoken(','));
dvander@167
  1924
	needtoken('}');
dvander@167
  1925
	matchtoken(';');	/* eat up optional semicolon */
dvander@167
  1926
dvander@167
  1927
	for (i=0; i<pstruct->argcount; i++)
dvander@167
  1928
	{
dvander@167
  1929
		if (!found[i])
dvander@167
  1930
		{
dvander@167
  1931
			structarg_t *arg = pstruct->args[i];
dvander@167
  1932
			if (arg->ident == iREFARRAY)
dvander@167
  1933
			{
dvander@167
  1934
				values[arg->index] = glb_declared * sizeof(cell);
dvander@167
  1935
				glb_declared += 1;
dvander@167
  1936
				litadd(0);
dvander@167
  1937
				cur_litidx = litidx;
dvander@167
  1938
			} else if (arg->ident == iVARIABLE) {
dvander@167
  1939
				values[arg->index] = 0;
dvander@167
  1940
			} else {
dvander@167
  1941
				/* :TODO: broken for iARRAY! (unused tho) */
dvander@167
  1942
			}
dvander@167
  1943
		}
dvander@167
  1944
	}
dvander@167
  1945
dvander@167
  1946
	mysym->addr = glb_declared * sizeof(cell);
dvander@167
  1947
	glb_declared += pstruct->argcount;
dvander@167
  1948
dvander@167
  1949
	for (i=0; i<pstruct->argcount; i++)
dvander@167
  1950
	{
dvander@167
  1951
		litadd(values[i]);
dvander@167
  1952
	}
dvander@167
  1953
dvander@167
  1954
	begdseg();
dvander@167
  1955
	dumplits();
dvander@167
  1956
	litidx=0;
dvander@167
  1957
dvander@167
  1958
	free(found);
dvander@167
  1959
	free(values);
dvander@167
  1960
}
dvander@167
  1961
dvander@27
  1962
/*  declglb     - declare global symbols
dvander@27
  1963
 *
dvander@27
  1964
 *  Declare a static (global) variable. Global variables are stored in
dvander@27
  1965
 *  the DATA segment.
dvander@27
  1966
 *
dvander@27
  1967
 *  global references: glb_declared     (altered)
dvander@27
  1968
 */
dvander@27
  1969
static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst)
dvander@27
  1970
{
dvander@27
  1971
  int ident,tag,ispublic;
dvander@27
  1972
  int idxtag[sDIMEN_MAX];
dvander@27
  1973
  char name[sNAMEMAX+1];
dvander@27
  1974
  cell val,size,cidx;
dvander@27
  1975
  ucell address;
dvander@27
  1976
  int glb_incr;
dvander@27
  1977
  char *str;
dvander@27
  1978
  int dim[sDIMEN_MAX];
dvander@27
  1979
  int numdim;
dvander@286
  1980
  int slength=0;
dvander@27
  1981
  short filenum;
dvander@27
  1982
  symbol *sym;
dvander@27
  1983
  constvalue *enumroot;
dvander@27
  1984
  #if !defined NDEBUG
dvander@27
  1985
    cell glbdecl=0;
dvander@27
  1986
  #endif
dvander@27
  1987
dvander@27
  1988
  assert(!fpublic || !fstatic);         /* may not both be set */
dvander@27
  1989
  insert_docstring_separator();         /* see comment in newfunc() */
dvander@27
  1990
  filenum=fcurrent;                     /* save file number at the start of the declaration */
dvander@27
  1991
  do {
dvander@27
  1992
    size=1;                             /* single size (no array) */
dvander@27
  1993
    numdim=0;                           /* no dimensions */
dvander@27
  1994
    ident=iVARIABLE;
dvander@27
  1995
    if (firstname!=NULL) {
dvander@27
  1996
      assert(strlen(firstname)<=sNAMEMAX);
dvander@27
  1997
      strcpy(name,firstname);           /* save symbol name */
dvander@27
  1998
      tag=firsttag;
dvander@27
  1999
      firstname=NULL;
dvander@27
  2000
    } else {
dvander@27
  2001
      tag=pc_addtag(NULL);
dvander@27
  2002
      if (lex(&val,&str)!=tSYMBOL)      /* read in (new) token */
dvander@27
  2003
        error(20,str);                  /* invalid symbol name */
dvander@27
  2004
      assert(strlen(str)<=sNAMEMAX);
dvander@27
  2005
      strcpy(name,str);                 /* save symbol name */
dvander@27
  2006
    } /* if */
dvander@27
  2007
    ispublic=fpublic;
dvander@27
  2008
    if (name[0]==PUBLIC_CHAR) {
dvander@27
  2009
      ispublic=TRUE;                    /* implicitly public variable */
dvander@27
  2010
      assert(!fstatic);
dvander@27
  2011
    } /* if */
dvander@118
  2012
	while (matchtoken('[')) {
dvander@27
  2013
      ident=iARRAY;
dvander@27
  2014
      if (numdim == sDIMEN_MAX) {
dvander@27
  2015
        error(53);                      /* exceeding maximum number of dimensions */
dvander@27
  2016
        return;
dvander@27
  2017
      } /* if */
dvander@27
  2018
      size=needsub(&idxtag[numdim],&enumroot);  /* get size; size==0 for "var[]" */
dvander@27
  2019
      #if INT_MAX < LONG_MAX
dvander@27
  2020
        if (size > INT_MAX)
ds@841
  2021
          error(125);                   /* overflow, exceeding capacity */
dvander@27
  2022
      #endif
dvander@127
  2023
#if 0	/* We don't actually care */
dvander@27
  2024
      if (ispublic)
dvander@27
  2025
        error(56,name);                 /* arrays cannot be public */
dvander@127
  2026
#endif
dvander@27
  2027
      dim[numdim++]=(int)size;
dvander@27
  2028
    } /* while */
dvander@286
  2029
    if (ident == iARRAY && tag == pc_tag_string && dim[numdim-1]) {
dvander@286
  2030
      slength=dim[numdim-1];
dvander@151
  2031
      dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
dvander@286
  2032
    }
dvander@27
  2033
    assert(sc_curstates==0);
dvander@27
  2034
    sc_curstates=getstates(name);
dvander@27
  2035
    if (sc_curstates<0) {
dvander@27
  2036
      error(85,name);           /* empty state list on declaration */
dvander@27
  2037
      sc_curstates=0;
dvander@27
  2038
    } else if (sc_curstates>0 && ispublic) {
dvander@27
  2039
      error(88,name);           /* public variables may not have states */
dvander@27
  2040
      sc_curstates=0;
dvander@27
  2041
    } /* if */
dvander@27
  2042
    sym=findconst(name,NULL);
dvander@27
  2043
    if (sym==NULL) {
dvander@27
  2044
      sym=findglb(name,sSTATEVAR);
dvander@27
  2045
      /* if a global variable without states is found and this declaration has
dvander@27
  2046
       * states, the declaration is okay
dvander@27
  2047
       */
dvander@27
  2048
      if (sym!=NULL && sym->states==NULL && sc_curstates>0)
dvander@27
  2049
        sym=NULL;               /* set to NULL, we found the global variable */
dvander@27
  2050
      if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL)
dvander@27
  2051
        error(233,name);        /* state variable shadows a global variable */
dvander@27
  2052
    } /* if */
dvander@27
  2053
    /* we have either:
dvander@27
  2054
     * a) not found a matching variable (or rejected it, because it was a shadow)
dvander@27
  2055
     * b) found a global variable and we were looking for that global variable
dvander@27
  2056
     * c) found a state variable in the automaton that we were looking for
dvander@27
  2057
     */
dvander@27
  2058
    assert(sym==NULL
dvander@27
  2059
           || sym->states==NULL && sc_curstates==0
dvander@27
  2060
           || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates);
dvander@27
  2061
    /* a state variable may only have a single id in its list (so either this
dvander@27
  2062
     * variable has no states, or it has a single list)
dvander@27
  2063
     */
dvander@27
  2064
    assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL);
dvander@27
  2065
    /* it is okay for the (global) variable to exist, as long as it belongs to
dvander@27
  2066
     * a different automaton
dvander@27
  2067
     */
dvander@27
  2068
    if (sym!=NULL && (sym->usage & uDEFINE)!=0)
dvander@27
  2069
      error(21,name);                   /* symbol already defined */
dvander@27
  2070
    /* if this variable is never used (which can be detected only in the
dvander@27
  2071
     * second stage), shut off code generation
dvander@27
  2072
     */
dvander@27
  2073
    cidx=0;             /* only to avoid a compiler warning */
dvander@27
  2074
    if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) {
dvander@27
  2075
      sc_status=statSKIP;
dvander@27
  2076
      cidx=code_idx;
dvander@27
  2077
      #if !defined NDEBUG
dvander@27
  2078
        glbdecl=glb_declared;
dvander@27
  2079
      #endif
dvander@27
  2080
    } /* if */
dvander@27
  2081
    begdseg();          /* real (initialized) data in data segment */
dvander@27
  2082
    assert(litidx==0);  /* literal queue should be empty */
dvander@27
  2083
    if (sc_alignnext) {
dvander@27
  2084
      litidx=0;
dvander@27
  2085
      aligndata(sc_dataalign);
dvander@27
  2086
      dumplits();       /* dump the literal queue */
dvander@27
  2087
      sc_alignnext=FALSE;
dvander@27
  2088
      litidx=0;         /* global initial data is dumped, so restart at zero */
dvander@27
  2089
    } /* if */
dvander@27
  2090
    assert(litidx==0);  /* literal queue should be empty (again) */
dvander@27
  2091
    initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */
dvander@510
  2092
    if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
dvander@510
  2093
      slength = glbstringread;
dvander@510
  2094
    }
dvander@27
  2095
    assert(size>=litidx);
dvander@27
  2096
    if (numdim==1)
dvander@27
  2097
      dim[0]=(int)size;
dvander@27
  2098
    /* before dumping the initial values (or zeros) check whether this variable
dvander@27
  2099
     * overlaps another
dvander@27
  2100
     */
dvander@27
  2101
    if (sc_curstates>0) {
dvander@27
  2102
      unsigned char *map;
dvander@27
  2103
dvander@27
  2104
      if (litidx!=0)
dvander@27
  2105
        error(89,name); /* state variables may not be initialized */
dvander@27
  2106
      /* find an appropriate address for the state variable */
dvander@27
  2107
      /* assume that it cannot be found */
dvander@27
  2108
      address=sizeof(cell)*glb_declared;
dvander@27
  2109
      glb_incr=(int)size;
dvander@27
  2110
      /* use a memory map in which every cell occupies one bit */
dvander@27
  2111
      if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) {
dvander@27
  2112
        int fsa=state_getfsa(sc_curstates);
dvander@27
  2113
        symbol *sweep;
dvander@27
  2114
        cell sweepsize,addr;
dvander@27
  2115
        memset(map,0,(glb_declared+7)/8);
dvander@27
  2116
        assert(fsa>=0);
dvander@27
  2117
        /* fill in all variables belonging to this automaton */
dvander@27
  2118
        for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
dvander@27
  2119
          if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
dvander@27
  2120
            continue;   /* hierarchical type, or no states, or same as this variable */
dvander@27
  2121
          if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
dvander@27
  2122
            continue;   /* a function or a constant */
dvander@27
  2123
          if ((sweep->usage & uDEFINE)==0)
dvander@27
  2124
            continue;   /* undefined variable, ignore */
dvander@27
  2125
          if (fsa!=state_getfsa(sweep->states->next->index))
dvander@27
  2126
            continue;   /* wrong automaton */
dvander@27
  2127
          /* when arrived here, this is a global variable, with states and
dvander@27
  2128
           * belonging to the same automaton as the variable we are declaring
dvander@27
  2129
           */
dvander@27
  2130
          sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
dvander@27
  2131
          assert(sweep->addr % sizeof(cell) == 0);
dvander@27
  2132
          addr=sweep->addr/sizeof(cell);
dvander@27
  2133
          /* mark this address range */
dvander@27
  2134
          while (sweepsize-->0) {
dvander@27
  2135
            map[addr/8] |= (unsigned char)(1 << (addr % 8));
dvander@27
  2136
            addr++;
dvander@27
  2137
          } /* while */
dvander@27
  2138
        } /* for */
dvander@27
  2139
        /* go over it again, clearing any ranges that have conflicts */
dvander@27
  2140
        for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
dvander@27
  2141
          if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
dvander@27
  2142
            continue;   /* hierarchical type, or no states, or same as this variable */
dvander@27
  2143
          if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
dvander@27
  2144
            continue;   /* a function or a constant */
dvander@27
  2145
          if ((sweep->usage & uDEFINE)==0)
dvander@27
  2146
            continue;   /* undefined variable, ignore */
dvander@27
  2147
          if (fsa!=state_getfsa(sweep->states->next->index))
dvander@27
  2148
            continue;   /* wrong automaton */
dvander@27
  2149
          /* when arrived here, this is a global variable, with states and
dvander@27
  2150
           * belonging to the same automaton as the variable we are declaring
dvander@27
  2151
           */
dvander@27
  2152
          /* if the lists of states of the existing variable and the new
dvander@27
  2153
           * variable have a non-empty intersection, this is not a suitable
dvander@27
  2154
           * overlap point -> wipe the address range
dvander@27
  2155
           */
dvander@27
  2156
          if (state_conflict_id(sc_curstates,sweep->states->next->index)) {
dvander@27
  2157
            sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
dvander@27
  2158
            assert(sweep->addr % sizeof(cell) == 0);
dvander@27
  2159
            addr=sweep->addr/sizeof(cell);
dvander@27
  2160
            /* mark this address range */
dvander@27
  2161
            while (sweepsize-->0) {
dvander@27
  2162
              map[addr/8] &= (unsigned char)(~(1 << (addr % 8)));
dvander@27
  2163
              addr++;
dvander@27
  2164
            } /* while */
dvander@27
  2165
          } /* if */
dvander@27
  2166
        } /* for */
dvander@27
  2167
        /* now walk through the map and find a starting point that is big enough */
dvander@27
  2168
        sweepsize=0;
dvander@27
  2169
        for (addr=0; addr<glb_declared; addr++) {
dvander@27
  2170
          if ((map[addr/8] & (1 << (addr % 8)))==0)
dvander@27
  2171
            continue;
dvander@27
  2172
          for (sweepsize=addr+1; sweepsize<glb_declared; sweepsize++) {
dvander@27
  2173
            if ((map[sweepsize/8] & (1 << (sweepsize % 8)))==0)
dvander@27
  2174
              break;    /* zero bit found, skip this range */
dvander@27
  2175
            if (sweepsize-addr>=size)
dvander@27
  2176
              break;    /* fitting range found, no need to search further */
dvander@27
  2177
          } /* for */
dvander@27
  2178
          if (sweepsize-addr>=size)
dvander@27
  2179
            break;      /* fitting range found, no need to search further */
dvander@27
  2180
          addr=sweepsize;
dvander@27
  2181
        } /* for */
dvander@27
  2182
        free(map);
dvander@27
  2183
        if (sweepsize-addr>=size) {
dvander@27
  2184
          address=sizeof(cell)*addr;    /* fitting range found, set it */
dvander@27
  2185
          glb_incr=0;
dvander@27
  2186
        } /* if */
dvander@27
  2187
      } /* if */
dvander@27
  2188
    } else {
dvander@27
  2189
      address=sizeof(cell)*glb_declared;
dvander@27
  2190
      glb_incr=(int)size;
dvander@27
  2191
    } /* if */
dvander@1046
  2192
    if (size != CELL_MAX && address==sizeof(cell)*glb_declared) {
dvander@27
  2193
      dumplits();       /* dump the literal queue */
dvander@27
  2194
      dumpzero((int)size-litidx);
dvander@27
  2195
    } /* if */
dvander@27
  2196
    litidx=0;
dvander@27
  2197
    if (sym==NULL) {    /* define only if not yet defined */
dvander@286
  2198
      sym=addvariable2(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag,slength);
dvander@27
  2199
      if (sc_curstates>0)
dvander@27
  2200
        attachstatelist(sym,sc_curstates);
dvander@27
  2201
    } else {            /* if declared but not yet defined, adjust the variable's address */
dvander@27
  2202
      assert(sym->states==NULL && sc_curstates==0
dvander@27
  2203
             || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL);
dvander@27
  2204
      sym->addr=address;
dvander@27
  2205
      sym->codeaddr=code_idx;
dvander@27
  2206
      sym->usage|=uDEFINE;
dvander@27
  2207
    } /* if */
dvander@27
  2208
    assert(sym!=NULL);
dvander@27
  2209
    sc_curstates=0;
dvander@27
  2210
    if (ispublic)
dvander@127
  2211
      sym->usage|=uPUBLIC|uREAD;
dvander@27
  2212
    if (fconst)
dvander@27
  2213
      sym->usage|=uCONST;
dvander@27
  2214
    if (fstock)
dvander@27
  2215
      sym->usage|=uSTOCK;
dvander@27
  2216
    if (fstatic)
dvander@27
  2217
      sym->fnumber=filenum;
dvander@27
  2218
    sc_attachdocumentation(sym);/* attach any documenation to the variable */
dvander@27
  2219
    if (sc_status==statSKIP) {
dvander@27
  2220
      sc_status=statWRITE;
dvander@27
  2221
      code_idx=cidx;
dvander@27
  2222
      assert(glb_declared==glbdecl);
dvander@27
  2223
    } else {
dvander@27
  2224
      glb_declared+=glb_incr;   /* add total number of cells (if added to the end) */
dvander@27
  2225
    } /* if */
dvander@27
  2226
  } while (matchtoken(',')); /* enddo */   /* more? */
dvander@27
  2227
  needtoken(tTERM);    /* if not comma, must be semicolumn */
dvander@27
  2228
}
dvander@27
  2229
dvander@27
  2230
/*  declloc     - declare local symbols
dvander@27
  2231
 *
dvander@27
  2232
 *  Declare local (automatic) variables. Since these variables are relative
dvander@27
  2233
 *  to the STACK, there is no switch to the DATA segment. These variables
dvander@27
  2234
 *  cannot be initialized either.
dvander@27
  2235
 *
dvander@27
  2236
 *  global references: declared   (altered)
dvander@27
  2237
 *                     funcstatus (referred to only)
dvander@27
  2238
 */
dvander@27
  2239
static int declloc(int fstatic)
dvander@27
  2240
{
dvander@27
  2241
  int ident,tag;
dvander@27
  2242
  int idxtag[sDIMEN_MAX];
dvander@27
  2243
  char name[sNAMEMAX+1];
dvander@27
  2244
  symbol *sym;
dvander@152
  2245
  constvalue *enumroot=NULL;
dvander@27
  2246
  cell val,size;
dvander@27
  2247
  char *str;
dvander@27
  2248
  value lval = {0};
dvander@27
  2249
  int cur_lit=0;
dvander@27
  2250
  int dim[sDIMEN_MAX];
dvander@27
  2251
  int numdim;
dvander@27
  2252
  int fconst;
dvander@27
  2253
  int staging_start;
dvander@286
  2254
  int slength = 0;
dvander@27
  2255
dvander@27
  2256
  fconst=matchtoken(tCONST);
dvander@27
  2257
  do {
dvander@27
  2258
    ident=iVARIABLE;
dvander@27
  2259
    size=1;
dvander@510
  2260
    slength=0;
dvander@27
  2261
    numdim=0;                           /* no dimensions */
dvander@27
  2262
    tag=pc_addtag(NULL);
dvander@27
  2263
    if (lex(&val,&str)!=tSYMBOL)        /* read in (new) token */
dvander@27
  2264
      error(20,str);                    /* invalid symbol name */
dvander@27
  2265
    assert(strlen(str)<=sNAMEMAX);
dvander@27
  2266
    strcpy(name,str);                   /* save symbol name */
dvander@27
  2267
    if (name[0]==PUBLIC_CHAR)
dvander@27
  2268
      error(56,name);                   /* local variables cannot be public */
dvander@27
  2269
    /* Note: block locals may be named identical to locals at higher
dvander@27
  2270
     * compound blocks (as with standard C); so we must check (and add)
dvander@27
  2271
     * the "nesting level" of local variables to verify the
dvander@27
  2272
     * multi-definition of symbols.
dvander@27
  2273
     */
dvander@27
  2274
    if ((sym=findloc(name))!=NULL && sym->compound==nestlevel)
dvander@27
  2275
      error(21,name);                   /* symbol already defined */
dvander@27
  2276
    /* Although valid, a local variable whose name is equal to that
dvander@27
  2277
     * of a global variable or to that of a local variable at a lower
dvander@27
  2278
     * level might indicate a bug.
dvander@27
  2279
     */
dvander@27
  2280
    if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL)
dvander@27
  2281
      error(219,name);                  /* variable shadows another symbol */
dvander@118
  2282
	if (matchtoken('[')) {
dvander@152
  2283
      int _index;
dvander@152
  2284
      cell _code;
dvander@152
  2285
      int dim_ident;
dvander@152
  2286
      symbol *dim_sym;
dvander@152
  2287
      value dim_val;
dvander@152
  2288
      int all_constant = 1;
dvander@152
  2289
      int _staging = staging;
dvander@152
  2290
dvander@152
  2291
      if (!_staging)
dvander@152
  2292
        stgset(TRUE);
dvander@152
  2293
      stgget(&_index, &_code);
dvander@152
  2294
dvander@118
  2295
      do {
dvander@118
  2296
        if (numdim == sDIMEN_MAX) {
dvander@118
  2297
          error(53);                      /* exceeding maximum number of dimensions */
dvander@152
  2298
          return (all_constant ? iARRAY : iREFARRAY);
dvander@200
  2299
        } else if (numdim) { /* if */
dvander@200
  2300
          /* If we have a dimension on the stack, push it */
dvander@200
  2301
          pushreg(sPRI);
dvander@200
  2302
        }
dvander@152
  2303
        if (matchtoken(']')) {
dvander@534
  2304
          idxtag[numdim] = 0;
dvander@534
  2305
          dim[numdim] = 0;
dvander@534
  2306
		  numdim++;
dvander@152
  2307
          continue;
dvander@152
  2308
        }
dvander@152
  2309
        dim_ident = doexpr2(TRUE,FALSE,FALSE,FALSE,&idxtag[numdim],&dim_sym,0,&dim_val);
dvander@152
  2310
        if (dim_ident == iVARIABLE || dim_ident == iEXPRESSION || dim_ident == iARRAYCELL) {
dvander@152
  2311
          all_constant = 0;
dvander@118
  2312
        } else if (dim_ident == iCONSTEXPR) {
dvander@152
  2313
          dim[numdim] = dim_val.constval;
dvander@152
  2314
		  /* :TODO: :URGENT: Make sure this still works */
dvander@152
  2315
          if (dim_sym && dim_sym->usage & uENUMROOT)
dvander@152
  2316
            enumroot = dim_sym->dim.enumlist;
dvander@152
  2317
		  idxtag[numdim] = dim_sym ? dim_sym->tag : 0;
dvander@152
  2318
          #if INT_MAX < LONG_MAX
dvander@152
  2319
		    if (dim[numdim] > INT_MAX)
ds@841
  2320
			  error(125);                   /* overflow, exceeding capacity */
dvander@152
  2321
          #endif
dvander@118
  2322
        } else {
dvander@152
  2323
          error(29); /* invalid expression, assumed 0 */
dvander@118
  2324
        }
dvander@119
  2325
        numdim++;
dvander@152
  2326
		needtoken(']');
dvander@152
  2327
      } while (matchtoken('['));
dvander@152
  2328
      if (all_constant) {
dvander@152
  2329
        /* Change the last dimension to be based on chars instead if we have a string */
dvander@286
  2330
        if (tag == pc_tag_string && numdim && dim[numdim-1]) {
dvander@286
  2331
          slength = dim[numdim-1];
dvander@153
  2332
          dim[numdim-1] = (dim[numdim-1] + sizeof(cell)-1) / sizeof(cell);
dvander@286
  2333
        }
dvander@152
  2334
        /* Scrap the code generated */
dvander@152
  2335
        ident = iARRAY;
dvander@152
  2336
        stgdel(_index, _code);
dvander@152
  2337
      } else {
dvander@200
  2338
        if (tag == pc_tag_string && numdim && dim[numdim-1]) {
dvander@200
  2339
          stradjust(sPRI);
dvander@200
  2340
        }
dvander@200
  2341
        pushreg(sPRI);
dvander@152
  2342
        memset(dim, 0, sizeof(int)*sDIMEN_MAX);
dvander@152
  2343
        ident = iREFARRAY;
dvander@152
  2344
        genarray(numdim, autozero);
dvander@152
  2345
      }
dvander@152
  2346
      stgout(_index);
dvander@152
  2347
      if (!_staging)
dvander@152
  2348
        stgset(FALSE);
dvander@118
  2349
    }
dvander@27
  2350
    if (getstates(name))
dvander@27
  2351
      error(88,name);           /* local variables may not have states */
dvander@27
  2352
    if (ident==iARRAY || fstatic) {
dvander@27
  2353
      if (sc_alignnext) {
dvander@27
  2354
        aligndata(sc_dataalign);
dvander@27
  2355
        sc_alignnext=FALSE;
dvander@27
  2356
      } /* if */
dvander@27
  2357
      cur_lit=litidx;           /* save current index in the literal table */
dvander@510
  2358
      if (numdim && !dim[numdim-1])
dvander@510
  2359
        size = 0;
dvander@27
  2360
      initials(ident,tag,&size,dim,numdim,enumroot);
dvander@510
  2361
      if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
dvander@510
  2362
        slength = glbstringread;
dvander@510
  2363
      }
dvander@27
  2364
      if (size==0)
dvander@27
  2365
        return ident;           /* error message already given */
dvander@27
  2366
      if (numdim==1)
dvander@27
  2367
        dim[0]=(int)size;
dvander@118
  2368
    }
dvander@27
  2369
    /* reserve memory (on the stack) for the variable */
dvander@27
  2370
    if (fstatic) {
dvander@27
  2371
      /* write zeros for uninitialized fields */
dvander@27
  2372
      while (litidx<cur_lit+size)
dvander@27
  2373
        litadd(0);
dvander@286
  2374
      sym=addvariable2(name,(cur_lit+glb_declared)*sizeof(cell),ident,sSTATIC,
dvander@286
  2375
                      tag,dim,numdim,idxtag,slength);
dvander@118
  2376
    } else if (ident!=iREFARRAY) {
dvander@27
  2377
      declared+=(int)size;      /* variables are put on stack, adjust "declared" */
dvander@286
  2378
      sym=addvariable2(name,-declared*sizeof(cell),ident,sLOCAL,tag,
dvander@286
  2379
                      dim,numdim,idxtag,slength);
dvander@27
  2380
      if (ident==iVARIABLE) {
dvander@27
  2381
        assert(!staging);
dvander@27
  2382
        stgset(TRUE);           /* start stage-buffering */
dvander@27
  2383
        assert(stgidx==0);
dvander@27
  2384
        staging_start=stgidx;
dvander@27
  2385
      } /* if */
dvander@27
  2386
      markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
dvander@27
  2387
      modstk(-(int)size*sizeof(cell));
dvander@117
  2388
      markstack(MEMUSE_STATIC, size);
dvander@27
  2389
      assert(curfunc!=NULL);
dvander@27
  2390
      assert((curfunc->usage & uNATIVE)==0);
dvander@27
  2391
      if (curfunc->x.stacksize<declared+1)
dvander@27
  2392
        curfunc->x.stacksize=declared+1;  /* +1 for PROC opcode */
dvander@118
  2393
    } else if (ident==iREFARRAY) {
dvander@118
  2394
      declared+=1;	/* one cell for address */
dvander@118
  2395
      sym=addvariable(name,-declared*sizeof(cell),ident,sLOCAL,tag,dim,numdim,idxtag);
dvander@118
  2396
      //markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
dvander@118
  2397
      /* genarray() pushes the address onto the stack, so we don't need to call modstk() here! */
dvander@118
  2398
      markheap(MEMUSE_DYNAMIC, 0);
dvander@119
  2399
	  markstack(MEMUSE_STATIC, 1);
dvander@118
  2400
      assert(curfunc != NULL && ((curfunc->usage & uNATIVE) == 0));
dvander@118
  2401
      if (curfunc->x.stacksize<declared+1)
dvander@118
  2402
        curfunc->x.stacksize=declared+1;  /* +1 for PROC opcode */
dvander@27
  2403
    } /* if */
dvander@27
  2404
    /* now that we have reserved memory for the variable, we can proceed
dvander@27
  2405
     * to initialize it */
dvander@27
  2406
    assert(sym!=NULL);          /* we declared it, it must be there */
dvander@27
  2407
    sym->compound=nestlevel;    /* for multiple declaration/shadowing check */
dvander@27
  2408
    if (fconst)
dvander@27
  2409
      sym->usage|=uCONST;
dvander@27
  2410
    if (!fstatic) {             /* static variables already initialized */
dvander@27
  2411
      if (ident==iVARIABLE) {
dvander@27
  2412
        /* simple variable, also supports initialization */
dvander@27
  2413
        int ctag = tag;         /* set to "tag" by default */
dvander@27
  2414
        int explicit_init=FALSE;/* is the variable explicitly initialized? */
dvander@151
  2415
        int cident=ident;
dvander@27
  2416
        if (matchtoken('=')) {
dvander@27
  2417
          if (!autozero)
dvander@27
  2418
            error(10);
dvander@151
  2419
          cident=doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE);
dvander@27
  2420
          explicit_init=TRUE;
dvander@27
  2421
        } else {
dvander@27
  2422
          if (autozero)
dvander@27
  2423
            ldconst(0,sPRI);      /* uninitialized variable, set to zero */
dvander@27
  2424
        } /* if */
dvander@27
  2425
        if (autozero) {
dvander@27
  2426
          /* now try to save the value (still in PRI) in the variable */
dvander@27
  2427
          lval.sym=sym;
dvander@27
  2428
          lval.ident=iVARIABLE;
dvander@27
  2429
          lval.constval=0;
dvander@27
  2430
          lval.tag=tag;
dvander@27
  2431
          check_userop(NULL,ctag,lval.tag,2,NULL,&ctag);
dvander@27
  2432
          store(&lval);
dvander@27
  2433
          markexpr(sEXPR,NULL,0); /* full expression ends after the store */
dvander@27
  2434
        }
dvander@27
  2435
        assert(staging);        /* end staging phase (optimize expression) */
dvander@27
  2436
        stgout(staging_start);
dvander@27
  2437
        stgset(FALSE);
dvander@151
  2438
        if (!matchtag_string(cident, ctag) && !matchtag(tag,ctag,TRUE))
ds@844
  2439
		{
ds@844
  2440
		  if (tag & FUNCTAG)
ds@844
  2441
		    error(100);      /* error - function prototypes do not match */
ds@844
  2442
		  else
ds@844
  2443
		    error(213);      /* warning - tag mismatch */
ds@844
  2444
		}
dvander@27
  2445
        /* if the variable was not explicitly initialized, reset the
dvander@27
  2446
         * "uWRITTEN" flag that store() set */
dvander@27
  2447
        if (!explicit_init)
dvander@27
  2448
          sym->usage &= ~uWRITTEN;
dvander@118
  2449
      } else if (ident!=iREFARRAY) {
dvander@27
  2450
        /* an array */
dvander@27
  2451
        assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax);
dvander@27
  2452
        assert(size>0 && size>=sym->dim.array.length);
dvander@27
  2453
        assert(numdim>1 || size==sym->dim.array.length);
dvander@27
  2454
        if (autozero) {
dvander@27
  2455
          /* final literal values that are zero make no sense to put in the literal
dvander@27
  2456
           * pool, because values get zero-initialized anyway; we check for this,
dvander@27
  2457
           * because users often explicitly initialize strings to ""
dvander@27
  2458
           */
dvander@27
  2459
          while (litidx>cur_lit && litq[litidx-1]==0)
dvander@27
  2460
            litidx--;
dvander@27
  2461
          /* if the array is not completely filled, set all values to zero first */
dvander@27
  2462
          if (litidx-cur_lit<size && (ucell)size<CELL_MAX)
dvander@27
  2463
            fillarray(sym,size*sizeof(cell),0);
dvander@27
  2464
        }
dvander@27
  2465
        if (cur_lit<litidx) {
dvander@27
  2466
          /* check whether the complete array is set to a single value; if
dvander@27
  2467
           * it is, more compact code can be generated */
dvander@27
  2468
          cell first=litq[cur_lit];
dvander@27
  2469
          int i;
dvander@27
  2470
          for (i=cur_lit; i<litidx && litq[i]==first; i++)
dvander@27
  2471
            /* nothing */;
dvander@27
  2472
          if (i==litidx) {
dvander@27
  2473
            /* all values are the same */
dvander@27
  2474
            fillarray(sym,(litidx-cur_lit)*sizeof(cell),first);
dvander@27
  2475
            litidx=cur_lit;     /* reset literal table */
dvander@27
  2476
          } else {
dvander@27
  2477
            /* copy the literals to the array */
dvander@27
  2478
            ldconst((cur_lit+glb_declared)*sizeof(cell),sPRI);
dvander@27
  2479
            copyarray(sym,(litidx-cur_lit)*sizeof(cell));
dvander@27
  2480
          } /* if */
dvander@27
  2481
        } /* if */
dvander@27
  2482
      } /* if */
dvander@27
  2483
    } /* if */
dvander@27
  2484
  } while (matchtoken(',')); /* enddo */   /* more? */
dvander@27
  2485
  needtoken(tTERM);    /* if not comma, must be semicolumn */
dvander@27
  2486
  return ident;
dvander@27
  2487
}
dvander@27
  2488
dvander@27
  2489
/* this function returns the maximum value for a cell in case of an error
dvander@27
  2490
 * (invalid dimension).
dvander@27
  2491
 */
dvander@27
  2492
static cell calc_arraysize(int dim[],int numdim,int cur)
dvander@27
  2493
{
dvander@27
  2494
  cell subsize;
dvander@27
  2495
  ucell newsize;
dvander@27
  2496
dvander@27
  2497
  /* the return value is in cells, not bytes */
dvander@27
  2498
  assert(cur>=0 && cur<=numdim);
dvander@27
  2499
  if (cur==numdim)
dvander@27
  2500
    return 0;
dvander@27
  2501
  subsize=calc_arraysize(dim,numdim,cur+1);
dvander@27
  2502
  newsize=dim[cur]+dim[cur]*subsize;
dvander@842
  2503
  if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize*sizeof(cell)>=CELL_MAX)
dvander@27
  2504
    return CELL_MAX;
dvander@27
  2505
  return newsize;
dvander@27
  2506
}
dvander@27
  2507
dvander@1472
  2508
static cell gen_indirection_vecs(array_info_t *ar, int dim, cell cur_offs)
dvander@1472
  2509
{
dvander@1472
  2510
	int i;
dvander@1472
  2511
	cell write_offs = cur_offs;
dvander@1472
  2512
	cell *data_offs = ar->data_offs;
dvander@1472
  2513
dvander@1472
  2514
	cur_offs += ar->dim_list[dim];
dvander@1472
  2515
dvander@1472
  2516
	/**
dvander@1472
  2517
	 * Dimension n-x where x > 2 will have sub-vectors.  
dvander@1472
  2518
	 * Otherwise, we just need to reference the data section.
dvander@1472
  2519
	 */
dvander@1472
  2520
	if (ar->dim_count > 2 && dim < ar->dim_count - 2)
dvander@1472
  2521
	{
dvander@1472
  2522
		/**
dvander@1472
  2523
		 * For each index at this dimension, write offstes to our sub-vectors.
dvander@1472
  2524
		 * After we write one sub-vector, we generate its sub-vectors recursively.
dvander@1472
  2525
		 * At the end, we're given the next offset we can use.
dvander@1472
  2526
		 */
dvander@1472
  2527
		for (i = 0; i < ar->dim_list[dim]; i++)
dvander@1472
  2528
		{
dvander@1472
  2529
			ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell);
dvander@1472
  2530
			write_offs++;
dvander@1472
  2531
			ar->cur_dims[dim] = i;
dvander@1472
  2532
			cur_offs = gen_indirection_vecs(ar, dim + 1, cur_offs);
dvander@1472
  2533
		}
dvander@1472
  2534
	} else if (ar->dim_count > 1) {
dvander@1472
  2535
		/**
dvander@1472
  2536
		 * In this section, there are no sub-vectors, we need to write offsets 
dvander@1472
  2537
		 * to the data.  This is separate so the data stays in one big chunk.
dvander@1472
  2538
		 * The data offset will increment by the size of the last dimension, 
dvander@1472
  2539
		 * because that is where the data is finally computed as.  But the last 
dvander@1472
  2540
		 * dimension can be of variable size, so we have to detect that.
dvander@1472
  2541
		 */
dvander@1472
  2542
		if (ar->dim_list[dim + 1] == 0)
dvander@1472
  2543
		{
dvander@1472
  2544
			int vec_start = 0;
dvander@1472
  2545
dvander@1472
  2546
			/**
dvander@1472
  2547
			 * Using the precalculated offsets, compute an index into the last 
dvander@1472
  2548
			 * dimension array.
dvander@1472
  2549
			 */
dvander@1472
  2550
			for (i = 0; i < dim; i++)
dvander@1472
  2551
			{
dvander@1472
  2552
				vec_start += ar->cur_dims[i] * ar->dim_offs_precalc[i];
dvander@1472
  2553
			}
dvander@1472
  2554
dvander@1472
  2555
			/**
dvander@1472
  2556
			 * Now, vec_start points to a vector of last dimension offsets for 
dvander@1472
  2557
			 * the preceding dimension combination(s).
dvander@1472
  2558
			 * I.e. (1,2,i,j) in [3][4][5][] will be:
dvander@1472
  2559
			 *  j = 1*(4*5) + 2*(5) + i, and the parenthetical expressions are 
dvander@1472
  2560
			 * precalculated for us so we can easily generalize here.
dvander@1472
  2561
			 */
dvander@1472
  2562
			for (i = 0; i < ar->dim_list[dim]; i++)
dvander@1472
  2563
			{
dvander@1472
  2564
				ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell);
dvander@1472
  2565
				write_offs++;
dvander@1472
  2566
				*data_offs = *data_offs + ar->lastdim_list[vec_start + i];
dvander@1472
  2567
			}
dvander@1472
  2568
		} else {
dvander@1472
  2569
			/**
dvander@1472
  2570
			 * The last dimension size is constant.  There's no extra work to 
dvander@1472
  2571
			 * compute the last dimension size.
dvander@1472
  2572
			 */
dvander@1472
  2573
			for (i = 0; i < ar->dim_list[dim]; i++)
dvander@1472
  2574
			{
dvander@1472
  2575
				ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell);
dvander@1472
  2576
				write_offs++;
dvander@1472
  2577
				*data_offs = *data_offs + ar->dim_list[dim + 1];
dvander@1472
  2578
			}
dvander@1472
  2579
		}
dvander@1472
  2580
	}
dvander@1472
  2581
dvander@1472
  2582
	return cur_offs;
dvander@1472
  2583
}
dvander@1472
  2584
dvander@1472
  2585
static cell calc_indirection(const int dim_list[], int dim_count, int dim)
dvander@1472
  2586
{
dvander@1472
  2587
	cell size = dim_list[dim];
dvander@1472
  2588
dvander@1472
  2589
	if (dim < dim_count - 2)
dvander@1472
  2590
	{
dvander@1472
  2591
		size += dim_list[dim] * calc_indirection(dim_list, dim_count, dim + 1);
dvander@1472
  2592
	}
dvander@1472
  2593
dvander@1472
  2594
	return size;
dvander@1472
  2595
}
dvander@1472
  2596
dvander@1472
  2597
static void adjust_indirectiontables(int dim[],int numdim,int cur,cell increment,
dvander@27
  2598
                                     int startlit,constvalue *lastdim,int *skipdim)
dvander@27
  2599
{
dvander@1472
  2600
	/* Find how many cells the indirection table will be */
dvander@1472
  2601
	cell tbl_size;
dvander@1472
  2602
	int *dyn_list = NULL;
dvander@1472
  2603
	int cur_dims[sDIMEN_MAX];
dvander@1472
  2604
	cell dim_offset_precalc[sDIMEN_MAX];
dvander@1472
  2605
	array_info_t ar;
dvander@1472
  2606
dvander@1472
  2607
	if (numdim == 1)
dvander@1472
  2608
	{
dvander@1472
  2609
		return;
dvander@1472
  2610
	}
dvander@1472
  2611
dvander@1472
  2612
	tbl_size = calc_indirection(dim, numdim, 0);
dvander@1472
  2613
	memset(cur_dims, 0, sizeof(cur_dims));
dvander@1472
  2614
dvander@1472
  2615
	/**
dvander@1472
  2616
	 * Flatten the last dimension array list -- this makes 
dvander@1472
  2617
	 * things MUCH easier in the indirection calculator.
dvander@1472
  2618
	 */
dvander@1472
  2619
	if (lastdim)
dvander@1472
  2620
	{
dvander@1472
  2621
		int i;
dvander@1472
  2622
		constvalue *ld = lastdim->next;
dvander@1472
  2623
dvander@1472
  2624
		/* Get the total number of last dimensions. */
dvander@1472
  2625
		for (i = 0; ld != NULL; i++, ld = ld->next)
dvander@1472
  2626
		{
dvander@1472
  2627
			/* Nothing */
dvander@1472
  2628
		}
dvander@1472
  2629
		/* Store them in an array instead of a linked list. */
dvander@1472
  2630
		dyn_list = (int *)malloc(sizeof(int) * i);
dvander@1472
  2631
		for (i = 0, ld = lastdim->next;
dvander@1472
  2632
			 ld != NULL;
dvander@1472
  2633
			 i++, ld = ld->next)
dvander@1472
  2634
		{
dvander@1472
  2635
			dyn_list[i] = ld->value;
dvander@1472
  2636
		}
dvander@1472
  2637
dvander@1472
  2638
		/**
dvander@1472
  2639
		 * Pre-calculate all of the offsets.  This speeds up and simplifies 
dvander@1472
  2640
		 * the indirection process.  For example, if we have an array like:
dvander@1472
  2641
		 * [a][b][c][d][], and given (A,B,C), we want to find the size of 
dvander@1472
  2642
		 * the last dimension [A][B][C][i], we must do:
dvander@1472
  2643
		 *
dvander@1472
  2644
		 * list[A*(b*c*d) + B*(c*d) + C*(d) + i]
dvander@1472
  2645
		 *
dvander@1472
  2646
		 * Generalizing this algorithm in the indirection process is expensive, 
dvander@1472
  2647
		 * so we lessen the need for nested loops by pre-computing the parts:
dvander@1472
  2648
		 * (b*c*d), (c*d), and (d).
dvander@1472
  2649
		 *
dvander@1472
  2650
		 * In other words, finding the offset to dimension N at index I is 
dvander@1472
  2651
		 * I * (S[N+1] * S[N+2] ... S[N+n-1]) where S[] is the size of dimension
dvander@1472
  2652
		 * function, and n is the index of the last dimension.
dvander@1472
  2653
		 */
dvander@1472
  2654
		for (i = 0; i < numdim - 1; i++)
dvander@1472
  2655
		{
dvander@1472
  2656
			int j;
dvander@1472
  2657
dvander@1472
  2658
			dim_offset_precalc[i] = 1;
dvander@1472
  2659
			for (j = i + 1; j < numdim - 1; j++)
dvander@1472
  2660
			{
dvander@1472
  2661
				dim_offset_precalc[i] *= dim[j];
dvander@1472
  2662
			}
dvander@1472
  2663
		}
dvander@1472
  2664
dvander@1472
  2665
		ar.dim_offs_precalc = dim_offset_precalc;
dvander@1472
  2666
		ar.lastdim_list = dyn_list;
dvander@1472
  2667
	} else {
dvander@1472
  2668
		ar.dim_offs_precalc = NULL;
dvander@1472
  2669
		ar.lastdim_list = NULL;
dvander@1472
  2670
	}
dvander@1472
  2671
dvander@1472
  2672
	ar.base = &litq[startlit];
dvander@1472
  2673
	ar.data_offs = &tbl_size;
dvander@1472
  2674
	ar.dim_list = dim;
dvander@1472
  2675
	ar.dim_count = numdim;
dvander@1472
  2676
	ar.cur_dims = cur_dims;
dvander@1472
  2677
dvander@1472
  2678
	gen_indirection_vecs(&ar, 0, 0);
dvander@1472
  2679
dvander@1472
  2680
	free(dyn_list);
dvander@27
  2681
}
dvander@27
  2682
dvander@27
  2683
/*  initials
dvander@27
  2684
 *
dvander@27
  2685
 *  Initialize global objects and local arrays.
dvander@27
  2686
 *    size==array cells (count), if 0 on input, the routine counts the number of elements
dvander@27
  2687
 *    tag==required tagname id (not the returned tag)
dvander@27
  2688
 *
dvander@27
  2689
 *  Global references: litidx (altered)
dvander@27
  2690
 */
dvander@1044
  2691
static void initials2(int ident,int tag,cell *size,int dim[],int numdim,
dvander@1045
  2692
                     constvalue *enumroot, int eq_match_override, int curlit_override)
dvander@27
  2693
{
dvander@27
  2694
  int ctag;
dvander@27
  2695
  cell tablesize;
dvander@1045
  2696
  int curlit=(curlit_override == -1) ? litidx : curlit_override;
dvander@27
  2697
  int err=0;
dvander@27
  2698
dvander@1044
  2699
  if (eq_match_override == -1) {
dvander@1044
  2700
    eq_match_override = matchtoken('=');
dvander@1044
  2701
  }
dvander@1044
  2702
dvander@1044
  2703
  if (!eq_match_override) {
dvander@27
  2704
    assert(ident!=iARRAY || numdim>0);
dvander@27
  2705
    if (ident==iARRAY && dim[numdim-1]==0) {
dvander@27
  2706
      /* declared as "myvar[];" which is senseless (note: this *does* make
dvander@27
  2707
       * sense in the case of a iREFARRAY, which is a function parameter)
dvander@27
  2708
       */
dvander@27
  2709
      error(9);         /* array has zero length -> invalid size */
dvander@27
  2710
    } /* if */
dvander@27
  2711
    if (ident==iARRAY) {
dvander@27
  2712
      assert(numdim>0 && numdim<=sDIMEN_MAX);
dvander@27
  2713
      *size=calc_arraysize(dim,numdim,0);
dvander@27
  2714
      if (*size==(cell)CELL_MAX) {
dvander@27
  2715
        error(9);       /* array is too big -> invalid size */
dvander@27
  2716
        return;
dvander@27
  2717
      } /* if */
dvander@27
  2718
      /* first reserve space for the indirection vectors of the array, then
dvander@27
  2719
       * adjust it to contain the proper values
dvander@27
  2720
       * (do not use dumpzero(), as it bypasses the literal queue)
dvander@27
  2721
       */
dvander@27
  2722
      for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
dvander@27
  2723
        litadd(0);
dvander@27
  2724
      if (dim[numdim-1]!=0)     /* error 9 has already been given */
dvander@27
  2725
        adjust_indirectiontables(dim,numdim,0,0,curlit,NULL,NULL);
dvander@27
  2726
    } /* if */
dvander@27
  2727
    return;
dvander@27
  2728
  } /* if */
dvander@27
  2729
dvander@27
  2730
  if (ident==iVARIABLE) {
dvander@27
  2731
    assert(*size==1);
dvander@27
  2732
    init(ident,&ctag,NULL);
dvander@27
  2733
    if (!matchtag(tag,ctag,TRUE))
ds@844
  2734
	{
ds@844
  2735
	  if (tag & FUNCTAG)
ds@844
  2736
	    error(100);     /* error - function prototypes do not match */
ds@844
  2737
	  else
ds@844
  2738
        error(213);     /* warning - tag mismatch */
ds@844
  2739
	}
dvander@27
  2740
  } else {
dvander@27
  2741
    assert(numdim>0);
dvander@27
  2742
    if (numdim==1) {
dvander@27
  2743
      *size=initvector(ident,tag,dim[0],FALSE,enumroot,NULL);
dvander@27
  2744
    } else {
dvander@27
  2745
      int errorfound=FALSE;
dvander@27
  2746
      int counteddim[sDIMEN_MAX];
dvander@27
  2747
      int idx;
dvander@27
  2748
      constvalue lastdim={NULL,"",0,0};     /* sizes of the final dimension */
dvander@27
  2749
      int skipdim=0;
dvander@27
  2750
dvander@27
  2751
      if (dim[numdim-1]!=0)
dvander@27
  2752
        *size=calc_arraysize(dim,numdim,0); /* calc. full size, if known */
dvander@27
  2753
      /* already reserve space for the indirection tables (for an array with
dvander@27
  2754
       * known dimensions)
dvander@27
  2755
       * (do not use dumpzero(), as it bypasses the literal queue)
dvander@27
  2756
       */
dvander@27
  2757
      for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
dvander@27
  2758
        litadd(0);
dvander@27
  2759
      /* now initialize the sub-arrays */
dvander@27
  2760
      memset(counteddim,0,sizeof counteddim);
dvander@27
  2761
      initarray(ident,tag,dim,numdim,0,curlit,counteddim,&lastdim,enumroot,&errorfound);
ds@563
  2762
      /* check the specified array dimensions with the initializer counts */
dvander@27
  2763
      for (idx=0; idx<numdim-1; idx++) {
dvander@27
  2764
        if (dim[idx]==0) {
dvander@27
  2765
          dim[idx]=counteddim[idx];
dvander@27
  2766
        } else if (counteddim[idx]<dim[idx]) {
dvander@27
  2767
          error(52);            /* array is not fully initialized */
dvander@27
  2768
          err++;
dvander@27
  2769
        } else if (counteddim[idx]>dim[idx]) {
dvander@27
  2770
          error(18);            /* initialization data exceeds declared size */
dvander@27
  2771
          err++;
dvander@27
  2772
        } /* if */
dvander@27
  2773
      } /* for */
faluco@62
  2774
      if (numdim>1 && dim[numdim-1]==0) {
faluco@62
  2775
        /* also look whether, by any chance, all "counted" final dimensions are
faluco@62
  2776
         * the same value; if so, we can store this
faluco@62
  2777
         */
faluco@62
  2778
        constvalue *ld=lastdim.next;
faluco@62
  2779
        int d,match;
faluco@62
  2780
        for (d=0; d<dim[numdim-2]; d++) {
faluco@62
  2781
          assert(ld!=NULL);
faluco@62
  2782
          assert(strtol(ld->name,NULL,16)==d);
faluco@62
  2783
          if (d==0)
faluco@62
  2784
            match=ld->value;
faluco@62
  2785
          else if (match!=ld->value)
faluco@62
  2786
            break;
faluco@62
  2787
          ld=ld->next;
faluco@62
  2788
        } /* for */
faluco@62
  2789
        if (d==dim[numdim-2])
faluco@62
  2790
          dim[numdim-1]=match;
faluco@62
  2791
      } /* if */
dvander@27
  2792
      /* after all arrays have been initalized, we know the (major) dimensions
dvander@27
  2793
       * of the array and we can properly adjust the indirection vectors
dvander@27
  2794
       */
dvander@27
  2795
      if (err==0)
dvander@27
  2796
        adjust_indirectiontables(dim,numdim,0,0,curlit,&lastdim,&skipdim);
dvander@27
  2797
      delete_consttable(&lastdim);  /* clear list of minor dimension sizes */
dvander@27
  2798
    } /* if */
dvander@27
  2799
  } /* if */
dvander@27
  2800
dvander@27
  2801
  if (*size==0)
dvander@27
  2802
    *size=litidx-curlit;        /* number of elements defined */
dvander@27
  2803
}
dvander@27
  2804
dvander@1044
  2805
static void initials(int ident,int tag,cell *size,int dim[],int numdim,
dvander@1044
  2806
					 constvalue *enumroot)
dvander@1044
  2807
{
dvander@1045
  2808
  initials2(ident, tag, size, dim, numdim, enumroot, -1, -1);
dvander@1044
  2809
}
dvander@1044
  2810
dvander@27
  2811
static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
dvander@27
  2812
                      int startlit,int counteddim[],constvalue *lastdim,
dvander@27
  2813
                      constvalue *enumroot,int *errorfound)
dvander@27
  2814
{
dvander@27
  2815
  cell dsize,totalsize;
dvander@27
  2816
  int idx,abortparse;
dvander@27
  2817
dvander@27
  2818
  assert(cur>=0 && cur<numdim);
dvander@27
  2819
  assert(startlit>=0);
dvander@27
  2820
  assert(cur+2<=numdim);/* there must be 2 dimensions or more to do */
dvander@27
  2821
  assert(errorfound!=NULL && *errorfound==FALSE);
dvander@27
  2822
  totalsize=0;
dvander@27
  2823
  needtoken('{');
dvander@27
  2824
  for (idx=0,abortparse=FALSE; !abortparse; idx++) {
dvander@1976
  2825
dvander@27
  2826
    /* In case the major dimension is zero, we need to store the offset
dvander@27
  2827
     * to the newly detected sub-array into the indirection table; i.e.
dvander@27
  2828
     * this table needs to be expanded and updated.
dvander@27
  2829
     * In the current design, the indirection vectors for a multi-dimensional
ds@563
  2830
     * array are adjusted after parsing all initializers. Hence, it is only
dvander@27
  2831
     * necessary at this point to reserve space for an extra cell in the
dvander@27
  2832
     * indirection vector.
dvander@27
  2833
     */
dvander@27
  2834
    if (dim[cur]==0) {
dvander@27
  2835
      litinsert(0,startlit);
dvander@27
  2836
    } else if (idx>=dim[cur]) {
dvander@27
  2837
      error(18);            /* initialization data exceeds array size */
dvander@27
  2838
      break;
dvander@27
  2839
    } /* if */
dvander@27
  2840
    if (cur+2<numdim) {
dvander@27
  2841
      dsize=initarray(ident,tag,dim,numdim,cur+1,startlit,counteddim,
dvander@27
  2842
                      lastdim,enumroot,errorfound);
dvander@27
  2843
    } else {
dvander@27
  2844
      dsize=initvector(ident,tag,dim[cur+1],TRUE,enumroot,errorfound);
dvander@27
  2845
      /* The final dimension may be variable length. We need to keep the
dvander@27
  2846
       * lengths of the final dimensions in order to set the indirection
dvander@27
  2847
       * vectors for the next-to-last dimension.
dvander@27
  2848
       */
dvander@27
  2849
      append_constval(lastdim,itoh(idx),dsize,0);
dvander@27
  2850
    } /* if */
dvander@27
  2851
    totalsize+=dsize;
dvander@27
  2852
    if (*errorfound || !matchtoken(','))
dvander@27
  2853
      abortparse=TRUE;
dvander@27
  2854
  } /* for */
dvander@27
  2855
  needtoken('}');
dvander@27
  2856
  assert(counteddim!=NULL);
dvander@27
  2857
  if (counteddim[cur]>0) {
dvander@27
  2858
    if (idx<counteddim[cur])
dvander@27
  2859
      error(52);                /* array is not fully initialized */
dvander@27
  2860
    else if (idx>counteddim[cur])
dvander@27
  2861
      error(18);                /* initialization data exceeds declared size */
dvander@27
  2862
  } /* if */
dvander@27
  2863
  counteddim[cur]=idx;
dvander@27
  2864
dvander@27
  2865
  return totalsize+dim[cur];    /* size of sub-arrays + indirection vector */
dvander@27
  2866
}
dvander@27
  2867
dvander@27
  2868
/*  initvector
dvander@27
  2869
 *  Initialize a single dimensional array
dvander@27
  2870
 */
dvander@27
  2871
static cell initvector(int ident,int tag,cell size,int fillzero,
dvander@27
  2872
                       constvalue *enumroot,int *errorfound)
dvander@27
  2873
{
dvander@27
  2874
  cell prev1=0,prev2=0;
faluco@202
  2875
  int ellips=FALSE;
dvander@27
  2876
  int curlit=litidx;
dvander@27
  2877
  int rtag,ctag;
dvander@27
  2878
dvander@27
  2879
  assert(ident==iARRAY || ident==iREFARRAY);
faluco@202
  2880
  if (matchtoken('{')) {
dvander@27
  2881
    constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL;
dvander@27
  2882
    do {
dvander@27
  2883
      int fieldlit=litidx;
dvander@27
  2884
      int matchbrace,i;
dvander@27
  2885
      if (matchtoken('}')) {    /* to allow for trailing ',' after the initialization */
dvander@27
  2886
        lexpush();
dvander@27
  2887
        break;
dvander@27
  2888
      } /* if */
dvander@27
  2889
      if ((ellips=matchtoken(tELLIPS))!=0)
dvander@27
  2890
        break;
dvander@27
  2891
      /* for enumeration fields, allow another level of braces ("{...}") */
dvander@27
  2892
      matchbrace=0;             /* preset */
dvander@27
  2893
      ellips=0;
dvander@27
  2894
      if (enumfield!=NULL)
dvander@27
  2895
        matchbrace=matchtoken('{');
dvander@27
  2896
      for ( ;; ) {
dvander@27
  2897
        prev2=prev1;
dvander@27
  2898
        prev1=init(ident,&ctag,errorfound);
dvander@27
  2899
        if (!matchbrace)
dvander@27
  2900
          break;
dvander@27
  2901
        if ((ellips=matchtoken(tELLIPS))!=0)
dvander@27
  2902
          break;
dvander@27
  2903
        if (!matchtoken(',')) {
dvander@27
  2904
          needtoken('}');
dvander@27
  2905
          break;
dvander@27
  2906
        } /* for */
dvander@27
  2907
      } /* for */
dvander@27
  2908
      /* if this array is based on an enumeration, fill the "field" up with
dvander@27
  2909
       * zeros, and toggle the tag
dvander@27
  2910
       */
dvander@27
  2911
      if (enumroot!=NULL && enumfield==NULL)
ds@563
  2912
        error(227);             /* more initializers than enum fields */
dvander@27
  2913
      rtag=tag;                 /* preset, may be overridden by enum field tag */
dvander@27
  2914
      if (enumfield!=NULL) {
dvander@27
  2915
        cell step;
dvander@27
  2916
        int cmptag=enumfield->index;
dvander@27
  2917
        symbol *symfield=findconst(enumfield->name,&cmptag);
dvander@27
  2918
        if (cmptag>1)
dvander@27
  2919
          error(91,enumfield->name); /* ambiguous constant, needs tag override */
dvander@27
  2920
        assert(symfield!=NULL);
dvander@27
  2921
        assert(fieldlit<litidx);
dvander@27
  2922
        if (litidx-fieldlit>symfield->dim.array.length)
ds@563
  2923
          error(228);           /* length of initializer exceeds size of the enum field */
dvander@27
  2924
        if (ellips) {
dvander@27
  2925
          step=prev1-prev2;
dvander@27
  2926
        } else {
dvander@27
  2927
          step=0;
dvander@27
  2928
          prev1=0;
dvander@27
  2929
        } /* if */
dvander@27
  2930
        for (i=litidx-fieldlit; i<symfield->dim.array.length; i++) {
dvander@27
  2931
          prev1+=step;
dvander@27
  2932
          litadd(prev1);
dvander@27
  2933
        } /* for */
dvander@27
  2934
        rtag=symfield->x.tags.index;  /* set the expected tag to the index tag */
dvander@27
  2935
        enumfield=enumfield->next;
dvander@27
  2936
      } /* if */
dvander@27
  2937
      if (!matchtag(rtag,ctag,TRUE))
ds@844
  2938
	  {
ds@844
  2939
	    if (rtag & FUNCTAG)
ds@844
  2940
		  error(100);           /* error - function prototypes do not match */
ds@844
  2941
		else
ds@844
  2942
		  error(213);           /* warning - tag mismatch */
ds@844
  2943
	  }
dvander@27
  2944
    } while (matchtoken(',')); /* do */
dvander@27
  2945
    needtoken('}');
dvander@27
  2946
  } else {
dvander@27
  2947
    init(ident,&ctag,errorfound);
dvander@27
  2948
    if (!matchtag(tag,ctag,TRUE))
dvander@27
  2949
      error(213);               /* tagname mismatch */
dvander@27
  2950
  } /* if */
dvander@27
  2951
  /* fill up the literal queue with a series */
dvander@27
  2952
  if (ellips) {
dvander@27
  2953
    cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2;
dvander@27
  2954
    if (size==0 || (litidx-curlit)==0)
dvander@27
  2955
      error(41);                /* invalid ellipsis, array size unknown */
dvander@27
  2956
    else if ((litidx-curlit)==(int)size)
dvander@1965
  2957
      error(18);                /* initialization data exceeds declared size */
dvander@27
  2958
    while ((litidx-curlit)<(int)size) {
dvander@27
  2959
      prev1+=step;
dvander@27
  2960
      litadd(prev1);
dvander@27
  2961
    } /* while */
dvander@27
  2962
  } /* if */
dvander@27
  2963
  if (fillzero && size>0) {
dvander@27
  2964
    while ((litidx-curlit)<(int)size)
dvander@27
  2965
      litadd(0);
dvander@27
  2966
  } /* if */
dvander@27
  2967
  if (size==0) {
dvander@27
  2968
    size=litidx-curlit;                 /* number of elements defined */
dvander@27
  2969
  } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */
dvander@1965
  2970
    error(18);                  /* initialization data exceeds declared size */
dvander@27
  2971
    litidx=(int)size+curlit;    /* avoid overflow in memory moves */
dvander@27
  2972
  } /* if */
dvander@27
  2973
  return size;
dvander@27
  2974
}
dvander@27
  2975
dvander@27
  2976
/*  init
dvander@27
  2977
 *
dvander@27
  2978
 *  Evaluate one initializer.
dvander@27
  2979
 */
dvander@27
  2980
static cell init(int ident,int *tag,int *errorfound)
dvander@27
  2981
{
dvander@27
  2982
  cell i = 0;
dvander@27
  2983
dvander@27
  2984
  if (matchtoken(tSTRING)){
dvander@27
  2985
    /* lex() automatically stores strings in the literal table (and
dvander@27
  2986
     * increases "litidx") */
dvander@27
  2987
    if (ident==iVARIABLE) {
dvander@27
  2988
      error(6);         /* must be assigned to an array */
dvander@27
  2989
      litidx=1;         /* reset literal queue */
dvander@27
  2990
    } /* if */
dvander@151
  2991
    *tag=pc_tag_string;
dvander@27
  2992
  } else if (constexpr(&i,tag,NULL)){
dvander@27
  2993
    litadd(i);          /* store expression result in literal table */
dvander@27
  2994
  } else {
dvander@27
  2995
    if (errorfound!=NULL)
dvander@27
  2996
      *errorfound=TRUE;
dvander@27
  2997
  } /* if */
dvander@27
  2998
  return i;
dvander@27
  2999
}
dvander@27
  3000
dvander@27
  3001
/*  needsub
dvander@27
  3002
 *
dvander@27
  3003
 *  Get required array size
dvander@27
  3004
 */
dvander@27
  3005
static cell needsub(int *tag,constvalue **enumroot)
dvander@27
  3006
{
dvander@27
  3007
  cell val;
dvander@27
  3008
  symbol *sym;
dvander@27
  3009
dvander@27
  3010
  assert(tag!=NULL);
dvander@27
  3011
  *tag=0;
dvander@27
  3012
  if (enumroot!=NULL)
dvander@27
  3013
    *enumroot=NULL;         /* preset */
dvander@27
  3014
  if (matchtoken(']'))      /* we have already seen "[" */
dvander@27
  3015
    return 0;               /* zero size (like "char msg[]") */
dvander@27
  3016
dvander@27
  3017
  constexpr(&val,tag,&sym); /* get value (must be constant expression) */
dvander@27
  3018
  if (val<0) {
dvander@27
  3019
    error(9);               /* negative array size is invalid; assumed zero */
dvander@27
  3020
    val=0;
dvander@27
  3021
  } /* if */
dvander@27
  3022
  needtoken(']');
dvander@27
  3023
dvander@27
  3024
  if (enumroot!=NULL) {
dvander@27
  3025
    /* get the field list for an enumeration */
dvander@27
  3026
    assert(*enumroot==NULL);/* should have been preset */
dvander@27
  3027
    assert(sym==NULL || sym->ident==iCONSTEXPR);
dvander@27
  3028
    if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) {
dvander@27
  3029
      assert(sym->dim.enumlist!=NULL);
dvander@27
  3030
      *enumroot=sym->dim.enumlist;
dvander@27
  3031
    } /* if */
dvander@27
  3032
  } /* if */
dvander@27
  3033
dvander@27
  3034
  return val;               /* return array size */
dvander@27
  3035
}
dvander@27
  3036
dvander@27
  3037
/*  decl_const  - declare a single constant
dvander@27
  3038
 *
dvander@27
  3039
 */
dvander@27
  3040
static void decl_const(int vclass)
dvander@27
  3041
{
dvander@27
  3042
  char constname[sNAMEMAX+1];
dvander@27
  3043
  cell val;
dvander@27
  3044
  char *str;
dvander@27
  3045
  int tag,exprtag;
dvander@27
  3046
  int symbolline;
dvander@27
  3047
  symbol *sym;
dvander@27
  3048
faluco@62
  3049
  insert_docstring_separator();         /* see comment in newfunc() */
faluco@62
  3050
  do {
faluco@62
  3051
    tag=pc_addtag(NULL);
faluco@62
  3052
    if (lex(&val,&str)!=tSYMBOL)        /* read in (new) token */
faluco@62
  3053
      error(20,str);                    /* invalid symbol name */
faluco@62
  3054
    symbolline=fline;                   /* save line where symbol was found */
faluco@62
  3055
    strcpy(constname,str);              /* save symbol name */
faluco@62
  3056
    needtoken('=');
faluco@62
  3057
    constexpr(&val,&exprtag,NULL);      /* get value */
faluco@62
  3058
    /* add_constant() checks for duplicate definitions */
faluco@62
  3059
    if (!matchtag(tag,exprtag,FALSE)) {
faluco@62
  3060
      /* temporarily reset the line number to where the symbol was defined */
faluco@62
  3061
      int orgfline=fline;
faluco@62
  3062
      fline=symbolline;
faluco@62
  3063
      error(213);                       /* tagname mismatch */
faluco@62
  3064
      fline=orgfline;
faluco@62
  3065
    } /* if */
faluco@62
  3066
    sym=add_constant(constname,val,vclass,tag);
faluco@62
  3067
    if (sym!=NULL)
faluco@62
  3068
      sc_attachdocumentation(sym);/* attach any documenation to the constant */
faluco@62
  3069
  } while (matchtoken(',')); /* enddo */   /* more? */
dvander@27
  3070
  needtoken(tTERM);
dvander@27
  3071
}
dvander@27
  3072
dvander@167
  3073
/*
dvander@167
  3074
 * declstruct - declare a struct type
dvander@167
  3075
 */
dvander@167
  3076
static void declstruct(void)
dvander@167
  3077
{
dvander@167
  3078
	cell val;
dvander@167
  3079
	char *str;
dvander@167
  3080
	int tok;
dvander@167
  3081
	pstruct_t *pstruct;
dvander@167
  3082
	int size;
dvander@167
  3083
dvander@167
  3084
	/* get the explicit tag (required!) */
dvander@167
  3085
	tok = lex(&val,&str);
dvander@167
  3086
	if (tok != tSYMBOL)
dvander@167
  3087
	{
dvander@167
  3088
		error(93);
dvander@167
  3089
	}
dvander@167
  3090
dvander@167
  3091
	if (pstructs_find(str) != NULL)
dvander@167
  3092
	{
ds@841
  3093
		error(98);
dvander@167
  3094
	}
dvander@167
  3095
dvander@167
  3096
	pstruct = pstructs_add(str);
dvander@167
  3097
dvander@167
  3098
	needtoken('{');
dvander@167
  3099
	do
dvander@167
  3100
	{
dvander@167
  3101
		structarg_t arg;
dvander@167
  3102
		if (matchtoken('}'))
dvander@167
  3103
		{
dvander@167
  3104
			/* Quick exit */
dvander@167
  3105
			lexpush();
dvander@167
  3106
			break;
dvander@167
  3107
		}
dvander@167
  3108
		memset(&arg, 0, sizeof(structarg_t));
dvander@167
  3109
		tok = lex(&val,&str);
dvander@167
  3110
		if (tok == tCONST)
dvander@167
  3111
		{
dvander@167
  3112
			arg.fconst = 1;
dvander@167
  3113
			tok = lex(&val,&str);
dvander@167
  3114
		}
dvander@167
  3115
		arg.ident = 0;
dvander@167
  3116
		if (tok == '&')
dvander@167
  3117
		{
dvander@167
  3118
			arg.ident = iREFERENCE;
dvander@167
  3119
			tok = lex(&val,&str);
dvander@167
  3120
		}
dvander@167
  3121
		if (tok == tLABEL)
dvander@167
  3122
		{
dvander@167
  3123
			arg.tag = pc_addtag(str);
dvander@167
  3124
			tok = lex(&val,&str);
dvander@167
  3125
		}
dvander@167
  3126
		if (tok != tSYMBOL)
dvander@167
  3127
		{
dvander@167
  3128
			error(1, "-identifier-", str);
dvander@167
  3129
			continue;
dvander@167
  3130
		}
dvander@167
  3131
		strcpy(arg.name, str);
dvander@167
  3132
		if (matchtoken('['))
dvander@167
  3133
		{
dvander@167
  3134
			if (arg.ident == iREFERENCE)
dvander@167
  3135
			{
dvander@167
  3136
				error(67, arg.name);
dvander@167
  3137
			}
dvander@167
  3138
			arg.ident = iARRAY;
dvander@167
  3139
			do 
dvander@167
  3140
			{
dvander@167
  3141
				constvalue *enumroot;
dvander@167
  3142
				int ignore_tag;
dvander@167
  3143
				if (arg.dimcount == sDIMEN_MAX)
dvander@167
  3144
				{
dvander@167
  3145
					error(53);
dvander@167
  3146
					break;
dvander@167
  3147
				}
dvander@167
  3148
				size = needsub(&ignore_tag, &enumroot);
dvander@167
  3149
				arg.dims[arg.dimcount++] = size;
dvander@167
  3150
			} while (matchtoken('['));
dvander@167
  3151
			/* Handle strings */
dvander@167
  3152
			if (arg.tag == pc_tag_string && arg.dims[arg.dimcount-1])
dvander@167
  3153
			{
dvander@167
  3154
				arg.dims[arg.dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
dvander@167
  3155
			}
dvander@167
  3156
			if (arg.dimcount == 1 && !arg.dims[arg.dimcount-1])
dvander@167
  3157
			{
dvander@167
  3158
				arg.ident = iREFARRAY;
dvander@167
  3159
			}
dvander@167
  3160
		} else if (!arg.ident) {
dvander@167
  3161
			arg.ident = iVARIABLE;
dvander@167
  3162
		}
dvander@167
  3163
		if (pstructs_addarg(pstruct, &arg) == NULL)
dvander@167
  3164
		{
ds@841
  3165
			error(99, arg.name, pstruct->name);
dvander@167
  3166
		}
dvander@167
  3167
	} while (matchtoken(','));
dvander@167
  3168
	needtoken('}');
dvander@167
  3169
	matchtoken(';');	/* eat up optional semicolon */
dvander@167
  3170
}
dvander@167
  3171
dvander@167
  3172
dvander@167
  3173
/**
dvander@167
  3174
 * dofuncenum - declare function enumerations
dvander@132
  3175
 */
dvander@338
  3176
static void dofuncenum(int listmode)
dvander@132
  3177
{
dvander@132
  3178
	cell val;
ds@425
  3179
	char *str;
ds@425
  3180
	// char *ptr;
dvander@132
  3181
	char tagname[sNAMEMAX+1];
dvander@132
  3182
	constvalue *cur;
dvander@132
  3183
	funcenum_t *fenum = NULL;
dvander@132
  3184
	int i;
dvander@2317
  3185
	int newStyleTag = 0;
dvander@2317
  3186
	int isNewStyle = 0;
dvander@132
  3187
dvander@132
  3188
	/* get the explicit tag (required!) */
dvander@132
  3189
	int l = lex(&val,&str);
dvander@132
  3190
	if (l != tSYMBOL)
dvander@132
  3191
	{
dvander@2317
  3192
		if (listmode == FALSE && l == tPUBLIC)
dvander@2317
  3193
		{
dvander@2317
  3194
			isNewStyle = 1;
dvander@2317
  3195
			newStyleTag = pc_addtag(NULL);
dvander@2317
  3196
			l = lex(&val, &str);
dvander@2317
  3197
			if (l != tSYMBOL)
dvander@2317
  3198
			{
dvander@2317
  3199
				error(93);
dvander@2317
  3200
			}
dvander@2317
  3201
		}
dvander@2317
  3202
		else
dvander@2317
  3203
		{
dvander@2317
  3204
			error(93);
dvander@2317
  3205
		}
dvander@132
  3206
	}
dvander@132
  3207
dvander@132
  3208
	/* This tag can't already exist! */
dvander@132
  3209
	cur=tagname_tab.next;
dvander@132
  3210
	while (cur)
dvander@132
  3211
	{
dvander@132
  3212
		if (strcmp(cur->name, str) == 0)
dvander@132
  3213
		{
dvander@132
  3214
			/* Another bad one... */
dvander@132
  3215
			if (!(cur->value & FUNCTAG))
dvander@132
  3216
			{
faluco@138
  3217
				error(94);
dvander@132
  3218
			}
dvander@132
  3219
			break;
dvander@132
  3220
		}
dvander@132
  3221
		cur = cur->next;
dvander@132
  3222
	}
dvander@132
  3223
	strcpy(tagname, str);
dvander@132
  3224
dvander@132
  3225
	fenum = funcenums_add(tagname);
dvander@132
  3226
dvander@338
  3227
	if (listmode)
dvander@338
  3228
	{
dvander@338
  3229
		needtoken('{');
dvander@338
  3230
	}
dvander@132
  3231
	do
dvander@132
  3232
	{
dvander@132
  3233
		functag_t func;
dvander@338
  3234
		if (listmode && matchtoken('}'))
dvander@132
  3235
		{
dvander@132
  3236
			/* Quick exit */
dvander@132
  3237
			lexpush();
dvander@132
  3238
			break;
dvander@132
  3239
		}
dvander@132
  3240
		memset(&func, 0, sizeof(func));
dvander@2317
  3241
		if (!isNewStyle)
dvander@132
  3242
		{
dvander@2317
  3243
			func.ret_tag = pc_addtag(NULL);	/* Get the return tag */
dvander@2317
  3244
			l = lex(&val, &str);
dvander@2317
  3245
			/* :TODO:
dvander@2317
  3246
			 * Right now, there is a problem.  We can't pass non-public function pointer addresses around,
dvander@2317
  3247
			 * because the address isn't known until the final reparse.  Unfortunately, you can write code
dvander@2317
  3248
			 * before the address is known, because Pawn's compiler isn't truly multipass.
dvander@2317
  3249
			 *
dvander@2317
  3250
			 * When you call a function, there's no problem, because it does not write the address.
dvander@2317
  3251
			 * The assembly looks like this:
dvander@2317
  3252
			 *   call MyFunction
dvander@2317
  3253
			 * Then, only at assembly time (once all passes are done), does it know the address.
dvander@2317
  3254
			 * I do not see any solution to this because there is no way I know to inject the function name
dvander@2317
  3255
			 * rather than the constant value.  And even if we could, we'd have to change the assembler recognize that.
dvander@2317
  3256
			 */
dvander@2317
  3257
			if (l == tPUBLIC) {
dvander@2317
  3258
				func.type = uPUBLIC;
dvander@2317
  3259
			} else {
dvander@2317
  3260
				error(1, "-public-", str);
dvander@2317
  3261
			}
dvander@2317
  3262
		}
dvander@2317
  3263
		else
dvander@2317
  3264
		{
dvander@2317
  3265
			func.ret_tag = newStyleTag;
dvander@132
  3266
			func.type = uPUBLIC;
dvander@132
  3267
		}
dvander@132
  3268
		needtoken('(');
dvander@132
  3269
		do 
dvander@132
  3270
		{
dvander@132
  3271
			funcarg_t *arg = &(func.args[func.argcount]);
dvander@132
  3272
dvander@132
  3273
			/* Quick exit */
dvander@132
  3274
			if (matchtoken(')'))
dvander@132
  3275
			{
dvander@132
  3276
				lexpush();
dvander@132
  3277
				break;
dvander@132
  3278
			}
dvander@132
  3279
			l = lex(&val, &str);
dvander@132
  3280
			if (l == '&')
dvander@132
  3281
			{
dvander@132
  3282
				if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
dvander@132
  3283
				{
dvander@132
  3284
					error(1, "-identifier-", "&");
dvander@132
  3285
				}
dvander@132
  3286
				arg->ident = iREFERENCE;
dvander@132
  3287
			} else if (l == tCONST) {
dvander@132
  3288
				if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
dvander@132
  3289
				{
dvander@132
  3290
					error(1, "-identifier-", "const");
dvander@132
  3291
				}
dvander@132
  3292
				arg->fconst=TRUE;
dvander@132
  3293
			} else if (l == tLABEL) {
dvander@132
  3294
				if (arg->tagcount > 0)
dvander@132
  3295
				{
dvander@132
  3296
					error(1, "-identifier-", "-tagname-");
dvander@132
  3297
				}
dvander@132
  3298
				arg->tags[arg->tagcount++] = pc_addtag(str);
dvander@132
  3299
#if 0
dvander@132
  3300
				while (arg->tagcount < sTAGS_MAX)
dvander@132
  3301
				{
dvander@132
  3302
					if (!matchtoken('_') && !needtoken(tSYMBOL))
dvander@132
  3303
					{
dvander@132
  3304
						break;
dvander@132
  3305
					}
dvander@132
  3306
					tokeninfo(&val, &ptr);
dvander@132
  3307
					arg->tags[arg->tagcount++] = pc_addtag(ptr);
dvander@132
  3308
					if (matchtoken('}'))
dvander@132
  3309
					{
dvander@132
  3310
						break;
dvander@132
  3311
					}
dvander@132
  3312
					needtoken(',');
dvander@132
  3313
				}
dvander@132
  3314
				needtoken(':');
dvander@132
  3315
#endif
dvander@132
  3316
				l=tLABEL;
dvander@132
  3317
			} else if (l == tSYMBOL) {
dvander@132
  3318
				if (func.argcount >= sARGS_MAX)
dvander@132
  3319
				{
dvander@132
  3320
					error(45);
dvander@132
  3321
				}
dvander@132
  3322
				if (str[0] == PUBLIC_CHAR)
dvander@132
  3323
				{
dvander@132
  3324
					error(56, str);
dvander@132
  3325
				}
dvander@132
  3326
				if (matchtoken('['))
dvander@132
  3327
				{
dvander@151
  3328
					cell size;
dvander@132
  3329
					if (arg->ident == iREFERENCE)
dvander@132
  3330
					{
dvander@132
  3331
						error(67, str);
dvander@132
  3332
					}
dvander@132
  3333
					do 
dvander@132
  3334
					{
dvander@132
  3335
						constvalue *enumroot;
dvander@132
  3336
						int ignore_tag;
dvander@132
  3337
						if (arg->dimcount == sDIMEN_MAX)
dvander@132
  3338
						{
dvander@132
  3339
							error(53);
dvander@132
  3340
							break;
dvander@132
  3341
						}
dvander@132
  3342
						size = needsub(&ignore_tag, &enumroot);
dvander@132
  3343
						arg->dims[arg->dimcount] = size;
dvander@132
  3344
						arg->dimcount += 1;
dvander@132
  3345
					} while (matchtoken('['));
dvander@151
  3346
					/* Handle strings */
dvander@167
  3347
					if ((arg->tagcount == 1 && arg->tags[0] == pc_tag_string)
dvander@167
  3348
						&& arg->dims[arg->dimcount-1])
dvander@151
  3349
					{
dvander@151
  3350
						arg->dims[arg->dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
dvander@151
  3351
					}
dvander@132
  3352
					arg->ident=iREFARRAY;
dvander@132
  3353
				} else if (arg->ident == 0) {
dvander@132
  3354
					arg->ident = iVARIABLE;
dvander@132
  3355
				}
dvander@132
  3356
			
dvander@132
  3357
				if (matchtoken('='))
dvander@132
  3358
				{
dvander@132
  3359
					needtoken('0');
dvander@132
  3360
					arg->ommittable = TRUE;
dvander@132
  3361
					func.ommittable = TRUE;
dvander@132
  3362
				} else if (func.ommittable) {
faluco@138
  3363
					error(95);
dvander@132
  3364
				}
dvander@132
  3365
				func.argcount++;
dvander@132
  3366
			} else if (l == tELLIPS) {
dvander@132
  3367
				if (arg->ident == iVARIABLE)
dvander@132
  3368
				{
dvander@132
  3369
					error(10);
dvander@132
  3370
				}
dvander@132
  3371
				arg->ident = iVARARGS;
dvander@132
  3372
				func.argcount++;
dvander@132
  3373
			} else {
dvander@132
  3374
				error(10);
dvander@132
  3375
			}
dvander@132
  3376
		} while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(','));
dvander@132
  3377
		needtoken(')');
dvander@132
  3378
		for (i=0; i<func.argcount; i++)
dvander@132
  3379
		{
dvander@132
  3380
			if (func.args[i].tagcount == 0)
dvander@132
  3381
			{
dvander@132
  3382
				func.args[i].tags[0] = 0;
dvander@132
  3383
				func.args[i].tagcount = 1;
dvander@132
  3384
			}
dvander@132
  3385
		}
dvander@132
  3386
		functags_add(fenum, &func);
dvander@338
  3387
		if (!listmode)
dvander@338
  3388
		{
dvander@338
  3389
			break;
dvander@338
  3390
		}
dvander@132
  3391
	} while (matchtoken(','));
dvander@338
  3392
	if (listmode)
dvander@338
  3393
	{
dvander@338
  3394
		needtoken('}');
dvander@338
  3395
	}
dvander@132
  3396
	matchtoken(';'); /* eat an optional semicolon.  nom nom nom */
dvander@132
  3397
	errorset(sRESET, 0);
dvander@132
  3398
}
dvander@132
  3399
dvander@27
  3400
/*  decl_enum   - declare enumerated constants
dvander@27
  3401
 *
dvander@27
  3402
 */
dvander@27
  3403
static void decl_enum(int vclass)
dvander@27
  3404
{
dvander@27
  3405
  char enumname[sNAMEMAX+1],constname[sNAMEMAX+1];
dvander@27
  3406
  cell val,value,size;
dvander@27
  3407
  char *str;
dvander@27
  3408
  int tag,explicittag;
dvander@27
  3409
  cell increment,multiplier;
dvander@27
  3410
  constvalue *enumroot;
dvander@27
  3411
  symbol *enumsym;
dvander@27
  3412
dvander@27
  3413
  /* get an explicit tag, if any (we need to remember whether an explicit
dvander@27
  3414
   * tag was passed, even if that explicit tag was "_:", so we cannot call
dvander@27
  3415
   * pc_addtag() here
dvander@27
  3416
   */
dvander@27
  3417
  if (lex(&val,&str)==tLABEL) {
dvander@27
  3418
    tag=pc_addtag(str);
dvander@27
  3419
    explicittag=TRUE;
dvander@27
  3420
  } else {
dvander@27
  3421
    lexpush();
dvander@27
  3422
    tag=0;
dvander@27
  3423
    explicittag=FALSE;
dvander@27
  3424
  } /* if */
dvander@27
  3425
dvander@27
  3426
  /* get optional enum name (also serves as a tag if no explicit tag was set) */
dvander@27
  3427
  if (lex(&val,&str)==tSYMBOL) {        /* read in (new) token */
dvander@27
  3428
    strcpy(enumname,str);               /* save enum name (last constant) */
dvander@27
  3429
    if (!explicittag)
dvander@27
  3430
      tag=pc_addtag(enumname);
dvander@27
  3431
  } else {
dvander@27
  3432
    lexpush();                          /* analyze again */
dvander@27
  3433
    enumname[0]='\0';
dvander@27
  3434
  } /* if */
dvander@27
  3435
dvander@27
  3436
  /* get increment and multiplier */
dvander@27
  3437
  increment=1;
dvander@27
  3438
  multiplier=1;
dvander@27
  3439
  if (matchtoken('(')) {
dvander@27
  3440
    if (matchtoken(taADD)) {
dvander@27
  3441
      constexpr(&increment,NULL,NULL);
dvander@27
  3442
    } else if (matchtoken(taMULT)) {
dvander@27
  3443
      constexpr(&multiplier,NULL,NULL);
dvander@27
  3444
    } else if (matchtoken(taSHL)) {
dvander@27
  3445
      constexpr(&val,NULL,NULL);
dvander@27
  3446
      while (val-->0)
dvander@27
  3447
        multiplier*=2;
dvander@27
  3448
    } /* if */
dvander@27
  3449
    needtoken(')');
dvander@27
  3450
  } /* if */
dvander@27
  3451
dvander@27
  3452
  if (strlen(enumname)>0) {
dvander@27
  3453
    /* already create the root symbol, so the fields can have it as their "parent" */
dvander@27
  3454
    enumsym=add_constant(enumname,0,vclass,tag);
dvander@27
  3455
    if (enumsym!=NULL)
dvander@27
  3456
      enumsym->usage |= uENUMROOT;
dvander@27
  3457
    /* start a new list for the element names */
dvander@27
  3458
    if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL)
ds@841
  3459
      error(123);                       /* insufficient memory (fatal error) */
dvander@27
  3460
    memset(enumroot,0,sizeof(constvalue));
dvander@27
  3461
  } else {
dvander@27
  3462
    enumsym=NULL;
dvander@27
  3463
    enumroot=NULL;
dvander@27
  3464
  } /* if */
dvander@27
  3465
dvander@27
  3466
  needtoken('{');
dvander@27
  3467
  /* go through all constants */
dvander@27
  3468
  value=0;                              /* default starting value */
dvander@27
  3469
  do {
dvander@27
  3470
    int idxtag,fieldtag;
dvander@27
  3471
    symbol *sym;
dvander@27
  3472
    if (matchtoken('}')) {              /* quick exit if '}' follows ',' */
dvander@27
  3473
      lexpush();
dvander@27
  3474
      break;
dvander@27
  3475
    } /* if */
dvander@27
  3476
    idxtag=pc_addtag(NULL);             /* optional explicit item tag */
dvander@27
  3477
    if (needtoken(tSYMBOL)) {           /* read in (new) token */
dvander@27
  3478
      tokeninfo(&val,&str);             /* get the information */
dvander@27
  3479
      strcpy(constname,str);            /* save symbol name */
dvander@27
  3480
    } else {
dvander@27
  3481
      constname[0]='\0';
dvander@27
  3482
    } /* if */
dvander@27
  3483
    size=increment;                     /* default increment of 'val' */
dvander@27
  3484
    fieldtag=0;                         /* default field tag */
dvander@27
  3485
    if (matchtoken('[')) {
dvander@27
  3486
      constexpr(&size,&fieldtag,NULL);  /* get size */
dvander@27
  3487
      needtoken(']');
dvander@27
  3488
    } /* if */
dvander@151
  3489
    /* :TODO: do we need a size modifier here for pc_tag_string? */
dvander@27
  3490
    if (matchtoken('='))
dvander@27
  3491
      constexpr(&value,NULL,NULL);      /* get value */
dvander@27
  3492
    /* add_constant() checks whether a variable (global or local) or
dvander@27
  3493
     * a constant with the same name already exists
dvander@27
  3494
     */
dvander@27
  3495
    sym=add_constant(constname,value,vclass,tag);
dvander@27
  3496
    if (sym==NULL)
dvander@27
  3497
      continue;                         /* error message already given */
dvander@27
  3498
    /* set the item tag and the item size, for use in indexing arrays */
dvander@27
  3499
    sym->x.tags.index=idxtag;
dvander@27
  3500
    sym->x.tags.field=fieldtag;
dvander@27
  3501
    sym->dim.array.length=size;
dvander@27
  3502
    sym->dim.array.level=0;
dvander@286
  3503
    sym->dim.array.slength=0;
dvander@27
  3504
    sym->parent=enumsym;
dvander@27
  3505
    /* add the constant to a separate list as well */
dvander@27
  3506
    if (enumroot!=NULL) {
dvander@27
  3507
      sym->usage |= uENUMFIELD;
dvander@27
  3508
      append_constval(enumroot,constname,value,tag);
dvander@27
  3509
    } /* if */
dvander@27
  3510
    if (multiplier==1)
dvander@27
  3511
      value+=size;
dvander@27
  3512
    else
dvander@27
  3513
      value*=size*multiplier;
dvander@27
  3514
  } while (matchtoken(','));
dvander@27
  3515
  needtoken('}');       /* terminates the constant list */
dvander@27
  3516
  matchtoken(';');      /* eat an optional ; */
dvander@27
  3517
dvander@27
  3518
  /* set the enum name to the "next" value (typically the last value plus one) */
dvander@27
  3519
  if (enumsym!=NULL) {
dvander@27
  3520
    assert((enumsym->usage & uENUMROOT)!=0);
dvander@27
  3521
    enumsym->addr=value;
dvander@27
  3522
    /* assign the constant list */
dvander@27
  3523
    assert(enumroot!=NULL);
dvander@27
  3524
    enumsym->dim.enumlist=enumroot;
dvander@27
  3525
    sc_attachdocumentation(enumsym);  /* attach any documenation to the enumeration */
dvander@27
  3526
  } /* if */
dvander@27
  3527
}
dvander@27
  3528
dvander@27
  3529
static int getstates(const char *funcname)
dvander@27
  3530
{
dvander@27
  3531
  char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1];
dvander@27
  3532
  cell val;
dvander@27
  3533
  char *str;
dvander@27
  3534
  constvalue *automaton;
dvander@27
  3535
  constvalue *state;
dvander@27
  3536
  int fsa,islabel;
dvander@27
  3537
  int *list;
dvander@27
  3538
  int count,listsize,state_id;
dvander@27
  3539
dvander@27
  3540
  if (!matchtoken('<'))
dvander@27
  3541
    return 0;
dvander@27
  3542
  if (matchtoken('>'))
dvander@27
  3543
    return -1;          /* special construct: all other states (fall-back) */
dvander@27
  3544
dvander@27
  3545
  count=0;
dvander@27
  3546
  listsize=0;
dvander@27
  3547
  list=NULL;
dvander@27
  3548
  fsa=-1;
dvander@27
  3549
dvander@27
  3550
  do {
dvander@27
  3551
    if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
dvander@27
  3552
      break;
dvander@27
  3553
    tokeninfo(&val,&str);
dvander@27
  3554
    assert(strlen(str)<sizeof fsaname);
dvander@27
  3555
    strcpy(fsaname,str);  /* assume this is the name of the automaton */
dvander@27
  3556
    if (islabel || matchtoken(':')) {
dvander@27
  3557
      /* token is an automaton name, add the name and get a new token */
dvander@27
  3558
      if (!needtoken(tSYMBOL))
dvander@27
  3559
        break;
dvander@27
  3560
      tokeninfo(&val,&str);
dvander@27
  3561
      assert(strlen(str)<sizeof statename);
dvander@27
  3562
      strcpy(statename,str);
dvander@27
  3563
    } else {
dvander@27
  3564
      /* the token was the state name (part of an anynymous automaton) */
dvander@27
  3565
      assert(strlen(fsaname)<sizeof statename);
dvander@27
  3566
      strcpy(statename,fsaname);
dvander@27
  3567
      fsaname[0]='\0';
dvander@27
  3568
    } /* if */
dvander@27
  3569
    if (fsa<0 || fsaname[0]!='\0') {
dvander@27
  3570
      automaton=automaton_add(fsaname);
dvander@27
  3571
      assert(automaton!=NULL);
dvander@27
  3572
      if (fsa>=0 && automaton->index!=fsa)
dvander@27
  3573
        error(83,funcname); /* multiple automatons for a single function/variable */
dvander@27
  3574
      fsa=automaton->index;
dvander@27
  3575
    } /* if */
dvander@27
  3576
    state=state_add(statename,fsa);
dvander@27
  3577
    /* add this state to the state combination list (it will be attached to the
dvander@27
  3578
     * automaton later) */
dvander@27
  3579
    state_buildlist(&list,&listsize,&count,(int)state->value);
dvander@27
  3580
  } while (matchtoken(','));
dvander@27
  3581
  needtoken('>');
dvander@27
  3582
dvander@27
  3583
  if (count>0) {
dvander@27
  3584
    assert(automaton!=NULL);
dvander@27
  3585
    assert(fsa>=0);
dvander@27
  3586
    state_id=state_addlist(list,count,fsa);
dvander@27
  3587
    assert(state_id>0);
dvander@27
  3588
  } else {
dvander@27
  3589
    /* error is already given */
dvander@27
  3590
    state_id=0;
dvander@27
  3591
  } /* if */
dvander@27
  3592
  if (list!=NULL)
dvander@27
  3593
    free(list);
dvander@27
  3594
dvander@27
  3595
  return state_id;
dvander@27
  3596
}
dvander@27
  3597
dvander@27
  3598
static void attachstatelist(symbol *sym, int state_id)
dvander@27
  3599
{
dvander@27
  3600
  assert(sym!=NULL);
dvander@27
  3601
  if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0))
dvander@27
  3602
    error(21,sym->name); /* function already defined, either without states or the current definition has no states */
dvander@27
  3603
dvander@27
  3604
  if (state_id!=0) {
dvander@27
  3605
    /* add the state list id */
dvander@27
  3606
    constvalue *stateptr;
dvander@27
  3607
    if (sym->states==NULL) {
dvander@27
  3608
      if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL)
ds@841
  3609
        error(123);             /* insufficient memory (fatal error) */
dvander@27
  3610
      memset(sym->states,0,sizeof(constvalue));
dvander@27
  3611
    } /* if */
dvander@27
  3612
    /* see whether the id already exists (add new state only if it does not
dvander@27
  3613
     * yet exist
dvander@27
  3614
     */
dvander@27
  3615
    assert(sym->states!=NULL);
dvander@27
  3616
    for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next)
dvander@27
  3617
      /* nothing */;
dvander@27
  3618
    assert(state_id<=SHRT_MAX);
dvander@27
  3619
    if (stateptr==NULL)
dvander@27
  3620
      append_constval(sym->states,"",code_idx,(short)state_id);
dvander@27
  3621
    else if (stateptr->value==0)
dvander@27
  3622
      stateptr->value=code_idx;
dvander@27
  3623
    else
dvander@27
  3624
      error(84,sym->name);
dvander@27
  3625
    /* also check for another conflicting situation: a fallback function
dvander@27
  3626
     * without any states
dvander@27
  3627
     */
dvander@27
  3628
    if (state_id==-1 && sc_status!=statFIRST) {
dvander@27
  3629
      /* in the second round, all states should have been accumulated */
dvander@27
  3630
      assert(sym->states!=NULL);
dvander@27
  3631
      for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next)
dvander@27
  3632
        /* nothing */;
dvander@27
  3633
      if (stateptr==NULL)
dvander@27
  3634
        error(85,sym->name);      /* no states are defined for this function */
dvander@27
  3635
    } /* if */
dvander@27
  3636
  } /* if */
dvander@27
  3637
}
dvander@27
  3638
dvander@27
  3639
/*
dvander@27
  3640
 *  Finds a function in the global symbol table or creates a new entry.
dvander@27
  3641
 *  It does some basic processing and error checking.
dvander@27
  3642
 */
dvander@27
  3643
SC_FUNC symbol *fetchfunc(char *name,int tag)
dvander@27
  3644
{
dvander@27
  3645
  symbol *sym;
dvander@27
  3646
dvander@27
  3647
  if ((sym=findglb(name,sGLOBAL))!=0) {   /* already in symbol table? */
dvander@27
  3648
    if (sym->ident!=iFUNCTN) {
dvander@27
  3649
      error(21,name);                     /* yes, but not as a function */
dvander@27
  3650
      return NULL;                        /* make sure the old symbol is not damaged */
dvander@27
  3651
    } else if ((sym->usage & uNATIVE)!=0) {
dvander@27
  3652
      error(21,name);                     /* yes, and it is a native */
dvander@27
  3653
    } /* if */
dvander@27
  3654
    assert(sym->vclass==sGLOBAL);
dvander@27
  3655
    if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag)
dvander@27
  3656
      error(25);                          /* mismatch from earlier prototype */
dvander@27
  3657
    if ((sym->usage & uDEFINE)==0) {
dvander@27
  3658
      /* as long as the function stays undefined, update the address and the tag */
dvander@27
  3659
      if (sym->states==NULL)
dvander@27
  3660
        sym->addr=code_idx;
dvander@27
  3661
      sym->tag=tag;
dvander@27
  3662
    } /* if */
dvander@27
  3663
  } else {
dvander@27
  3664
    /* don't set the "uDEFINE" flag; it may be a prototype */
dvander@27
  3665
    sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0);
dvander@27
  3666
    assert(sym!=NULL);          /* fatal error 103 must be given on error */
dvander@27
  3667
    /* assume no arguments */
dvander@27
  3668
    sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo));
dvander@27
  3669
    sym->dim.arglist[0].ident=0;
dvander@27
  3670
    /* set library ID to NULL (only for native functions) */
dvander@27
  3671
    sym->x.lib=NULL;
dvander@27
  3672
    /* set the required stack size to zero (only for non-native functions) */
dvander@27
  3673
    sym->x.stacksize=1;         /* 1 for PROC opcode */
dvander@27
  3674
  } /* if */
ds@52
  3675
  if (pc_deprecate!=NULL) {
dvander@27
  3676
    assert(sym!=NULL);
ds@52
  3677
    sym->flags|=flgDEPRECATED;
dvander@27
  3678
    if (sc_status==statWRITE) {
dvander@27
  3679
      if (sym->documentation!=NULL) {
dvander@27
  3680
        free(sym->documentation);
dvander@27
  3681
        sym->documentation=NULL;
dvander@27
  3682
      } /* if */
ds@52
  3683
      sym->documentation=pc_deprecate;
dvander@27
  3684
    } else {
ds@52
  3685
      free(pc_deprecate);
dvander@27
  3686
    } /* if */
ds@52
  3687
    pc_deprecate=NULL;
dvander@27
  3688
  } /* if */
dvander@27
  3689
dvander@27
  3690
  return sym;
dvander@27
  3691
}
dvander@27
  3692
dvander@27
  3693
/* This routine adds symbolic information for each argument.
dvander@27
  3694
 */
dvander@27
  3695
static void define_args(void)
dvander@27
  3696
{
dvander@27
  3697
  symbol *sym;
dvander@27
  3698
dvander@27
  3699
  /* At this point, no local variables have been declared. All
dvander@27
  3700
   * local symbols are function arguments.
dvander@27
  3701
   */
dvander@27
  3702
  sym=loctab.next;
dvander@27
  3703
  while (sym!=NULL) {
dvander@27
  3704
    assert(sym->ident!=iLABEL);
dvander@27
  3705
    assert(sym->vclass==sLOCAL);
dvander@27
  3706
    markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */
dvander@27
  3707
    sym=sym->next;
dvander@27
  3708
  } /* while */
dvander@27
  3709
}
dvander@27
  3710
dvander@27
  3711
static int operatorname(char *name)
dvander@27
  3712
{
dvander@27
  3713
  int opertok;
dvander@27
  3714
  char *str;
dvander@27
  3715
  cell val;
dvander@27
  3716
dvander@27
  3717
  assert(name!=NULL);
dvander@27
  3718
dvander@27
  3719
  /* check the operator */
dvander@27
  3720
  opertok=lex(&val,&str);
dvander@27
  3721
  switch (opertok) {
dvander@27
  3722
  case '+':
dvander@27
  3723
  case '-':
dvander@27
  3724
  case '*':
dvander@27
  3725
  case '/':
dvander@27
  3726
  case '%':
dvander@27
  3727
  case '>':
dvander@27
  3728
  case '<':
dvander@27
  3729
  case '!':
dvander@27
  3730
  case '~':
dvander@27
  3731
  case '=':
dvander@27
  3732
    name[0]=(char)opertok;
dvander@27
  3733
    name[1]='\0';
dvander@27
  3734
    break;
dvander@27
  3735
  case tINC:
dvander@27
  3736
    strcpy(name,"++");
dvander@27
  3737
    break;
dvander@27
  3738
  case tDEC:
dvander@27
  3739
    strcpy(name,"--");
dvander@27
  3740
    break;
dvander@27
  3741
  case tlEQ:
dvander@27
  3742
    strcpy(name,"==");
dvander@27
  3743
    break;
dvander@27
  3744
  case tlNE:
dvander@27
  3745
    strcpy(name,"!=");
dvander@27
  3746
    break;
dvander@27
  3747
  case tlLE:
dvander@27
  3748
    strcpy(name,"<=");
dvander@27
  3749
    break;
dvander@27
  3750
  case tlGE:
dvander@27
  3751
    strcpy(name,">=");
dvander@27
  3752
    break;
dvander@27
  3753
  default:
dvander@27
  3754
    name[0]='\0';
dvander@27
  3755
    error(7);           /* operator cannot be redefined (or bad operator name) */
dvander@27
  3756
    return 0;
dvander@27
  3757
  } /* switch */
dvander@27
  3758
dvander@27
  3759
  return opertok;
dvander@27
  3760
}
dvander@27
  3761
dvander@27
  3762
static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag)
dvander@27
  3763
{
dvander@27
  3764
  int tags[2]={0,0};
dvander@27
  3765
  int count=0;
dvander@27
  3766
  arginfo *arg;
dvander@27
  3767
  char tmpname[sNAMEMAX+1];
dvander@27
  3768
  symbol *oldsym;
dvander@27
  3769
dvander@27
  3770
  if (opertok==0)
dvander@27
  3771
    return TRUE;
dvander@27
  3772
dvander@27
  3773
  assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL);
dvander@27
  3774
  /* count arguments and save (first two) tags */
dvander@27
  3775
  while (arg=&sym->dim.arglist[count], arg->ident!=0) {
dvander@27
  3776
    if (count<2) {
dvander@27
  3777
      if (arg->numtags>1)
dvander@27
  3778
        error(65,count+1);  /* function argument may only have a single tag */
dvander@27
  3779
      else if (arg->numtags==1)
dvander@27
  3780
        tags[count]=arg->tags[0];
dvander@27
  3781
    } /* if */
dvander@27
  3782
    if (opertok=='~' && count==0) {
dvander@27
  3783
      if (arg->ident!=iREFARRAY)
dvander@27
  3784
        error(73,arg->name);/* must be an array argument */
dvander@27
  3785
    } else {
ds@841
  3786
      //if (arg->ident!=iVARIABLE)
ds@841
  3787
        //error(66,arg->name);/* must be non-reference argument */
dvander@27
  3788
    } /* if */
dvander@27
  3789
    if (arg->hasdefault)
dvander@27
  3790
      error(59,arg->name);  /* arguments of an operator may not have a default value */
dvander@27
  3791
    count++;
dvander@27
  3792
  } /* while */
dvander@27
  3793
dvander@27
  3794
  /* for '!', '++' and '--', count must be 1
dvander@27
  3795
   * for '-', count may be 1 or 2
dvander@27
  3796
   * for '=', count must be 1, and the resulttag is also important
dvander@27
  3797
   * for all other (binary) operators and the special '~' operator, count must be 2
dvander@27
  3798
   */
dvander@27
  3799
  switch (opertok) {
dvander@27
  3800
  case '!':
dvander@27
  3801
  case '=':
dvander@27
  3802
  case tINC:
dvander@27
  3803
  case tDEC:
dvander@27
  3804
    if (count!=1)
dvander@27
  3805
      error(62);      /* number or placement of the operands does not fit the operator */
dvander@27
  3806
    break;
dvander@27
  3807
  case '-':
dvander@27
  3808
    if (count!=1 && count!=2)
dvander@27
  3809
      error(62);      /* number or placement of the operands does not fit the operator */
dvander@27
  3810
    break;
dvander@27
  3811
  default:
dvander@27
  3812
    if (count!=2)
dvander@27
  3813
      error(62);      /* number or placement of the operands does not fit the operator */
dvander@27
  3814
  } /* switch */
dvander@27
  3815
dvander@27
  3816
  if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0))
dvander@27
  3817
    error(64);        /* cannot change predefined operators */
dvander@27
  3818
dvander@27
  3819
  /* change the operator name */
dvander@27
  3820
  assert(strlen(opername)>0);
dvander@27
  3821
  operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag);
dvander@27
  3822
  if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) {
dvander@27
  3823
    int i;
dvander@27
  3824
    if ((oldsym->usage & uDEFINE)!=0) {
dvander@27
  3825
      char errname[2*sNAMEMAX+16];
dvander@27
  3826
      funcdisplayname(errname,tmpname);
dvander@27
  3827
      error(21,errname);        /* symbol already defined */
dvander@27
  3828
    } /* if */
dvander@27
  3829
    sym->usage|=oldsym->usage;  /* copy flags from the previous definition */
dvander@27
  3830
    for (i=0; i<oldsym->numrefers; i++)
dvander@27
  3831
      if (oldsym->refer[i]!=NULL)
dvander@27
  3832
        refer_symbol(sym,oldsym->refer[i]);
dvander@27
  3833
    delete_symbol(&glbtab,oldsym);
dvander@27
  3834
  } /* if */
dvander@27
  3835
  strcpy(sym->name,tmpname);
dvander@27
  3836
  sym->hash=namehash(sym->name);/* calculate new hash */
dvander@27
  3837
dvander@27
  3838
  /* operators should return a value, except the '~' operator */
dvander@27
  3839
  if (opertok!='~')
dvander@27
  3840
    sym->usage |= uRETVALUE;
dvander@27
  3841
dvander@27
  3842
  return TRUE;
dvander@27
  3843
}
dvander@27
  3844
dvander@27
  3845
static int check_operatortag(int opertok,int resulttag,char *opername)
dvander@27
  3846
{
dvander@27
  3847
  assert(opername!=NULL && strlen(opername)>0);
dvander@27
  3848
  switch (opertok) {
dvander@27
  3849
  case '!':
dvander@27
  3850
  case '<':
dvander@27
  3851
  case '>':
dvander@27
  3852
  case tlEQ:
dvander@27
  3853
  case tlNE:
dvander@27
  3854
  case tlLE:
dvander@27
  3855
  case tlGE:
dvander@27
  3856
    if (resulttag!=pc_addtag("bool")) {
dvander@27
  3857
      error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */
dvander@27
  3858
      return FALSE;
dvander@27
  3859
    } /* if */
dvander@27
  3860
    break;
dvander@27
  3861
  case '~':
dvander@27
  3862
    if (resulttag!=0) {
dvander@27
  3863
      error(63,opername,"_:");    /* operator "~" requires a "_:" result tag */
dvander@27
  3864
      return FALSE;
dvander@27
  3865
    } /* if */
dvander@27
  3866
    break;
dvander@27
  3867
  } /* switch */
dvander@27
  3868
  return TRUE;
dvander@27
  3869
}
dvander@27
  3870
dvander@27
  3871
static char *tag2str(char *dest,int tag)
dvander@27
  3872
{
dvander@27
  3873
  tag &= TAGMASK;
dvander@27
  3874
  assert(tag>=0);
dvander@27
  3875
  sprintf(dest,"0%x",tag);
dvander@27
  3876
  return isdigit(dest[1]) ? &dest[1] : dest;
dvander@27
  3877
}
dvander@27
  3878
dvander@27
  3879
SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag)
dvander@27
  3880
{
dvander@27
  3881
  char tagstr1[10], tagstr2[10];
dvander@27
  3882
  int opertok;
dvander@27
  3883
dvander@27
  3884
  assert(numtags>=1 && numtags<=2);
dvander@27
  3885
  opertok= (opername[1]=='\0') ? opername[0] : 0;
dvander@27
  3886
  if (opertok=='=')
dvander@27
  3887
    sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1));
dvander@27
  3888
  else if (numtags==1 || opertok=='~')
dvander@27
  3889
    sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1));
dvander@27
  3890
  else
dvander@27
  3891
    sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2));
dvander@27
  3892
  return symname;
dvander@27
  3893
}
dvander@27
  3894
dvander@27
  3895
static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname)
dvander@27
  3896
{
dvander@27
  3897
  char *ptr,*name;
dvander@27
  3898
  int unary;
dvander@27
  3899
dvander@27
  3900
  /* tags are only positive, so if the function name starts with a '-',
dvander@27
  3901
   * the operator is an unary '-' or '--' operator.
dvander@27
  3902
   */
dvander@27
  3903
  if (*fname=='-') {
dvander@27
  3904
    *tag1=0;
dvander@27
  3905
    unary=TRUE;
dvander@27
  3906
    ptr=fname;
dvander@27
  3907
  } else {
dvander@27
  3908
    *tag1=(int)strtol(fname,&ptr,16);
dvander@27
  3909
    unary= ptr==fname;  /* unary operator if it doesn't start with a tag name */
dvander@27
  3910
  } /* if */
dvander@27
  3911
  assert(!unary || *tag1==0);
dvander@27
  3912
  assert(*ptr!='\0');
dvander@27
  3913
  for (name=opname; !isdigit(*ptr); )
dvander@27
  3914
    *name++ = *ptr++;
dvander@27
  3915
  *name='\0';
dvander@27
  3916
  *tag2=(int)strtol(ptr,NULL,16);
dvander@27
  3917
  return unary;
dvander@27
  3918
}
dvander@27
  3919
dvander@132
  3920
constvalue *find_tag_byval(int tag)
dvander@27
  3921
{
dvander@27
  3922
  constvalue *tagsym;
dvander@27
  3923
  tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG);
dvander@27
  3924
  if (tagsym==NULL)
dvander@27
  3925
    tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);
dvander@27
  3926
  return tagsym;
dvander@27
  3927
}
dvander@27
  3928
dvander@27
  3929
SC_FUNC char *funcdisplayname(char *dest,char *funcname)
dvander@27
  3930
{
dvander@27
  3931
  int tags[2];
dvander@27
  3932
  char opname[10];
dvander@27
  3933
  constvalue *tagsym[2];
dvander@27
  3934
  int unary;
dvander@27
  3935
dvander@27
  3936
  if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') {
dvander@27
  3937
    if (dest!=funcname)
dvander@27
  3938
      strcpy(dest,funcname);
dvander@27
  3939
    return dest;
dvander@27
  3940
  } /* if */
dvander@27
  3941
dvander@27
  3942
  unary=parse_funcname(funcname,&tags[0],&tags[1],opname);
dvander@27
  3943
  tagsym[1]=find_tag_byval(tags[1]);
dvander@27
  3944
  assert(tagsym[1]!=NULL);
dvander@27
  3945
  if (unary) {
dvander@27
  3946
    sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name);
dvander@27
  3947
  } else {
dvander@27
  3948
    tagsym[0]=find_tag_byval(tags[0]);
dvander@27
  3949
    assert(tagsym[0]!=NULL);
dvander@27
  3950
    /* special case: the assignment operator has the return value as the 2nd tag */
dvander@27
  3951
    if (opname[0]=='=' && opname[1]=='\0')
dvander@27
  3952
      sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name);
dvander@27
  3953
    else
dvander@27
  3954
      sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name);
dvander@27
  3955
  } /* if */
dvander@27
  3956
  return dest;
dvander@27
  3957
}
dvander@27
  3958
dvander@27
  3959
static void funcstub(int fnative)
dvander@27
  3960
{
dvander@27
  3961
  int tok,tag,fpublic;
dvander@27
  3962
  char *str;
dvander@27
  3963
  cell val,size;
dvander@27
  3964
  char symbolname[sNAMEMAX+1];
dvander@27
  3965
  int idxtag[sDIMEN_MAX];
dvander@27
  3966
  int dim[sDIMEN_MAX];
dvander@27
  3967
  int numdim;
dvander@27
  3968
  symbol *sym,*sub;
dvander@27
  3969
  int opertok;
dvander@27
  3970
dvander@27
  3971
  opertok=0;
dvander@27
  3972
  lastst=0;
dvander@27
  3973
  litidx=0;                     /* clear the literal pool */
dvander@27
  3974
  assert(loctab.next==NULL);    /* local symbol table should be empty */
dvander@27
  3975
dvander@27
  3976
  tag=pc_addtag(NULL);			/* get the tag of the return value */
dvander@27
  3977
  numdim=0;
dvander@27
  3978
  while (matchtoken('[')) {
dvander@27
  3979
    /* the function returns an array, get this tag for the index and the array
dvander@27
  3980
     * dimensions
dvander@27
  3981
     */
dvander@27
  3982
    if (numdim == sDIMEN_MAX) {
dvander@27
  3983
      error(53);                /* exceeding maximum number of dimensions */
dvander@27
  3984
      return;
dvander@27
  3985
    } /* if */
dvander@27
  3986
    size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
dvander@27
  3987
    if (size==0)
dvander@27
  3988
      error(9);                 /* invalid array size */
dvander@27
  3989
    #if INT_MAX < LONG_MAX
dvander@27
  3990
      if (size > INT_MAX)
ds@841
  3991
        error(125);             /* overflow, exceeding capacity */
dvander@27
  3992
    #endif
dvander@27
  3993
    dim[numdim++]=(int)size;
dvander@27
  3994
  } /* while */
dvander@27
  3995
dvander@191
  3996
  if (tag == pc_tag_string && numdim && dim[numdim-1])
dvander@151
  3997
    dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
dvander@151
  3998
dvander@27
  3999
  tok=lex(&val,&str);
dvander@27
  4000
  fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR);
dvander@27
  4001
  if (fnative) {
dvander@27
  4002
    if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR)
dvander@27
  4003
      error(42);                /* invalid combination of class specifiers */
dvander@27
  4004
  } else {
dvander@27
  4005
    if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC)
dvander@27
  4006
      tok=lex(&val,&str);
dvander@27
  4007
  } /* if */
dvander@27
  4008
dvander@27
  4009
  if (tok==tOPERATOR) {
dvander@27
  4010
    opertok=operatorname(symbolname);
dvander@27
  4011
    if (opertok==0)
dvander@27
  4012
      return;                   /* error message already given */
dvander@27
  4013
    check_operatortag(opertok,tag,symbolname);
dvander@27
  4014
  } else {
dvander@27
  4015
    if (tok!=tSYMBOL && freading) {
dvander@27
  4016
      error(10);                /* illegal function or declaration */
dvander@27
  4017
      return;
dvander@27
  4018
    } /* if */
dvander@27
  4019
    strcpy(symbolname,str);
dvander@27
  4020
  } /* if */
dvander@27
  4021
  needtoken('(');               /* only functions may be native/forward */
dvander@27
  4022
dvander@27
  4023
  sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
dvander@27
  4024
  if (sym==NULL)
dvander@27
  4025
    return;
dvander@27
  4026
  if (fnative) {
dvander@27
  4027
    sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
dvander@27
  4028
    sym->x.lib=curlibrary;
dvander@27
  4029
  } else if (fpublic) {
dvander@27
  4030
    sym->usage|=uPUBLIC;
dvander@27
  4031
  } /* if */
dvander@27
  4032
  sym->usage|=uFORWARD;
dvander@27
  4033
dvander@27
  4034
  declargs(sym,FALSE);
dvander@27
  4035
  /* "declargs()" found the ")" */
dvander@27
  4036
  sc_attachdocumentation(sym);  /* attach any documenation to the function */
dvander@27
  4037
  if (!operatoradjust(opertok,sym,symbolname,tag))
dvander@27
  4038
    sym->usage &= ~uDEFINE;
dvander@27
  4039
dvander@27
  4040
  if (getstates(symbolname)!=0) {
dvander@27
  4041
    if (fnative || opertok!=0)
dvander@27
  4042
      error(82);                /* native functions and operators may not have states */
dvander@27
  4043
    else
dvander@27
  4044
      error(231);               /* ignoring state specifications on forward declarations */
dvander@27
  4045
  } /* if */
dvander@27
  4046
dvander@27
  4047
  /* for a native operator, also need to specify an "exported" function name;
dvander@27
  4048
   * for a native function, this is optional
dvander@27
  4049
   */
dvander@27
  4050
  if (fnative) {
dvander@27
  4051
    if (opertok!=0) {
dvander@27
  4052
      needtoken('=');
dvander@27
  4053
      lexpush();        /* push back, for matchtoken() to retrieve again */
dvander@27
  4054
    } /* if */
dvander@27
  4055
    if (matchtoken('=')) {
dvander@27
  4056
      /* allow number or symbol */
dvander@27
  4057
      if (matchtoken(tSYMBOL)) {
dvander@27
  4058
        tokeninfo(&val,&str);
dvander@27
  4059
        insert_alias(sym->name,str);
dvander@27
  4060
      } else {
dvander@27
  4061
        constexpr(&val,NULL,NULL);
dvander@27
  4062
        sym->addr=val;
dvander@27
  4063
        /* At the moment, I have assumed that this syntax is only valid if
dvander@27
  4064
         * val < 0. To properly mix "normal" native functions and indexed
dvander@27
  4065
         * native functions, one should use negative indices anyway.
dvander@27
  4066
         * Special code for a negative index in sym->addr exists in SC4.C
dvander@27
  4067
         * (ffcall()) and in SC6.C (the loops for counting the number of native
dvander@27
  4068
         * variables and for writing them).
dvander@27
  4069
         */
dvander@27
  4070
      } /* if */
dvander@27
  4071
    } /* if */
dvander@27
  4072
  } /* if */
dvander@27
  4073
  needtoken(tTERM);
dvander@27
  4074
dvander@27
  4075
  /* attach the array to the function symbol */
dvander@27
  4076
  if (numdim>0) {
dvander@27
  4077
    assert(sym!=NULL);
dvander@27
  4078
    sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag);
dvander@27
  4079
    sub->parent=sym;
dvander@27
  4080
  } /* if */
dvander@27
  4081
dvander@27
  4082
  litidx=0;                     /* clear the literal pool */
dvander@27
  4083
  delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
dvander@27
  4084
}
dvander@27
  4085
dvander@27
  4086
/*  newfunc    - begin a function
dvander@27
  4087
 *
dvander@27
  4088
 *  This routine is called from "parse" and tries to make a function
dvander@27
  4089
 *  out of the following text
dvander@27
  4090
 *
dvander@27
  4091
 *  Global references: funcstatus,lastst,litidx
dvander@27
  4092
 *                     rettype  (altered)
dvander@27
  4093
 *                     curfunc  (altered)
dvander@27
  4094
 *                     declared (altered)
dvander@27
  4095
 *                     glb_declared (altered)
dvander@27
  4096
 *                     sc_alignnext (altered)
dvander@27
  4097
 */
dvander@27
  4098
static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock)
dvander@27
  4099
{
dvander@27
  4100
  symbol *sym;
dvander@27
  4101
  int argcnt,tok,tag,funcline;
dvander@27
  4102
  int opertok,opererror;
dvander@27
  4103
  char symbolname[sNAMEMAX+1];
dvander@27
  4104
  char *str;
dvander@27
  4105
  cell val,cidx,glbdecl;
dvander@27
  4106
  short filenum;
dvander@27
  4107
  int state_id;
dvander@27
  4108
dvander@27
  4109
  assert(litidx==0);    /* literal queue should be empty */
dvander@27
  4110
  litidx=0;             /* clear the literal pool (should already be empty) */
dvander@27
  4111
  opertok=0;
dvander@27
  4112
  lastst=0;             /* no statement yet */
dvander@27
  4113
  cidx=0;               /* just to avoid compiler warnings */
dvander@27
  4114
  glbdecl=0;
dvander@27
  4115
  assert(loctab.next==NULL);    /* local symbol table should be empty */
dvander@27
  4116
  filenum=fcurrent;     /* save file number at the start of the declaration */
dvander@27
  4117
dvander@27
  4118
  if (firstname!=NULL) {
dvander@27
  4119
    assert(strlen(firstname)<=sNAMEMAX);
dvander@27
  4120
    strcpy(symbolname,firstname);       /* save symbol name */
dvander@27
  4121
    tag=firsttag;
dvander@27
  4122
  } else {
dvander@27
  4123
    tag= (firsttag>=0) ? firsttag : pc_addtag(NULL);
dvander@27
  4124
    tok=lex(&val,&str);
dvander@27
  4125
    assert(!fpublic);
dvander@27
  4126
    if (tok==tNATIVE || tok==tPUBLIC && stock)
dvander@27
  4127
      error(42);                /* invalid combination of class specifiers */
dvander@27
  4128
    if (tok==tOPERATOR) {
dvander@27
  4129
      opertok=operatorname(symbolname);
dvander@27
  4130
      if (opertok==0)
dvander@27
  4131
        return TRUE;            /* error message already given */
dvander@27
  4132
      check_operatortag(opertok,tag,symbolname);
dvander@27
  4133
    } else {
dvander@27
  4134
      if (tok!=tSYMBOL && freading) {
dvander@27
  4135
        error(20,str);          /* invalid symbol name */
dvander@27
  4136
        return FALSE;
dvander@27
  4137
      } /* if */
dvander@27
  4138
      assert(strlen(str)<=sNAMEMAX);
dvander@27
  4139
      strcpy(symbolname,str);
dvander@27
  4140
    } /* if */
dvander@27
  4141
  } /* if */
dvander@27
  4142
  /* check whether this is a function or a variable declaration */
dvander@27
  4143
  if (!matchtoken('('))
dvander@27
  4144
    return FALSE;
dvander@27
  4145
  /* so it is a function, proceed */
dvander@27
  4146
  funcline=fline;               /* save line at which the function is defined */
dvander@27
  4147
  if (symbolname[0]==PUBLIC_CHAR) {
dvander@27
  4148
    fpublic=TRUE;               /* implicitly public function */
dvander@27
  4149
    if (stock)
dvander@27
  4150
      error(42);                /* invalid combination of class specifiers */
dvander@27
  4151
  } /* if */
dvander@27
  4152
  sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
dvander@27
  4153
  if (sym==NULL || (sym->usage & uNATIVE)!=0)
dvander@27
  4154
    return TRUE;                /* it was recognized as a function declaration, but not as a valid one */
dvander@27
  4155
  if (fpublic)
dvander@27
  4156
    sym->usage|=uPUBLIC;
dvander@27
  4157
  if (fstatic)
dvander@27
  4158
    sym->fnumber=filenum;
dvander@27
  4159
  /* if the function was used before being declared, and it has a tag for the
dvander@27
  4160
   * result, add a third pass (as second "skimming" parse) because the function
dvander@27
  4161
   * result may have been used with user-defined operators, which have now
dvander@27
  4162
   * been incorrectly flagged (as the return tag was unknown at the time of
dvander@27
  4163
   * the call)
dvander@27
  4164
   */
dvander@27
  4165
  if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) {
dvander@27
  4166
    int curstatus=sc_status;
dvander@27
  4167
    sc_status=statWRITE;  /* temporarily set status to WRITE, so the warning isn't blocked */
dvander@27
  4168
#if 0 /* SourceMod - silly, should be removed in first pass, so removed */
dvander@27
  4169
    error(208);
dvander@27
  4170
#endif
dvander@27
  4171
    sc_status=curstatus;
dvander@27
  4172
    sc_reparse=TRUE;      /* must add another pass to "initial scan" phase */
dvander@27
  4173
  } /* if */
dvander@27
  4174
#if 0	/* Not used for SourceMod */
dvander@27
  4175
  /* we want public functions to be explicitly prototyped, as they are called
dvander@27
  4176
   * from the outside
dvander@27
  4177
   */
dvander@27
  4178
  if (fpublic && (sym->usage & uFORWARD)==0)
dvander@27
  4179
    error(235,symbolname);
dvander@27
  4180
#endif
dvander@27
  4181
  /* declare all arguments */
dvander@27
  4182
  argcnt=declargs(sym,TRUE);
dvander@27
  4183
  opererror=!operatoradjust(opertok,sym,symbolname,tag);
dvander@27
  4184
  if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) {
dvander@27
  4185
    if (argcnt>0)
dvander@27
  4186
      error(5);         /* "main()" and "entry()" functions may not have any arguments */
dvander@27
  4187
    sym->usage|=uREAD;  /* "main()" is the program's entry point: always used */
dvander@27
  4188
  } /* if */
dvander@27
  4189
  state_id=getstates(symbolname);
dvander@27
  4190
  if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0))
dvander@27
  4191
    error(82);          /* operators may not have states, main() may neither */
dvander@27
  4192
  attachstatelist(sym,state_id);
dvander@27
  4193
  /* "declargs()" found the ")"; if a ";" appears after this, it was a
dvander@27
  4194
   * prototype */
dvander@27
  4195
  if (matchtoken(';')) {
dvander@1858
  4196
    if (sym->usage & uPUBLIC)
dvander@1858
  4197
      error(10);
dvander@27
  4198
    sym->usage|=uFORWARD;
dvander@27
  4199
    if (!sc_needsemicolon)
dvander@745
  4200
      error(10);       /* old style prototypes used with optional semicolumns */
dvander@27
  4201
    delete_symbols(&loctab,0,TRUE,TRUE);  /* prototype is done; forget everything */
dvander@27
  4202
    return TRUE;
dvander@27
  4203
  } /* if */
dvander@27
  4204
  /* so it is not a prototype, proceed */
dvander@27
  4205
  /* if this is a function that is not referred to (this can only be detected
dvander@27
  4206
   * in the second stage), shut code generation off */
dvander@27
  4207
  if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) {
dvander@27
  4208
    sc_status=statSKIP;
dvander@27
  4209
    cidx=code_idx;
dvander@27
  4210
    glbdecl=glb_declared;
dvander@27
  4211
  } /* if */
ds@1265
  4212
  if ((sym->flags & flgDEPRECATED) != 0 && (sym->usage & uSTOCK) == 0) {
dvander@27
  4213
    char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
ds@52
  4214
    error(234,symbolname,ptr);  /* deprecated (probably a public function) */
dvander@27
  4215
  } /* if */
dvander@27
  4216
  begcseg();
dvander@27
  4217
  sym->usage|=uDEFINE;  /* set the definition flag */
dvander@27
  4218
  if (stock)
dvander@27
  4219
    sym->usage|=uSTOCK;
dvander@27
  4220
  if (opertok!=0 && opererror)
dvander@27
  4221
    sym->usage &= ~uDEFINE;
dvander@27
  4222
  /* if the function has states, dump the label to the start of the function */
dvander@27
  4223
  if (state_id!=0) {
dvander@27
  4224
    constvalue *ptr=sym->states->next;
dvander@27
  4225
    while (ptr!=NULL) {
dvander@27
  4226
      assert(sc_status!=statWRITE || strlen(ptr->name)>0);
dvander@27
  4227
      if (ptr->index==state_id) {
dvander@27
  4228
        setlabel((int)strtol(ptr->name,NULL,16));
dvander@27
  4229
        break;
dvander@27
  4230
      } /* if */
dvander@27
  4231
      ptr=ptr->next;
dvander@27
  4232
    } /* while */
dvander@27
  4233
  } /* if */
dvander@27
  4234
  startfunc(sym->name); /* creates stack frame */
dvander@27
  4235
  insert_dbgline(funcline);
dvander@27
  4236
  setline(FALSE);
dvander@27
  4237
  if (sc_alignnext) {
dvander@27
  4238
    alignframe(sc_dataalign);
dvander@27
  4239
    sc_alignnext=FALSE;
dvander@27
  4240
  } /* if */
dvander@27
  4241
  declared=0;           /* number of local cells */
dvander@117
  4242
  resetstacklist();
dvander@118
  4243
  resetheaplist();
dvander@27
  4244
  rettype=(sym->usage & uRETVALUE);      /* set "return type" variable */
dvander@27
  4245
  curfunc=sym;
dvander@27
  4246
  define_args();        /* add the symbolic info for the function arguments */
dvander@27
  4247
  #if !defined SC_LIGHT
dvander@27
  4248
    if (matchtoken('{')) {
dvander@27
  4249
      lexpush();
dvander@27
  4250
    } else {
dvander@27
  4251
      /* Insert a separator so that comments following the statement will not
dvander@27
  4252
       * be attached to this function; they should be attached to the next
dvander@27
  4253
       * function. This is not a problem for functions having a compound block,
dvander@27
  4254
       * because the closing brace is an explicit "end token" for the function.
dvander@27
  4255
       * With single statement functions, the preprocessor may overread the
dvander@27
  4256
       * source code before the parser determines an "end of statement".
dvander@27
  4257
       */
dvander@27
  4258
      insert_docstring_separator();
dvander@27
  4259
    } /* if */
dvander@27
  4260
  #endif
dvander@27
  4261
  sc_curstates=state_id;/* set state id, for accessing global state variables */
dvander@27
  4262
  statement(NULL,FALSE);
dvander@27
  4263
  sc_curstates=0;
dvander@27
  4264
  if ((rettype & uRETVALUE)!=0)
dvander@27
  4265
    sym->usage|=uRETVALUE;
dvander@27
  4266
  if (declared!=0) {
dvander@27
  4267
    /* This happens only in a very special (and useless) case, where a function
dvander@27
  4268
     * has only a single statement in its body (no compound block) and that
dvander@27
  4269
     * statement declares a new variable
dvander@27
  4270
     */
dvander@459
  4271
    popstacklist(1);
dvander@27
  4272
    declared=0;
dvander@27
  4273
  } /* if */
dvander@27
  4274
  if ((lastst!=tRETURN) && (lastst!=tGOTO)){
dvander@27
  4275
    ldconst(0,sPRI);
dvander@27
  4276
    ffret(strcmp(sym->name,uENTRYFUNC)!=0);
dvander@27
  4277
    if ((sym->usage & uRETVALUE)!=0) {
dvander@27
  4278
      char symname[2*sNAMEMAX+16];  /* allow space for user defined operators */
dvander@27
  4279
      funcdisplayname(symname,sym->name);
dvander@27
  4280
      error(209,symname);       /* function should return a value */
dvander@27
  4281
    } /* if */
dvander@27
  4282
  } /* if */
dvander@27
  4283
  endfunc();
dvander@27
  4284
  sym->codeaddr=code_idx;
dvander@27
  4285
  sc_attachdocumentation(sym);  /* attach collected documenation to the function */
dvander@27
  4286
  if (litidx) {                 /* if there are literals defined */
dvander@27
  4287
    glb_declared+=litidx;
dvander@27
  4288
    begdseg();                  /* flip to DATA segment */
dvander@27
  4289
    dumplits();                 /* dump literal strings */
dvander@27
  4290
    litidx=0;
dvander@27
  4291
  } /* if */
dvander@27
  4292
  testsymbols(&loctab,0,TRUE,TRUE);     /* test for unused arguments and labels */
dvander@27
  4293
  delete_symbols(&loctab,0,TRUE,TRUE);  /* clear local variables queue */
dvander@27
  4294
  assert(loctab.next==NULL);
dvander@27
  4295
  curfunc=NULL;
dvander@27
  4296
  if (sc_status==statSKIP) {
dvander@27
  4297
    sc_status=statWRITE;
dvander@27
  4298
    code_idx=cidx;
dvander@27
  4299
    glb_declared=glbdecl;
dvander@27
  4300
  } /* if */
dvander@27
  4301
  return TRUE;
dvander@27
  4302
}
dvander@27
  4303
dvander@27
  4304
static int argcompare(arginfo *a1,arginfo *a2)
dvander@27
  4305
{
dvander@27
  4306
  int result,level,i;
dvander@27
  4307
dvander@27
  4308
#if 0	/* SourceMod uses case insensitive args for forwards */
dvander@27
  4309
  result= strcmp(a1->name,a2->name)==0;     /* name */
dvander@27
  4310
#else
dvander@27
  4311
  result=1;
dvander@27
  4312
#endif
dvander@27
  4313
  if (result)
dvander@27
  4314
    result= a1->ident==a2->ident;           /* type/class */
dvander@27
  4315
  if (result)
dvander@27
  4316
    result= a1->usage==a2->usage;           /* "const" flag */
dvander@27
  4317
  if (result)
dvander@27
  4318
    result= a1->numtags==a2->numtags;       /* tags (number and names) */
dvander@27
  4319
  for (i=0; result && i<a1->numtags; i++)
dvander@27
  4320
    result= a1->tags[i]==a2->tags[i];
dvander@27
  4321
  if (result)
dvander@27
  4322
    result= a1->numdim==a2->numdim;         /* array dimensions & index tags */
dvander@27
  4323
  for (level=0; result && level<a1->numdim; level++)
dvander@27
  4324
    result= a1->dim[level]==a2->dim[level];
dvander@27
  4325
  for (level=0; result && level<a1->numdim; level++)
dvander@27
  4326
    result= a1->idxtag[level]==a2->idxtag[level];
dvander@27
  4327
  if (result)
dvander@27
  4328
    result= a1->hasdefault==a2->hasdefault; /* availability of default value */
dvander@27
  4329
  if (a1->hasdefault) {
dvander@27
  4330
    if (a1->ident==iREFARRAY) {
dvander@27
  4331
      if (result)
dvander@27
  4332
        result= a1->defvalue.array.size==a2->defvalue.array.size;
dvander@27
  4333
      if (result)
dvander@27
  4334
        result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize;
dvander@27
  4335
      /* ??? should also check contents of the default array (these troubles
dvander@27
  4336
       * go away in a 2-pass compiler that forbids double declarations, but
dvander@27
  4337
       * Pawn currently does not forbid them) */
dvander@27
  4338
    } else {
dvander@27
  4339
      if (result) {
dvander@286
  4340
        if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0 || (a1->hasdefault & uCOUNTOF)!=0)
dvander@27
  4341
          result= a1->hasdefault==a2->hasdefault
dvander@27
  4342
                  && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0
dvander@27
  4343
                  && a1->defvalue.size.level==a2->defvalue.size.level;
dvander@27
  4344
        else
dvander@27
  4345
          result= a1->defvalue.val==a2->defvalue.val;
dvander@27
  4346
      } /* if */
dvander@27
  4347
    } /* if */
dvander@27
  4348
    if (result)
dvander@27
  4349
      result= a1->defvalue_tag==a2->defvalue_tag;
dvander@27
  4350
  } /* if */
dvander@27
  4351
  return result;
dvander@27
  4352
}
dvander@27
  4353
dvander@27
  4354
/*  declargs()
dvander@27
  4355
 *
dvander@27
  4356
 *  This routine adds an entry in the local symbol table for each argument
dvander@27
  4357
 *  found in the argument list. It returns the number of arguments.
dvander@27
  4358
 */
dvander@27
  4359
static int declargs(symbol *sym,int chkshadow)
dvander@27
  4360
{
dvander@27
  4361
  #define MAXTAGS 16
dvander@27
  4362
  char *ptr;
dvander@27
  4363
  int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags;
dvander@27
  4364
  cell val;
dvander@27
  4365
  arginfo arg, *arglist;
dvander@27
  4366
  char name[sNAMEMAX+1];
dvander@27
  4367
  int ident,fpublic,fconst;
dvander@27
  4368
  int idx;
dvander@27
  4369
dvander@27
  4370
  /* if the function is already defined earlier, get the number of arguments
dvander@27
  4371
   * of the existing definition
dvander@27
  4372
   */
dvander@27
  4373
  oldargcnt=0;
dvander@27
  4374
  if ((sym->usage & uPROTOTYPED)!=0)
dvander@27
  4375
    while (sym->dim.arglist[oldargcnt].ident!=0)
dvander@27
  4376
      oldargcnt++;
dvander@27
  4377
  argcnt=0;                             /* zero aruments up to now */
dvander@27
  4378
  ident=iVARIABLE;
dvander@27
  4379
  numtags=0;
dvander@27
  4380
  fconst=FALSE;
dvander@338
  4381
  fpublic= (sym->usage & (uPUBLIC|uSTOCK))!=0;
dvander@27
  4382
  /* the '(' parantheses has already been parsed */
dvander@27
  4383
  if (!matchtoken(')')){
dvander@27
  4384
    do {                                /* there are arguments; process them */
dvander@27
  4385
      /* any legal name increases argument count (and stack offset) */
dvander@27
  4386
      tok=lex(&val,&ptr);
dvander@27
  4387
      switch (tok) {
dvander@27
  4388
      case 0:
dvander@27
  4389
        /* nothing */
dvander@27
  4390
        break;
dvander@27
  4391
      case '&':
dvander@27
  4392
        if (ident!=iVARIABLE || numtags>0)
dvander@27
  4393
          error(1,"-identifier-","&");
dvander@27
  4394
        ident=iREFERENCE;
dvander@27
  4395
        break;
dvander@27
  4396
      case tCONST:
dvander@27
  4397
        if (ident!=iVARIABLE || numtags>0)
dvander@27
  4398
          error(1,"-identifier-","const");
dvander@27
  4399
        fconst=TRUE;
dvander@27
  4400
        break;
dvander@27
  4401
      case tLABEL:
dvander@27
  4402
        if (numtags>0)
dvander@27
  4403
          error(1,"-identifier-","-tagname-");
dvander@27
  4404
        tags[0]=pc_addtag(ptr);
dvander@27
  4405
        numtags=1;
dvander@27
  4406
        break;
dvander@27
  4407
      case '{':
dvander@27
  4408
        if (numtags>0)
dvander@27
  4409
          error(1,"-identifier-","-tagname-");
dvander@27
  4410
        numtags=0;
dvander@27
  4411
        while (numtags<MAXTAGS) {
dvander@27
  4412
          if (!matchtoken('_') && !needtoken(tSYMBOL))
dvander@27
  4413
            break;
dvander@27
  4414
          tokeninfo(&val,&ptr);
dvander@27
  4415
          tags[numtags++]=pc_addtag(ptr);
dvander@27
  4416
          if (matchtoken('}'))
dvander@27
  4417
            break;
dvander@27
  4418
          needtoken(',');
dvander@27
  4419
        } /* for */
dvander@27
  4420
        needtoken(':');
dvander@27
  4421
        tok=tLABEL;     /* for outer loop: flag that we have seen a tagname */
dvander@27
  4422
        break;
dvander@27
  4423
      case tSYMBOL:
dvander@27
  4424
        if (argcnt>=sMAXARGS)
dvander@27
  4425
          error(45);                    /* too many function arguments */
dvander@27
  4426
        strcpy(name,ptr);               /* save symbol name */
dvander@27
  4427
        if (name[0]==PUBLIC_CHAR)
dvander@27
  4428
          error(56,name);               /* function arguments cannot be public */
dvander@27
  4429
        if (numtags==0)
dvander@27
  4430
          tags[numtags++]=0;            /* default tag */
dvander@27
  4431
        /* Stack layout:
dvander@27
  4432
         *   base + 0*sizeof(cell)  == previous "base"
dvander@27
  4433
         *   base + 1*sizeof(cell)  == function return address
dvander@27
  4434
         *   base + 2*sizeof(cell)  == number of arguments
dvander@27
  4435
         *   base + 3*sizeof(cell)  == first argument of the function
dvander@27
  4436
         * So the offset of each argument is "(argcnt+3) * sizeof(cell)".
dvander@27
  4437
         */
dvander@27
  4438
        doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg);
dvander@338
  4439
        /* :TODO: fix this so stocks that are func pointers can't have default arguments? */
dvander@338
  4440
        if ((sym->usage & uPUBLIC) && arg.hasdefault)
dvander@27
  4441
          error(59,name);       /* arguments of a public function may not have a default value */
dvander@27
  4442
        if ((sym->usage & uPROTOTYPED)==0) {
dvander@27
  4443
          /* redimension the argument list, add the entry */
dvander@27
  4444
          sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
dvander@27
  4445
          if (sym->dim.arglist==0)
ds@841
  4446
            error(123);                 /* insufficient memory */
dvander@27
  4447
          memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo));  /* keep the list terminated */
dvander@27
  4448
          sym->dim.arglist[argcnt]=arg;
dvander@27
  4449
        } else {
dvander@27
  4450
          /* check the argument with the earlier definition */
dvander@27
  4451
          if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg))
dvander@27
  4452
            error(25);          /* function definition does not match prototype */
dvander@27
  4453
          /* may need to free default array argument and the tag list */
dvander@27
  4454
          if (arg.ident==iREFARRAY && arg.hasdefault)
dvander@27
  4455
            free(arg.defvalue.array.data);
dvander@27
  4456
          else if (arg.ident==iVARIABLE
dvander@286
  4457
                   && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0) || (arg.hasdefault & uCOUNTOF)!=0)
dvander@27
  4458
            free(arg.defvalue.size.symname);
dvander@27
  4459
          free(arg.tags);
dvander@27
  4460
        } /* if */
dvander@27
  4461
        argcnt++;
dvander@27
  4462
        ident=iVARIABLE;
dvander@27
  4463
        numtags=0;
dvander@27
  4464
        fconst=FALSE;
dvander@27
  4465
        break;
dvander@27
  4466
      case tELLIPS:
dvander@27
  4467
        if (ident!=iVARIABLE)
dvander@27
  4468
          error(10);                    /* illegal function or declaration */
dvander@27
  4469
        if (numtags==0)
dvander@27
  4470
          tags[numtags++]=0;            /* default tag */
dvander@27
  4471
        if ((sym->usage & uPROTOTYPED)==0) {
dvander@27
  4472
          /* redimension the argument list, add the entry iVARARGS */
dvander@27
  4473
          sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
dvander@27
  4474
          if (sym->dim.arglist==0)
ds@841
  4475
            error(123);                 /* insufficient memory */
dvander@27
  4476
          memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo));  /* keep the list terminated */
dvander@27
  4477
          sym->dim.arglist[argcnt].ident=iVARARGS;
dvander@27
  4478
          sym->dim.arglist[argcnt].hasdefault=FALSE;
dvander@27
  4479
          sym->dim.arglist[argcnt].defvalue.val=0;
dvander@27
  4480
          sym->dim.arglist[argcnt].defvalue_tag=0;
dvander@27
  4481
          sym->dim.arglist[argcnt].numtags=numtags;
dvander@27
  4482
          sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]);
dvander@27
  4483
          if (sym->dim.arglist[argcnt].tags==NULL)
ds@841
  4484
            error(123);                 /* insufficient memory */
dvander@27
  4485
          memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]);
dvander@27
  4486
        } else {
dvander@27
  4487
          if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS)
dvander@27
  4488
            error(25);          /* function definition does not match prototype */
dvander@27
  4489
        } /* if */
dvander@27
  4490
        argcnt++;
dvander@27
  4491
        break;
dvander@27
  4492
      default:
dvander@27
  4493
        error(10);                      /* illegal function or declaration */
dvander@27
  4494
      } /* switch */
dvander@27
  4495
    } while (tok=='&' || tok==tLABEL || tok==tCONST
dvander@27
  4496
             || tok!=tELLIPS && matchtoken(',')); /* more? */
dvander@27
  4497
    /* if the next token is not ",", it should be ")" */
dvander@27
  4498
    needtoken(')');
dvander@27
  4499
  } /* if */
dvander@27
  4500
  /* resolve any "sizeof" arguments (now that all arguments are known) */
dvander@27
  4501
  assert(sym->dim.arglist!=NULL);
dvander@27
  4502
  arglist=sym->dim.arglist;
dvander@27
  4503
  for (idx=0; idx<argcnt && arglist[idx].ident!=0; idx++) {
dvander@286
  4504
    if ((arglist[idx].hasdefault & uSIZEOF)!=0 
dvander@286
  4505
         || (arglist[idx].hasdefault & uTAGOF)!=0 
dvander@286
  4506
         || (arglist[idx].hasdefault & uCOUNTOF)!=0) {
dvander@27
  4507
      int altidx;
dvander@27
  4508
      /* Find the argument with the name mentioned after the "sizeof". Note
dvander@27
  4509
       * that we cannot use findloc here because we need the arginfo struct,
dvander@27
  4510
       * not the symbol.
dvander@27
  4511
       */
dvander@27
  4512
      ptr=arglist[idx].defvalue.size.symname;
dvander@27
  4513
      assert(ptr!=NULL);
dvander@27
  4514
      for (altidx=0; altidx<argcnt && strcmp(ptr,arglist[altidx].name)!=0; altidx++)
dvander@27
  4515
        /* nothing */;
dvander@27
  4516
      if (altidx>=argcnt) {
dvander@27
  4517
        error(17,ptr);                  /* undefined symbol */
dvander@27
  4518
      } else {
dvander@27
  4519
        assert(arglist[idx].defvalue.size.symname!=NULL);
dvander@27
  4520
        /* check the level against the number of dimensions */
dvander@27
  4521
        if (arglist[idx].defvalue.size.level>0
dvander@27
  4522
            && arglist[idx].defvalue.size.level>=arglist[altidx].numdim)
dvander@27
  4523
          error(28,arglist[idx].name);  /* invalid subscript */
dvander@27
  4524
        /* check the type of the argument whose size to take; for a iVARIABLE
dvander@27
  4525
         * or a iREFERENCE, this is always 1 (so the code is redundant)
dvander@27
  4526
         */
dvander@27
  4527
        assert(arglist[altidx].ident!=iVARARGS);
dvander@286
  4528
        if (arglist[altidx].ident!=iREFARRAY 
dvander@286
  4529
            && (((arglist[idx].hasdefault & uSIZEOF)!=0)
dvander@286
  4530
                  || (arglist[idx].hasdefault & uCOUNTOF)!=0)) {
dvander@27
  4531
          if ((arglist[idx].hasdefault & uTAGOF)!=0) {
dvander@27
  4532
            error(81,arglist[idx].name);  /* cannot take "tagof" an indexed array */
dvander@27
  4533
          } else {
dvander@27
  4534
            assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE);
dvander@27
  4535
            error(223,ptr);             /* redundant sizeof */
dvander@27
  4536
          } /* if */
dvander@27
  4537
        } /* if */
dvander@27
  4538
      } /* if */
dvander@27
  4539
    } /* if */
dvander@27
  4540
  } /* for */
dvander@27
  4541
dvander@27
  4542
  sym->usage|=uPROTOTYPED;
dvander@27
  4543
  errorset(sRESET,0);           /* reset error flag (clear the "panic mode")*/
dvander@27
  4544
  return argcnt;
dvander@27
  4545
}
dvander@27
  4546
dvander@27
  4547
/*  doarg       - declare one argument type
dvander@27
  4548
 *
dvander@27
  4549
 *  this routine is called from "declargs()" and adds an entry in the local
dvander@27
  4550
 *  symbol table for one argument.
dvander@27
  4551
 *
dvander@27
  4552
 *  "fpublic" indicates whether the function for this argument list is public.
dvander@27
  4553
 *  The arguments themselves are never public.
dvander@27
  4554
 */
dvander@27
  4555
static void doarg(char *name,int ident,int offset,int tags[],int numtags,
dvander@27
  4556
                  int fpublic,int fconst,int chkshadow,arginfo *arg)
dvander@27
  4557
{
dvander@27
  4558
  symbol *argsym;
dvander@27
  4559
  constvalue *enumroot;
dvander@27
  4560
  cell size;
dvander@286
  4561
  int slength=0;
dvander@27
  4562
dvander@27
  4563
  strcpy(arg->name,name);
dvander@27
  4564
  arg->hasdefault=FALSE;        /* preset (most common case) */
dvander@27
  4565
  arg->defvalue.val=0;          /* clear */
dvander@27
  4566
  arg->defvalue_tag=0;
dvander@27
  4567
  arg->numdim=0;
dvander@27
  4568
  if (matchtoken('[')) {
dvander@27
  4569
    if (ident==iREFERENCE)
dvander@27
  4570
      error(67,name);           /* illegal declaration ("&name[]" is unsupported) */
dvander@27
  4571
    do {
dvander@27
  4572
      if (arg->numdim == sDIMEN_MAX) {
dvander@27
  4573
        error(53);              /* exceeding maximum number of dimensions */
dvander@27
  4574
        return;
dvander@27
  4575
      } /* if */
dvander@27
  4576
      size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */
dvander@27
  4577
      #if INT_MAX < LONG_MAX
dvander@27
  4578
        if (size > INT_MAX)
ds@841
  4579
          error(125);           /* overflow, exceeding capacity */
dvander@27
  4580
      #endif
dvander@27
  4581
      arg->dim[arg->numdim]=(int)size;
dvander@27
  4582
      arg->numdim+=1;
dvander@27
  4583
    } while (matchtoken('['));
dvander@27
  4584
    ident=iREFARRAY;            /* "reference to array" (is a pointer) */
dvander@286
  4585
    if (checktag(tags, numtags, pc_tag_string)) {
dvander@286
  4586
      slength = arg->dim[arg->numdim - 1];
dvander@151
  4587
      arg->dim[arg->numdim - 1] = (size + sizeof(cell) - 1) / sizeof(cell);
dvander@286
  4588
    }
dvander@27
  4589
    if (matchtoken('=')) {
dvander@27
  4590
      assert(litidx==0);        /* at the start of a function, this is reset */
dvander@27
  4591
      assert(numtags>0);
dvander@1044
  4592
      /* Check if there is a symbol */
dvander@1044
  4593
      if (matchtoken(tSYMBOL)) {
dvander@1044
  4594
        symbol *sym;
dvander@1044
  4595
        char *name;
dvander@1044
  4596
        cell val;
dvander@1044
  4597
        tokeninfo(&val,&name);
dvander@1044
  4598
        if ((sym=findglb(name, sGLOBAL)) == NULL) {
dvander@1044
  4599
          error(17, name);      /* undefined symbol */
dvander@1044
  4600
        } else {
dvander@1044
  4601
          arg->hasdefault=TRUE; /* argument as a default value */
dvander@1044
  4602
		  memset(&arg->defvalue, 0, sizeof(arg->defvalue));
dvander@1044
  4603
          arg->defvalue.array.data=NULL;
dvander@1044
  4604
		  arg->defvalue.array.addr=sym->addr;
dvander@1044
  4605
		  arg->defvalue_tag=sym->tag;
dvander@1044
  4606
          if (sc_status==statWRITE && (sym->usage & uREAD)==0) {
dvander@1044
  4607
            markusage(sym, uREAD);
dvander@1044
  4608
          }
dvander@1044
  4609
        }
dvander@1044
  4610
      } else {
dvander@1045
  4611
        initials2(ident,tags[0],&size,arg->dim,arg->numdim,enumroot, 1, 0);
dvander@1044
  4612
        assert(size>=litidx);
dvander@1044
  4613
        /* allocate memory to hold the initial values */
dvander@1044
  4614
        arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell));
dvander@1044
  4615
        if (arg->defvalue.array.data!=NULL) {
dvander@1044
  4616
          int i;
dvander@1044
  4617
          memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell));
dvander@1044
  4618
          arg->hasdefault=TRUE;   /* argument has default value */
dvander@1044
  4619
          arg->defvalue.array.size=litidx;
dvander@1044
  4620
          arg->defvalue.array.addr=-1;
dvander@1044
  4621
          /* calulate size to reserve on the heap */
dvander@1044
  4622
          arg->defvalue.array.arraysize=1;
dvander@1044
  4623
          for (i=0; i<arg->numdim; i++)
dvander@1044
  4624
            arg->defvalue.array.arraysize*=arg->dim[i];
dvander@1044
  4625
          if (arg->defvalue.array.arraysize < arg->defvalue.array.size)
dvander@1044
  4626
            arg->defvalue.array.arraysize = arg->defvalue.array.size;
dvander@1044
  4627
        } /* if */
dvander@1044
  4628
        litidx=0;                 /* reset */
dvander@1044
  4629
      }
dvander@27
  4630
    } /* if */
dvander@27
  4631
  } else {
dvander@27
  4632
    if (matchtoken('=')) {
dvander@27
  4633
      unsigned char size_tag_token;
dvander@27
  4634
      assert(ident==iVARIABLE || ident==iREFERENCE);
dvander@27
  4635
      arg->hasdefault=TRUE;     /* argument has a default value */
dvander@27
  4636
      size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0);
dvander@27
  4637
      if (size_tag_token==0)
dvander@27
  4638
        size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0);
dvander@286
  4639
      if (size_tag_token==0)
dvander@286
  4640
        size_tag_token=(unsigned char)(matchtoken(tCELLSOF) ? uCOUNTOF : 0);
dvander@27
  4641
      if (size_tag_token!=0) {
dvander@27
  4642
        int paranthese;
dvander@27
  4643
        if (ident==iREFERENCE)
dvander@27
  4644
          error(66,name);       /* argument may not be a reference */
dvander@27
  4645
        paranthese=0;
dvander@27
  4646
        while (matchtoken('('))
dvander@27
  4647
          paranthese++;
dvander@27
  4648
        if (needtoken(tSYMBOL)) {
dvander@27
  4649
          /* save the name of the argument whose size id to take */
dvander@27
  4650
          char *name;
dvander@27
  4651
          cell val;
dvander@27
  4652
          tokeninfo(&val,&name);
dvander@27
  4653
          if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL)
ds@841
  4654
            error(123);         /* insufficient memory */
dvander@27
  4655
          arg->defvalue.size.level=0;
dvander@286
  4656
          if (size_tag_token==uSIZEOF || size_tag_token==uCOUNTOF) {
dvander@27
  4657
            while (matchtoken('[')) {
dvander@27
  4658
              arg->defvalue.size.level+=(short)1;
dvander@27
  4659
              needtoken(']');
dvander@27
  4660
            } /* while */
dvander@27
  4661
          } /* if */
dvander@27
  4662
          if (ident==iVARIABLE) /* make sure we set this only if not a reference */
dvander@27
  4663
            arg->hasdefault |= size_tag_token;  /* uSIZEOF or uTAGOF */
dvander@27
  4664
        } /* if */
dvander@27
  4665
        while (paranthese--)
dvander@27
  4666
          needtoken(')');
dvander@27
  4667
      } else {
dvander@27
  4668
        constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL);
dvander@27
  4669
        assert(numtags>0);
dvander@27
  4670
        if (!matchtag(tags[0],arg->defvalue_tag,TRUE))
dvander@27
  4671
          error(213);           /* tagname mismatch */
dvander@27
  4672
      } /* if */
dvander@27
  4673
    } /* if */
dvander@27
  4674
  } /* if */
dvander@27
  4675
  arg->ident=(char)ident;
dvander@27
  4676
  arg->usage=(char)(fconst ? uCONST : 0);
dvander@27
  4677
  arg->numtags=numtags;
dvander@27
  4678
  arg->tags=(int*)malloc(numtags*sizeof tags[0]);
dvander@27
  4679
  if (arg->tags==NULL)
ds@841
  4680
    error(123);                 /* insufficient memory */
dvander@27
  4681
  memcpy(arg->tags,tags,numtags*sizeof tags[0]);
dvander@27
  4682
  argsym=findloc(name);
dvander@27
  4683
  if (argsym!=NULL) {
dvander@27
  4684
    error(21,name);             /* symbol already defined */
dvander@27
  4685
  } else {
dvander@27
  4686
    if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN)
dvander@27
  4687
      error(219,name);          /* variable shadows another symbol */
dvander@27
  4688
    /* add details of type and address */
dvander@27
  4689
    assert(numtags>0);
dvander@286
  4690
    argsym=addvariable2(name,offset,ident,sLOCAL,tags[0],
dvander@286
  4691
                       arg->dim,arg->numdim,arg->idxtag,slength);
dvander@27
  4692
    argsym->compound=0;
dvander@27
  4693
    if (ident==iREFERENCE)
dvander@27
  4694
      argsym->usage|=uREAD;     /* because references are passed back */
dvander@27
  4695
    if (fpublic)
dvander@27
  4696
      argsym->usage|=uREAD;     /* arguments of public functions are always "used" */
dvander@27
  4697
    if (fconst)
dvander@27
  4698
      argsym->usage|=uCONST;
dvander@27
  4699
  } /* if */
dvander@27
  4700
}
dvander@27
  4701
dvander@27
  4702
static int count_referrers(symbol *entry)
dvander@27
  4703
{
dvander@27
  4704
  int i,count;
dvander@27
  4705
dvander@27
  4706
  count=0;
dvander@27
  4707
  for (i=0; i<entry->numrefers; i++)
dvander@27
  4708
    if (entry->refer[i]!=NULL)
dvander@27
  4709
      count++;
dvander@27
  4710
  return count;
dvander@27
  4711
}
dvander@27
  4712
dvander@27
  4713
#if !defined SC_LIGHT
dvander@27
  4714
static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue,
dvander@27
  4715
                       char **outer_start,int *outer_length,
dvander@27
  4716
                       char **inner_start,int *inner_length)
dvander@27
  4717
{
dvander@27
  4718
  char *ptr,*inner_end;
dvander@27
  4719
  int xmltag_len,xmlparam_len,xmlvalue_len;
dvander@27
  4720
  int match;
dvander@27
  4721
dvander@27
  4722
  assert(source!=NULL);
dvander@27
  4723
  assert(xmltag!=NULL);
dvander@27
  4724
  assert(outer_start!=NULL);
dvander@27
  4725
  assert(outer_length!=NULL);
dvander@27
  4726
  assert(inner_start!=NULL);
dvander@27
  4727
  assert(inner_length!=NULL);
dvander@27
  4728
dvander@27
  4729
  /* both NULL or both non-NULL */
dvander@27
  4730
  assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL);
dvander@27
  4731
dvander@27
  4732
  xmltag_len=strlen(xmltag);
dvander@27
  4733
  xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0;
dvander@27
  4734
  xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0;
dvander@27
  4735
  ptr=source;
dvander@27
  4736
  /* find an opening '<' */
dvander@27
  4737
  while ((ptr=strchr(ptr,'<'))!=NULL) {
dvander@27
  4738
    *outer_start=ptr;           /* be optimistic... */
dvander@27
  4739
    match=FALSE;                /* ...and pessimistic at the same time */
dvander@27
  4740
    ptr++;                      /* skip '<' */
dvander@27
  4741
    while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4742
      ptr++;                    /* skip white space */
dvander@27
  4743
    if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
dvander@27
  4744
      /* xml tag found, optionally check the parameter */
dvander@27
  4745
      ptr+=xmltag_len;
dvander@27
  4746
      while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4747
        ptr++;                  /* skip white space */
dvander@27
  4748
      if (xmlparam!=NULL) {
dvander@27
  4749
        if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) {
dvander@27
  4750
          ptr+=xmlparam_len;
dvander@27
  4751
          while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4752
            ptr++;              /* skip white space */
dvander@27
  4753
          if (*ptr=='=') {
dvander@27
  4754
            ptr++;              /* skip '=' */
dvander@27
  4755
            while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4756
              ptr++;            /* skip white space */
dvander@27
  4757
            if (*ptr=='"' || *ptr=='\'')
dvander@27
  4758
              ptr++;            /* skip " or ' */
dvander@27
  4759
            assert(xmlvalue!=NULL);
dvander@27
  4760
            if (strncmp(ptr,xmlvalue,xmlvalue_len)==0
dvander@27
  4761
                && (*(ptr+xmlvalue_len)<=' '
dvander@27
  4762
                    || *(ptr+xmlvalue_len)=='>'
dvander@27
  4763
                    || *(ptr+xmlvalue_len)=='"'
dvander@27
  4764
                    || *(ptr+xmlvalue_len)=='\''))
dvander@27
  4765
              match=TRUE;       /* found it */
dvander@27
  4766
          } /* if */
dvander@27
  4767
        } /* if */
dvander@27
  4768
      } else {
dvander@27
  4769
        match=TRUE;             /* don't check the parameter */
dvander@27
  4770
      } /* if */
dvander@27
  4771
    } /* if */
dvander@27
  4772
    if (match) {
dvander@27
  4773
      /* now find the end of the opening tag */
dvander@27
  4774
      while (*ptr!='\0' && *ptr!='>')
dvander@27
  4775
        ptr++;
dvander@27
  4776
      if (*ptr=='>')
dvander@27
  4777
        ptr++;
dvander@27
  4778
      while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4779
        ptr++;                  /* skip white space */
dvander@27
  4780
      *inner_start=ptr;
dvander@27
  4781
      /* find the start of the closing tag (assume no nesting) */
dvander@27
  4782
      while ((ptr=strchr(ptr,'<'))!=NULL) {
dvander@27
  4783
        inner_end=ptr;
dvander@27
  4784
        ptr++;                  /* skip '<' */
dvander@27
  4785
        while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4786
          ptr++;                /* skip white space */
dvander@27
  4787
        if (*ptr=='/') {
dvander@27
  4788
          ptr++;                /* skip / */
dvander@27
  4789
          while (*ptr!='\0' && *ptr<=' ')
dvander@27
  4790
            ptr++;              /* skip white space */
dvander@27
  4791
          if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
dvander@27
  4792
            /* find the end of the closing tag */
dvander@27
  4793
            while (*ptr!='\0' && *ptr!='>')
dvander@27
  4794
              ptr++;
dvander@27
  4795
            if (*ptr=='>')
dvander@27
  4796
              ptr++;
dvander@27
  4797
            /* set the lengths of the inner and outer segment */
dvander@27
  4798
            assert(*inner_start!=NULL);
dvander@27
  4799
            *inner_length=(int)(inner_end-*inner_start);
dvander@27
  4800
            assert(*outer_start!=NULL);
dvander@27
  4801
            *outer_length=(int)(ptr-*outer_start);
dvander@27
  4802
            break;              /* break out of the loop */
dvander@27
  4803
          } /* if */
dvander@27
  4804
        } /* if */
dvander@27
  4805
      } /* while */
dvander@27
  4806
      return TRUE;
dvander@27
  4807
    } /* if */
dvander@27
  4808
  } /* while */
dvander@27
  4809
  return FALSE; /* not found */
dvander@27
  4810
}
dvander@27
  4811
dvander@27
  4812
static char *xmlencode(char *dest,char *source)
dvander@27
  4813
{
dvander@27
  4814
  char temp[2*sNAMEMAX+20],*ptr;
dvander@27
  4815
dvander@27
  4816
  /* replace < by &lt; and such; normally, such a symbol occurs at most once in
dvander@27
  4817
   * a symbol name (e.g. "operator<")
dvander@27
  4818
   */
dvander@27
  4819
  ptr=temp;
dvander@27
  4820
  while (*source!='\0') {
dvander@27
  4821
    switch (*source) {
dvander@27
  4822
    case '<':
dvander@27
  4823
      strcpy(ptr,"&lt;");
dvander@27
  4824
      ptr+=4;
dvander@27
  4825
      break;
dvander@27
  4826
    case '>':
dvander@27
  4827
      strcpy(ptr,"&gt;");
dvander@27
  4828
      ptr+=4;
dvander@27
  4829
      break;
dvander@27
  4830
    case '&':
dvander@27
  4831
      strcpy(ptr,"&amp;");
dvander@27
  4832
      ptr+=5;
dvander@27
  4833
      break;
dvander@27
  4834
    default:
dvander@27
  4835
      *ptr++=*source;
dvander@27
  4836
    } /* switch */
dvander@27
  4837
    source++;
dvander@27
  4838
  } /* while */
dvander@27
  4839
  *ptr='\0';
dvander@27
  4840
  strcpy(dest,temp);
dvander@27
  4841
  return dest;
dvander@27
  4842
}
dvander@27
  4843
dvander@27
  4844
static void make_report(symbol *root,FILE *log,char *sourcefile)
dvander@27
  4845
{
dvander@27
  4846
  char symname[_MAX_PATH];
dvander@27
  4847
  int i,arg;
dvander@27
  4848
  symbol *sym,*ref;
dvander@27
  4849
  constvalue *tagsym;
dvander@27
  4850
  constvalue *enumroot;
dvander@27
  4851
  char *ptr;
dvander@27
  4852
dvander@27
  4853
  /* adapt the installation directory */
dvander@27
  4854
  strcpy(symname,sc_rootpath);
dvander@27
  4855
  #if DIRSEP_CHAR=='\\'
dvander@27
  4856
    while ((ptr=strchr(symname,':'))!=NULL)
dvander@27
  4857
      *ptr='|';
dvander@27
  4858
    while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL)
dvander@27
  4859
      *ptr='/';
dvander@27
  4860
  #endif
dvander@27
  4861
dvander@27
  4862
  /* the XML header */
dvander@27
  4863
  fprintf(log,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
dvander@27
  4864
  fprintf(log,"<?xml-stylesheet href=\"file:///%s/xml/pawndoc.xsl\" type=\"text/xsl\"?>\n",symname);
dvander@27
  4865
  fprintf(log,"<doc source=\"%s\">\n",sourcefile);
dvander@27
  4866
  ptr=strrchr(sourcefile,DIRSEP_CHAR);
dvander@27
  4867
  if (ptr!=NULL)
dvander@27
  4868
    ptr++;
dvander@27
  4869
  else
dvander@27
  4870
    ptr=sourcefile;
dvander@27
  4871
  fprintf(log,"\t<assembly>\n\t\t<name>%s</name>\n\t</assembly>\n",ptr);
dvander@27
  4872
dvander@27
  4873
  /* attach the global documentation, if any */
dvander@27
  4874
  if (sc_documentation!=NULL) {
dvander@27
  4875
    fprintf(log,"\n\t<!-- general -->\n");
dvander@27
  4876
    fprintf(log,"\t<general>\n\t\t");
dvander@27
  4877
    fputs(sc_documentation,log);
dvander@27
  4878
    fprintf(log,"\n\t</general>\n\n");
dvander@27
  4879
  } /* if */
dvander@27
  4880
dvander@27
  4881
  /* use multiple passes to print constants variables and functions in
dvander@27
  4882
   * separate sections
dvander@27
  4883
   */
dvander@27
  4884
  fprintf(log,"\t<members>\n");
dvander@27
  4885
dvander@27
  4886
  fprintf(log,"\n\t\t<!-- enumerations -->\n");
dvander@27
  4887
  for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  4888
    if (sym->parent!=NULL)
dvander@27
  4889
      continue;                 /* hierarchical data type */
dvander@27
  4890
    assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
dvander@27
  4891
           || sym->ident==iARRAY || sym->ident==iFUNCTN);
dvander@27
  4892
    if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0)
dvander@27
  4893
      continue;
dvander@27
  4894
    if ((sym->usage & uREAD)==0)
dvander@27
  4895
      continue;
dvander@27
  4896
    fprintf(log,"\t\t<member name=\"T:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
dvander@27
  4897
    if (sym->tag!=0) {
dvander@27
  4898
      tagsym=find_tag_byval(sym->tag);
dvander@27
  4899
      assert(tagsym!=NULL);
dvander@27
  4900
      fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
dvander@27
  4901
    } /* if */
dvander@27
  4902
    /* browse through all fields */
dvander@27
  4903
    if ((enumroot=sym->dim.enumlist)!=NULL) {
dvander@27
  4904
      enumroot=enumroot->next;  /* skip root */
dvander@27
  4905
      while (enumroot!=NULL) {
dvander@27
  4906
        fprintf(log,"\t\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,enumroot->name),enumroot->value);
dvander@27
  4907
        /* find the constant with this name and get the tag */
dvander@27
  4908
        ref=findglb(enumroot->name,sGLOBAL);
dvander@27
  4909
        if (ref!=NULL) {
dvander@27
  4910
          if (ref->x.tags.index!=0) {
dvander@27
  4911
            tagsym=find_tag_byval(ref->x.tags.index);
dvander@27
  4912
            assert(tagsym!=NULL);
dvander@27
  4913
            fprintf(log,"\t\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
dvander@27
  4914
          } /* if */
dvander@27
  4915
          if (ref->dim.array.length!=1)
dvander@27
  4916
            fprintf(log,"\t\t\t\t<size value=\"%ld\"/>\n",(long)ref->dim.array.length);
dvander@27
  4917
        } /* if */
dvander@27
  4918
        fprintf(log,"\t\t\t</member>\n");
dvander@27
  4919
        enumroot=enumroot->next;
dvander@27
  4920
      } /* while */
dvander@27
  4921
    } /* if */
dvander@27
  4922
    assert(sym->refer!=NULL);
dvander@27
  4923
    for (i=0; i<sym->numrefers; i++) {
dvander@27
  4924
      if ((ref=sym->refer[i])!=NULL)
dvander@27
  4925
        fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
dvander@27
  4926
    } /* for */
dvander@27
  4927
    if (sym->documentation!=NULL)
dvander@27
  4928
      fprintf(log,"\t\t\t%s\n",sym->documentation);
dvander@27
  4929
    fprintf(log,"\t\t</member>\n");
dvander@27
  4930
  } /* for */
dvander@27
  4931
dvander@27
  4932
  fprintf(log,"\n\t\t<!-- constants -->\n");
dvander@27
  4933
  for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  4934
    if (sym->parent!=NULL)
dvander@27
  4935
      continue;                 /* hierarchical data type */
dvander@27
  4936
    assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
dvander@27
  4937
           || sym->ident==iARRAY || sym->ident==iFUNCTN);
dvander@27
  4938
    if (sym->ident!=iCONSTEXPR)
dvander@27
  4939
      continue;
dvander@27
  4940
    if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0)
dvander@27
  4941
      continue;
dvander@27
  4942
    fprintf(log,"\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
dvander@27
  4943
    if (sym->tag!=0) {
dvander@27
  4944
      tagsym=find_tag_byval(sym->tag);
dvander@27
  4945
      assert(tagsym!=NULL);
dvander@27
  4946
      fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
dvander@27
  4947
    } /* if */
dvander@27
  4948
    assert(sym->refer!=NULL);
dvander@27
  4949
    for (i=0; i<sym->numrefers; i++) {
dvander@27
  4950
      if ((ref=sym->refer[i])!=NULL)
dvander@27
  4951
        fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
dvander@27
  4952
    } /* for */
dvander@27
  4953
    if (sym->documentation!=NULL)
dvander@27
  4954
      fprintf(log,"\t\t\t%s\n",sym->documentation);
dvander@27
  4955
    fprintf(log,"\t\t</member>\n");
dvander@27
  4956
  } /* for */
dvander@27
  4957
dvander@27
  4958
  fprintf(log,"\n\t\t<!-- variables -->\n");
dvander@27
  4959
  for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  4960
    if (sym->parent!=NULL)
dvander@27
  4961
      continue;                 /* hierarchical data type */
dvander@27
  4962
    if (sym->ident!=iVARIABLE && sym->ident!=iARRAY)
dvander@27
  4963
      continue;
dvander@27
  4964
    fprintf(log,"\t\t<member name=\"F:%s\">\n",funcdisplayname(symname,sym->name));
dvander@27
  4965
    if (sym->tag!=0) {
dvander@27
  4966
      tagsym=find_tag_byval(sym->tag);
dvander@27
  4967
      assert(tagsym!=NULL);
dvander@27
  4968
      fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
dvander@27
  4969
    } /* if */
dvander@27
  4970
    assert(sym->refer!=NULL);
dvander@27
  4971
    if ((sym->usage & uPUBLIC)!=0)
dvander@27
  4972
      fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
dvander@27
  4973
    for (i=0; i<sym->numrefers; i++) {
dvander@27
  4974
      if ((ref=sym->refer[i])!=NULL)
dvander@27
  4975
        fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
dvander@27
  4976
    } /* for */
dvander@27
  4977
    if (sym->documentation!=NULL)
dvander@27
  4978
      fprintf(log,"\t\t\t%s\n",sym->documentation);
dvander@27
  4979
    fprintf(log,"\t\t</member>\n");
dvander@27
  4980
  } /* for */
dvander@27
  4981
dvander@27
  4982
  fprintf(log,"\n\t\t<!-- functions -->\n");
dvander@27
  4983
  for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  4984
    if (sym->parent!=NULL)
dvander@27
  4985
      continue;                 /* hierarchical data type */
dvander@27
  4986
    if (sym->ident!=iFUNCTN)
dvander@27
  4987
      continue;
dvander@27
  4988
    if ((sym->usage & (uREAD | uNATIVE))==uNATIVE)
dvander@27
  4989
      continue;                 /* unused native function */
dvander@27
  4990
    funcdisplayname(symname,sym->name);
dvander@27
  4991
    xmlencode(symname,symname);
dvander@27
  4992
    fprintf(log,"\t\t<member name=\"M:%s\" syntax=\"%s(",symname,symname);
dvander@27
  4993
    /* print only the names of the parameters between the parentheses */
dvander@27
  4994
    assert(sym->dim.arglist!=NULL);
dvander@27
  4995
    for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
dvander@27
  4996
      int dim;
dvander@27
  4997
      if (arg>0)
dvander@27
  4998
        fprintf(log,", ");
dvander@27
  4999
      switch (sym->dim.arglist[arg].ident) {
dvander@27
  5000
      case iVARIABLE:
dvander@27
  5001
        fprintf(log,"%s",sym->dim.arglist[arg].name);
dvander@27
  5002
        break;
dvander@27
  5003
      case iREFERENCE:
dvander@27
  5004
        fprintf(log,"&amp;%s",sym->dim.arglist[arg].name);
dvander@27
  5005
        break;
dvander@27
  5006
      case iREFARRAY:
dvander@27
  5007
        fprintf(log,"%s",sym->dim.arglist[arg].name);
dvander@27
  5008
        for (dim=0; dim<sym->dim.arglist[arg].numdim;dim++)
dvander@27
  5009
          fprintf(log,"[]");
dvander@27
  5010
        break;
dvander@27
  5011
      case iVARARGS:
dvander@27
  5012
        fprintf(log,"...");
dvander@27
  5013
        break;
dvander@27
  5014
      } /* switch */
dvander@27
  5015
    } /* for */
dvander@27
  5016
    /* ??? should also print an "array return" size */
dvander@27
  5017
    fprintf(log,")\">\n");
dvander@27
  5018
    if (sym->tag!=0) {
dvander@27
  5019
      tagsym=find_tag_byval(sym->tag);
dvander@27
  5020
      assert(tagsym!=NULL);
dvander@27
  5021
      fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
dvander@27
  5022
    } /* if */
dvander@27
  5023
    /* check whether this function is called from the outside */
dvander@27
  5024
    if ((sym->usage & uNATIVE)!=0)
dvander@27
  5025
      fprintf(log,"\t\t\t<attribute name=\"native\"/>\n");
dvander@27
  5026
    if ((sym->usage & uPUBLIC)!=0)
dvander@27
  5027
      fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
dvander@27
  5028
    if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0)
dvander@27
  5029
      fprintf(log,"\t\t\t<attribute name=\"entry\"/>\n");
dvander@27
  5030
    if ((sym->usage & uNATIVE)==0)
dvander@27
  5031
      fprintf(log,"\t\t\t<stacksize value=\"%ld\"/>\n",(long)sym->x.stacksize);
dvander@27
  5032
    if (sym->states!=NULL) {
dvander@27
  5033
      constvalue *stlist=sym->states->next;
dvander@27
  5034
      assert(stlist!=NULL);     /* there should be at least one state item */
dvander@27
  5035
      while (stlist!=NULL && stlist->index==-1)
dvander@27
  5036
        stlist=stlist->next;
dvander@27
  5037
      assert(stlist!=NULL);     /* state id should be found */
dvander@27
  5038
      i=state_getfsa(stlist->index);
dvander@27
  5039
      assert(i>=0);             /* automaton 0 exists */
dvander@27
  5040
      stlist=automaton_findid(i);
dvander@27
  5041
      assert(stlist!=NULL);     /* automaton should be found */
dvander@27
  5042
      fprintf(log,"\t\t\t<automaton name=\"%s\"/>\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)");
dvander@27
  5043
      //??? dump state decision table
dvander@27
  5044
    } /* if */
dvander@27
  5045
    assert(sym->refer!=NULL);
dvander@27
  5046
    for (i=0; i<sym->numrefers; i++)
dvander@27
  5047
      if ((ref=sym->refer[i])!=NULL)
dvander@27
  5048
        fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
dvander@27
  5049
    /* print all symbols that are required for this function to compile */
dvander@27
  5050
    for (ref=root->next; ref!=NULL; ref=ref->next) {
dvander@27
  5051
      if (ref==sym)
dvander@27
  5052
        continue;
dvander@27
  5053
      for (i=0; i<ref->numrefers; i++)
dvander@27
  5054
        if (ref->refer[i]==sym)
dvander@27
  5055
          fprintf(log,"\t\t\t<dependency name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
dvander@27
  5056
    } /* for */
dvander@27
  5057
    /* print parameter list, with tag & const information, plus descriptions */
dvander@27
  5058
    assert(sym->dim.arglist!=NULL);
dvander@27
  5059
    for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
dvander@27
  5060
      int dim,paraminfo;
dvander@27
  5061
      char *outer_start,*inner_start;
dvander@27
  5062
      int outer_length,inner_length;
dvander@27
  5063
      if (sym->dim.arglist[arg].ident==iVARARGS)
dvander@27
  5064
        fprintf(log,"\t\t\t<param name=\"...\">\n");
dvander@27
  5065
      else
dvander@27
  5066
        fprintf(log,"\t\t\t<param name=\"%s\">\n",sym->dim.arglist[arg].name);
dvander@27
  5067
      /* print the tag name(s) for each parameter */
dvander@27
  5068
      assert(sym->dim.arglist[arg].numtags>0);
dvander@27
  5069
      assert(sym->dim.arglist[arg].tags!=NULL);
dvander@27
  5070
      paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0)
dvander@27
  5071
                || sym->dim.arglist[arg].ident==iREFERENCE
dvander@27
  5072
                || sym->dim.arglist[arg].ident==iREFARRAY;
dvander@27
  5073
      if (paraminfo)
dvander@27
  5074
        fprintf(log,"\t\t\t\t<paraminfo>");
dvander@27
  5075
      if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) {
dvander@27
  5076
        assert(paraminfo);
dvander@27
  5077
        if (sym->dim.arglist[arg].numtags>1)
dvander@27
  5078
          fprintf(log," {");
dvander@27
  5079
        for (i=0; i<sym->dim.arglist[arg].numtags; i++) {
dvander@27
  5080
          if (i>0)
dvander@27
  5081
            fprintf(log,",");
dvander@27
  5082
          tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]);
dvander@27
  5083
          assert(tagsym!=NULL);
dvander@27
  5084
          fprintf(log,"%s",tagsym->name);
dvander@27
  5085
        } /* for */
dvander@27
  5086
        if (sym->dim.arglist[arg].numtags>1)
dvander@27
  5087
          fprintf(log,"}");
dvander@27
  5088
      } /* if */
dvander@27
  5089
      switch (sym->dim.arglist[arg].ident) {
dvander@27
  5090
      case iREFERENCE:
dvander@27
  5091
        fprintf(log," &amp;");
dvander@27
  5092
        break;
dvander@27
  5093
      case iREFARRAY:
dvander@27
  5094
        fprintf(log," ");
dvander@27
  5095
        for (dim=0; dim<sym->dim.arglist[arg].numdim; dim++) {
dvander@27
  5096
          if (sym->dim.arglist[arg].dim[dim]==0) {
dvander@27
  5097
            fprintf(log,"[]");
dvander@27
  5098
          } else {
dvander@27
  5099
            //??? find index tag
dvander@27
  5100
            fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]);
dvander@27
  5101
          } /* if */
dvander@27
  5102
        } /* for */
dvander@27
  5103
        break;
dvander@27
  5104
      } /* switch */
dvander@27
  5105
      if (paraminfo)
dvander@27
  5106
        fprintf(log," </paraminfo>\n");
dvander@27
  5107
      /* print the user description of the parameter (parse through
dvander@27
  5108
       * sym->documentation)
dvander@27
  5109
       */
dvander@27
  5110
      if (sym->documentation!=NULL
dvander@27
  5111
          && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name,
dvander@27
  5112
                         &outer_start, &outer_length, &inner_start, &inner_length))
dvander@27
  5113
      {
dvander@27
  5114
        char *tail;
dvander@27
  5115
        fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start);
dvander@27
  5116
        /* delete from documentation string */
dvander@27
  5117
        tail=outer_start+outer_length;
dvander@27
  5118
        memmove(outer_start,tail,strlen(tail)+1);
dvander@27
  5119
      } /* if */
dvander@27
  5120
      fprintf(log,"\t\t\t</param>\n");
dvander@27
  5121
    } /* for */
dvander@27
  5122
    if (sym->documentation!=NULL)
dvander@27
  5123
      fprintf(log,"\t\t\t%s\n",sym->documentation);
dvander@27
  5124
    fprintf(log,"\t\t</member>\n");
dvander@27
  5125
  } /* for */
dvander@27
  5126
dvander@27
  5127
  fprintf(log,"\n\t</members>\n");
dvander@27
  5128
  fprintf(log,"</doc>\n");
dvander@27
  5129
}
dvander@27
  5130
#endif
dvander@27
  5131
dvander@27
  5132
/* Every symbol has a referrer list, that contains the functions that use
dvander@27
  5133
 * the symbol. Now, if function "apple" is accessed by functions "banana" and
dvander@27
  5134
 * "citron", but neither function "banana" nor "citron" are used by anyone
dvander@27
  5135
 * else, then, by inference, function "apple" is not used either.
dvander@27
  5136
 */
dvander@27
  5137
static void reduce_referrers(symbol *root)
dvander@27
  5138
{
dvander@27
  5139
  int i,restart;
dvander@27
  5140
  symbol *sym,*ref;
dvander@27
  5141
dvander@27
  5142
  do {
dvander@27
  5143
    restart=0;
dvander@27
  5144
    for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  5145
      if (sym->parent!=NULL)
dvander@27
  5146
        continue;                 /* hierarchical data type */
dvander@27
  5147
      if (sym->ident==iFUNCTN
dvander@27
  5148
          && (sym->usage & uNATIVE)==0
dvander@27
  5149
          && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0
dvander@27
  5150
          && count_referrers(sym)==0)
dvander@27
  5151
      {
dvander@27
  5152
        sym->usage&=~(uREAD | uWRITTEN);  /* erase usage bits if there is no referrer */
dvander@27
  5153
        /* find all symbols that are referred by this symbol */
dvander@27
  5154
        for (ref=root->next; ref!=NULL; ref=ref->next) {
dvander@27
  5155
          if (ref->parent!=NULL)
dvander@27
  5156
            continue;             /* hierarchical data type */
dvander@27
  5157
          assert(ref->refer!=NULL);
dvander@27
  5158
          for (i=0; i<ref->numrefers && ref->refer[i]!=sym; i++)
dvander@27
  5159
            /* nothing */;
dvander@27
  5160
          if (i<ref->numrefers) {
dvander@27
  5161
            assert(ref->refer[i]==sym);
dvander@27
  5162
            ref->refer[i]=NULL;
dvander@27
  5163
            restart++;
dvander@27
  5164
          } /* if */
dvander@27
  5165
        } /* for */
dvander@27
  5166
      } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY)
dvander@27
  5167
                 && (sym->usage & uPUBLIC)==0
dvander@27
  5168
                 && sym->parent==NULL
dvander@27
  5169
                 && count_referrers(sym)==0)
dvander@27
  5170
      {
dvander@27
  5171
        sym->usage&=~(uREAD | uWRITTEN);  /* erase usage bits if there is no referrer */
dvander@27
  5172
      } /* if */
dvander@27
  5173
    } /* for */
dvander@27
  5174
    /* after removing a symbol, check whether more can be removed */
dvander@27
  5175
  } while (restart>0);
dvander@27
  5176
}
dvander@27
  5177
dvander@27
  5178
#if !defined SC_LIGHT
dvander@27
  5179
static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion)
dvander@27
  5180
{
dvander@27
  5181
  long size,maxsize;
dvander@27
  5182
  int i;
dvander@27
  5183
dvander@27
  5184
  assert(sym!=NULL);
dvander@27
  5185
  assert(sym->ident==iFUNCTN);
dvander@27
  5186
  assert((sym->usage & uNATIVE)==0);
dvander@27
  5187
  assert(recursion!=NULL);
dvander@27
  5188
dvander@27
  5189
  maxsize=sym->x.stacksize;
dvander@27
  5190
  for (i=0; i<sym->numrefers; i++) {
dvander@27
  5191
    if (sym->refer[i]!=NULL) {
dvander@27
  5192
      assert(sym->refer[i]->ident==iFUNCTN);
dvander@27
  5193
      assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */
dvander@27
  5194
      if (sym->refer[i]==sourcesym) {   /* recursion detection */
dvander@27
  5195
        *recursion=1;
dvander@27
  5196
        break;                          /* recursion was detected, quit loop */
dvander@27
  5197
      } /* if */
dvander@27
  5198
      size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion);
dvander@27
  5199
      if (maxsize<size)
dvander@27
  5200
        maxsize=size;
dvander@27
  5201
    } /* if */
dvander@27
  5202
  } /* for */
dvander@27
  5203
dvander@27
  5204
  if ((sym->usage & uPUBLIC)!=0) {
dvander@27
  5205
    /* Find out how many parameters a public function has, then see if this
dvander@27
  5206
     * is bigger than some maximum
dvander@27
  5207
     */
dvander@27
  5208
    arginfo *arg=sym->dim.arglist;
dvander@27
  5209
    int count=0;
dvander@27
  5210
    assert(arg!=0);
dvander@27
  5211
    while (arg->ident!=0) {
dvander@27
  5212
      count++;
dvander@27
  5213
      arg++;
dvander@27
  5214
    } /* while */
dvander@27
  5215
    assert(pubfuncparams!=0);
dvander@27
  5216
    if (count>*pubfuncparams)
dvander@27
  5217
      *pubfuncparams=count;
dvander@27
  5218
  } /* if */
dvander@27
  5219
dvander@27
  5220
  return maxsize+basesize;
dvander@27
  5221
}
dvander@27
  5222
dvander@1164
  5223
static symbol *save_symbol;
dvander@1164
  5224
dvander@27
  5225
static long max_stacksize(symbol *root,int *recursion)
dvander@27
  5226
{
dvander@27
  5227
  /* Loop over all non-native functions. For each function, loop
dvander@27
  5228
   * over all of its referrers, accumulating the stack requirements.
dvander@27
  5229
   * Detect (indirect) recursion with a "mark-and-sweep" algorithm.
dvander@27
  5230
   * I (mis-)use the "compound" field of the symbol structure for
dvander@27
  5231
   * the marker, as this field is unused for functions.
dvander@27
  5232
   *
dvander@27
  5233
   * Note that the stack is shared with the heap. A host application
dvander@27
  5234
   * may "eat" cells from the heap as well, through amx_Allot(). The
dvander@27
  5235
   * stack requirements are thus only an estimate.
dvander@27
  5236
   */
dvander@27
  5237
  long size,maxsize;
dvander@27
  5238
  int maxparams;
dvander@27
  5239
  symbol *sym;
dvander@27
  5240
dvander@27
  5241
  assert(root!=NULL);
dvander@27
  5242
  assert(recursion!=NULL);
dvander@27
  5243
  #if !defined NDEBUG
dvander@27
  5244
    for (sym=root->next; sym!=NULL; sym=sym->next)
dvander@27
  5245
      if (sym->ident==iFUNCTN)
dvander@27
  5246
        assert(sym->compound==0);
dvander@27
  5247
  #endif
dvander@27
  5248
dvander@27
  5249
  maxsize=0;
dvander@27
  5250
  maxparams=0;
dvander@27
  5251
  *recursion=0;         /* assume no recursion */
dvander@27
  5252
  for (sym=root->next; sym!=NULL; sym=sym->next) {
dvander@27
  5253
    /* drop out if this is not a user-implemented function */
dvander@27
  5254
    if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0)
dvander@27
  5255
      continue;
dvander@27
  5256
    /* accumulate stack size for this symbol */
dvander@1164
  5257
	save_symbol = sym;
dvander@27
  5258
    size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion);
dvander@27
  5259
    assert(size>=0);
dvander@27
  5260
    if (maxsize<size)
dvander@27
  5261
      maxsize=size;
dvander@27
  5262
  } /* for */
dvander@27
  5263
dvander@27
  5264
  maxsize++;                  /* +1 because a zero cell is always pushed on top
dvander@27
  5265
                               * of the stack to catch stack overwrites */
dvander@27
  5266
  return maxsize+(maxparams+1);/* +1 because # of parameters is always pushed on entry */
dvander@27
  5267
}
dvander@27
  5268
#endif
dvander@27
  5269
dvander@27
  5270
/*  testsymbols - test for unused local or global variables
dvander@27
  5271
 *
dvander@27
  5272
 *  "Public" functions are excluded from the check, since these
dvander@27
  5273
 *  may be exported to other object modules.
dvander@27
  5274
 *  Labels are excluded from the check if the argument 'testlabs'
dvander@27
  5275
 *  is 0. Thus, labels are not tested until the end of the function.
dvander@27
  5276
 *  Constants may also be excluded (convenient for global constants).
dvander@27
  5277
 *
dvander@27
  5278
 *  When the nesting level drops below "level", the check stops.
dvander@27
  5279
 *
dvander@27
  5280
 *  The function returns whether there is an "entry" point for the file.
dvander@27
  5281
 *  This flag will only be 1 when browsing the global symbol table.
dvander@27
  5282
 */
dvander@27
  5283
static int testsymbols(symbol *root,int level,int testlabs,int testconst)
dvander@27
  5284
{
dvander@27
  5285
  char symname[2*sNAMEMAX+16];
dvander@27
  5286
  int entry=FALSE;
dvander@27
  5287
dvander@27
  5288
  symbol *sym=root->next;
dvander@27
  5289
  while (sym!=NULL && sym->compound>=level) {
dvander@27
  5290
    switch (sym->ident) {
dvander@27
  5291
    case iLABEL:
dvander@27
  5292
      if (testlabs) {
dvander@27
  5293
        if ((sym->usage & uDEFINE)==0) {
dvander@27
  5294
          error(19,sym->name);      /* not a label: ... */
dvander@27
  5295
        } else if ((sym->usage & uREAD)==0) {
dvander@27
  5296
          errorset(sSETPOS,sym->lnumber);
dvander@27
  5297
          error(203,sym->name);     /* symbol isn't used: ... */
dvander@27
  5298
        } /* if */
dvander@27
  5299
      } /* if */
dvander@27
  5300
      break;
dvander@27
  5301
    case iFUNCTN:
dvander@27
  5302
      if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) {
dvander@27
  5303
        funcdisplayname(symname,sym->name);
dvander@27
  5304
        if (strlen(symname)>0)
dvander@27
  5305
          error(203,symname);       /* symbol isn't used ... (and not public/native/stock) */
dvander@27
  5306
      } /* if */
dvander@27
  5307
      if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0)
dvander@27
  5308
        entry=TRUE;                 /* there is an entry point */
dvander@27
  5309
      /* also mark the function to the debug information */
dvander@27
  5310
      if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0)
dvander@27
  5311
        insert_dbgsymbol(sym);
dvander@27
  5312
      break;
dvander@27
  5313
    case iCONSTEXPR:
dvander@27
  5314
      if (testconst && (sym->usage & uREAD)==0) {
dvander@27
  5315
        errorset(sSETPOS,sym->lnumber);
dvander@27
  5316
        error(203,sym->name);       /* symbol isn't used: ... */
dvander@27
  5317
      } /* if */
dvander@27
  5318
      break;
dvander@27
  5319
    default:
dvander@27
  5320
      /* a variable */
dvander@27
  5321
      if (sym->parent!=NULL)
dvander@27
  5322
        break;                      /* hierarchical data type */
dvander@27
  5323
      if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) {
ds@876
  5324
        errorset(sSETPOS,sym->lnumber);
ds@876
  5325
        error(203,sym->name);  /* symbol isn't used (and not stock) */
dvander@27
  5326
      } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) {
dvander@27
  5327
        errorset(sSETPOS,sym->lnumber);
dvander@27
  5328
        error(204,sym->name);       /* value assigned to symbol is never used */
dvander@27
  5329
#if 0 // ??? not sure whether it is a good idea to force people use "const"
dvander@27
  5330
      } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) {
dvander@27
  5331
        errorset(sSETPOS,sym->lnumber);
dvander@27
  5332
        error(214,sym->name);       /* make array argument "const" */
dvander@27
  5333
#endif
dvander@27
  5334
      } /* if */
dvander@27
  5335
      /* also mark the variable (local or global) to the debug information */
dvander@27
  5336
      if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0)
dvander@27
  5337
        insert_dbgsymbol(sym);
dvander@27
  5338
    } /* if */
dvander@27
  5339
    sym=sym->next;
dvander@27
  5340
  } /* while */
dvander@27
  5341
dvander@27
  5342
  return entry;
dvander@27
  5343
}
dvander@27
  5344
dvander@27
  5345
static cell calc_array_datasize(symbol *sym, cell *offset)
dvander@27
  5346
{
dvander@27
  5347
  cell length;
dvander@27
  5348
dvander@27
  5349
  assert(sym!=NULL);
dvander@27
  5350
  assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
dvander@27
  5351
  length=sym->dim.array.length;
dvander@27
  5352
  if (sym->dim.array.level > 0) {
dvander@27
  5353
    cell sublength=calc_array_datasize(finddepend(sym),offset);
dvander@27
  5354
    if (offset!=NULL)
dvander@27
  5355
      *offset=length*(*offset+sizeof(cell));
dvander@27
  5356
    if (sublength>0)
dvander@27
  5357
      length*=length*sublength;
dvander@27
  5358
    else
dvander@27
  5359
      length=0;
dvander@27
  5360
  } else {
dvander@27
  5361
    if (offset!=NULL)
dvander@27
  5362
      *offset=0;
dvander@27
  5363
  } /* if */
dvander@27
  5364
  return length;
dvander@27
  5365
}
dvander@27
  5366
dvander@27
  5367
static void destructsymbols(symbol *root,int level)
dvander@27
  5368
{
dvander@27
  5369
  cell offset=0;
dvander@27
  5370
  int savepri=FALSE;
dvander@27
  5371
  symbol *sym=root->next;
dvander@27
  5372
  while (sym!=NULL && sym->compound>=level) {
dvander@27
  5373
    if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
dvander@27
  5374
      char symbolname[16];
dvander@27
  5375
      symbol *opsym;
dvander@27
  5376
      cell elements;
dvander@27
  5377
      /* check that the '~' operator is defined for this tag */
dvander@27
  5378
      operator_symname(symbolname,"~",sym->tag,0,1,0);
dvander@27
  5379
      if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) {
dvander@27
  5380
        /* save PRI, in case of a return statment */
dvander@27
  5381
        if (!savepri) {
dvander@27
  5382
          pushreg(sPRI);        /* right-hand operand is in PRI */
dvander@27
  5383
          savepri=TRUE;
dvander@27
  5384
        } /* if */
dvander@27
  5385
        /* if the variable is an array, get the number of elements */
dvander@27
  5386
        if (sym->ident==iARRAY) {
dvander@27
  5387
          elements=calc_array_datasize(sym,&offset);
dvander@27
  5388
          /* "elements" can be zero when the variable is declared like
dvander@27
  5389
           *    new mytag: myvar[2][] = { {1, 2}, {3, 4} }
dvander@27
  5390
           * one should declare all dimensions!
dvander@27
  5391
           */
dvander@27
  5392
          if (elements==0)
dvander@27
  5393
            error(46,sym->name);        /* array size is unknown */
dvander@27
  5394
        } else {
dvander@27
  5395
          elements=1;
dvander@27
  5396
          offset=0;
dvander@27
  5397
        } /* if */
dvander@27
  5398
        pushval(elements);
dvander@27
  5399
        /* call the '~' operator */
dvander@27
  5400
        address(sym,sPRI);
dvander@27
  5401
        addconst(offset);       /* add offset to array data to the address */
dvander@27
  5402
        pushreg(sPRI);
dvander@101
  5403
        pushval(2 /* *sizeof(cell)*/ );/* 2 parameters */
dvander@27
  5404
        assert(opsym->ident==iFUNCTN);
dvander@27
  5405
        ffcall(opsym,NULL,1);
dvander@27
  5406
        if (sc_status!=statSKIP)
dvander@27
  5407
          markusage(opsym,uREAD);   /* do not mark as "used" when this call itself is skipped */
dvander@27
  5408
        if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL)
dvander@27
  5409
          opsym->x.lib->value += 1; /* increment "usage count" of the library */
dvander@27
  5410
      } /* if */
dvander@27
  5411
    } /* if */
dvander@27
  5412
    sym=sym->next;
dvander@27
  5413
  } /* while */
dvander@27
  5414
  /* restore PRI, if it was saved */
dvander@27
  5415
  if (savepri)
dvander@27
  5416
    popreg(sPRI);
dvander@27
  5417
}
dvander@27
  5418
dvander@27
  5419
static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,
dvander@27
  5420
                                   int index)
dvander@27
  5421
{
dvander@27
  5422
  constvalue *cur;
dvander@27
  5423
dvander@27
  5424
  if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL)
ds@841
  5425
    error(123);       /* insufficient memory (fatal error) */
dvander@27
  5426
  memset(cur,0,sizeof(constvalue));
dvander@27
  5427
  if (name!=NULL) {
dvander@27
  5428
    assert(strlen(name)<sNAMEMAX);
dvander@27
  5429
    strcpy(cur->name,name);
dvander@27
  5430
  } /* if */
dvander@27
  5431
  cur->value=val;
dvander@27
  5432
  cur->index=index;
dvander@27
  5433
  cur->next=next;
dvander@27
  5434
  prev->next=cur;
dvander@27
  5435
  return cur;
dvander@27
  5436
}
dvander@27
  5437
dvander@27
  5438
SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index)
dvander@27
  5439
{
dvander@27
  5440
  constvalue *cur,*prev;
dvander@27
  5441
dvander@27
  5442
  /* find the end of the constant table */
dvander@27
  5443
  for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next)
dvander@27
  5444
    /* nothing */;
dvander@27
  5445
  return insert_constval(prev,NULL,name,val,index);
dvander@27
  5446
}
dvander@27
  5447
dvander@27
  5448
SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index)
dvander@27
  5449
{
dvander@27
  5450
  constvalue *ptr = table->next;
dvander@27
  5451
dvander@27
  5452
  while (ptr!=NULL) {
dvander@27
  5453
    if (strcmp(name,ptr->name)==0 && ptr->index==index)
dvander@27
  5454
      return ptr;
dvander@27
  5455
    ptr=ptr->next;
dvander@27
  5456
  } /* while */
dvander@27
  5457
  return NULL;
dvander@27
  5458
}
dvander@27
  5459
dvander@27
  5460
static constvalue *find_constval_byval(constvalue *table,cell val)
dvander@27
  5461
{
dvander@27
  5462
  constvalue *ptr = table->next;
dvander@27
  5463
dvander@27
  5464
  while (ptr!=NULL) {
dvander@27
  5465
    if (ptr->value==val)
dvander@27
  5466
      return ptr;
dvander@27
  5467
    ptr=ptr->next;
dvander@27
  5468
  } /* while */
dvander@27
  5469
  return NULL;
dvander@27
  5470
}
dvander@27
  5471
dvander@27
  5472
#if 0   /* never used */
dvander@27
  5473
static int delete_constval(constvalue *table,char *name)
dvander@27
  5474
{
dvander@27
  5475
  constvalue *prev = table;
dvander@27
  5476
  constvalue *cur = prev->next;
dvander@27
  5477
dvander@27
  5478
  while (cur!=NULL) {
dvander@27
  5479
    if (strcmp(name,cur->name)==0) {
dvander@27
  5480
      prev->next=cur->next;
dvander@27
  5481
      free(cur);
dvander@27
  5482
      return TRUE;
dvander@27
  5483
    } /* if */
dvander@27
  5484
    prev=cur;
dvander@27
  5485
    cur=cur->next;
dvander@27
  5486
  } /* while */
dvander@27
  5487
  return FALSE;
dvander@27
  5488
}
dvander@27
  5489
#endif
dvander@27
  5490
dvander@27
  5491
SC_FUNC void delete_consttable(constvalue *table)
dvander@27
  5492
{
dvander@27
  5493
  constvalue *cur=table->next, *next;
dvander@27
  5494
dvander@27
  5495
  while (cur!=NULL) {
dvander@27
  5496
    next=cur->next;
dvander@27
  5497
    free(cur);
dvander@27
  5498
    cur=next;
dvander@27
  5499
  } /* while */
dvander@27
  5500
  memset(table,0,sizeof(constvalue));
dvander@27
  5501
}
dvander@27
  5502
dvander@27
  5503
/*  add_constant
dvander@27
  5504
 *
dvander@27
  5505
 *  Adds a symbol to the symbol table. Returns NULL on failure.
dvander@27
  5506
 */
dvander@27
  5507
SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag)
dvander@27
  5508
{
dvander@27
  5509
  symbol *sym;
dvander@27
  5510
dvander@27
  5511
  /* Test whether a global or local symbol with the same name exists. Since
dvander@27
  5512
   * constants are stored in the symbols table, this also finds previously
dvander@27
  5513
   * defind constants. */
dvander@27
  5514
  sym=findglb(name,sSTATEVAR);
dvander@27
  5515
  if (!sym)
dvander@27
  5516
    sym=findloc(name);
dvander@27
  5517
  if (sym) {
dvander@27
  5518
    int redef=0;
dvander@27
  5519
    if (sym->ident!=iCONSTEXPR)
dvander@27
  5520
      redef=1;                  /* redefinition a function/variable to a constant is not allowed */
dvander@27
  5521
    if ((sym->usage & uENUMFIELD)!=0) {
dvander@27
  5522
      /* enum field, special case if it has a different tag and the new symbol is also an enum field */
dvander@27
  5523
      constvalue *tagid;
dvander@27
  5524
      symbol *tagsym;
dvander@27
  5525
      if (sym->tag==tag)
dvander@27
  5526
        redef=1;                /* enumeration field is redefined (same tag) */
dvander@27
  5527
      tagid=find_tag_byval(tag);
dvander@27
  5528
      if (tagid==NULL) {
dvander@27
  5529
        redef=1;                /* new constant does not have a tag */
dvander@27
  5530
      } else {
dvander@27
  5531
        tagsym=findconst(tagid->name,NULL);
dvander@27
  5532
        if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0)
dvander@27
  5533
          redef=1;              /* new constant is not an enumeration field */
dvander@27
  5534
      } /* if */
dvander@27
  5535
      /* in this particular case (enumeration field that is part of a different
dvander@27
  5536
       * enum, and non-conflicting with plain constants) we want to be able to
dvander@27
  5537
       * redefine it
dvander@27
  5538
       */
dvander@27
  5539
      if (!redef)
dvander@27
  5540
        goto redef_enumfield;
dvander@27
  5541
    } else if (sym->tag!=tag) {
dvander@27
  5542
      redef=1;                  /* redefinition of a constant (non-enum) to a different tag is not allowed */
dvander@27
  5543
    } /* if */
dvander@27
  5544
    if (redef) {
dvander@27
  5545
      error(21,name);           /* symbol already defined */
dvander@27
  5546
      return NULL;
dvander@27
  5547
    } else if (sym->addr!=val) {
dvander@27
  5548
      error(201,name);          /* redefinition of constant (different value) */
dvander@27
  5549
      sym->addr=val;            /* set new value */
dvander@27
  5550
    } /* if */
dvander@27
  5551
    /* silently ignore redefinitions of constants with the same value & tag */
dvander@27
  5552
    return sym;
dvander@27
  5553
  } /* if */
dvander@27
  5554
dvander@27
  5555
  /* constant doesn't exist yet (or is allowed to be redefined) */
dvander@27
  5556
redef_enumfield:
dvander@27
  5557
  sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE);
dvander@27
  5558
  assert(sym!=NULL);            /* fatal error 103 must be given on error */
dvander@27
  5559
  if (sc_status == statIDLE)
dvander@27
  5560
    sym->usage |= uPREDEF;
dvander@27
  5561
  return sym;
dvander@27
  5562
}
dvander@27
  5563
dvander@27
  5564
/*  statement           - The Statement Parser
dvander@27
  5565
 *
dvander@27
  5566
 *  This routine is called whenever the parser needs to know what statement
dvander@27
  5567
 *  it encounters (i.e. whenever program syntax requires a statement).
dvander@27
  5568
 */
dvander@27
  5569
static void statement(int *lastindent,int allow_decl)
dvander@27
  5570
{
faluco@62
  5571
  int tok,save;
dvander@27
  5572
  cell val;
dvander@27
  5573
  char *st;
dvander@27
  5574
dvander@27
  5575
  if (!freading) {
dvander@27
  5576
    error(36);                  /* empty statement */
dvander@27
  5577
    return;
dvander@27
  5578
  } /* if */
dvander@27
  5579
  errorset(sRESET,0);
dvander@27
  5580
dvander@27
  5581
  tok=lex(&val,&st);
dvander@27
  5582
  if (tok!='{') {
dvander@27
  5583
    insert_dbgline(fline);
dvander@27
  5584
    setline(TRUE);
dvander@27
  5585
  } /* if */
dvander@27
  5586
  /* lex() has set stmtindent */
dvander@27
  5587
  if (lastindent!=NULL && tok!=tLABEL) {
dvander@27
  5588
    if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0)
dvander@27
  5589
      error(217);               /* loose indentation */
dvander@27
  5590
    *lastindent=stmtindent;
dvander@27
  5591
    indent_nowarn=FALSE;        /* if warning was blocked, re-enable it */
dvander@27
  5592
  } /* if */
dvander@27
  5593
  switch (tok) {
dvander@27
  5594
  case 0:
dvander@27
  5595
    /* nothing */
dvander@27
  5596
    break;
dvander@27
  5597
  case tNEW:
dvander@27
  5598
    if (allow_decl) {
dvander@27
  5599
      autozero=1;
dvander@27
  5600
      declloc(FALSE);
dvander@27
  5601
      lastst=tNEW;
dvander@27
  5602
    } else {
dvander@27
  5603
      error(3);                 /* declaration only valid in a block */
dvander@27
  5604
    } /* if */
dvander@27
  5605
    break;
dvander@27
  5606
  case tDECL:
dvander@27
  5607
    if (allow_decl) {
dvander@27
  5608
      autozero=0;
dvander@27
  5609
      declloc(FALSE);
dvander@27
  5610
      lastst=tDECL;
dvander@27
  5611
    } else {
dvander@27
  5612
      error(3);                 /* declaration only valid in a block */
dvander@27
  5613
    } /* if */
dvander@27
  5614
    break;
dvander@27
  5615
  case tSTATIC:
dvander@27
  5616
    if (allow_decl) {
dvander@27
  5617
      declloc(TRUE);
dvander@27
  5618
      lastst=tNEW;
dvander@27
  5619
    } else {
dvander@27
  5620
      error(3);                 /* declaration only valid in a block */
dvander@27
  5621
    } /* if */
dvander@27
  5622
    break;
dvander@27
  5623
  case '{':
faluco@62
  5624
  case tBEGIN:
faluco@62
  5625
    save=fline;
dvander@27
  5626
    if (!matchtoken('}'))       /* {} is the empty statement */
faluco@62
  5627
      compound(save==fline,tok);
dvander@27
  5628
    /* lastst (for "last statement") does not change */
dvander@27
  5629
    break;
dvander@27
  5630
  case ';':
dvander@27
  5631
    error(36);                  /* empty statement */
dvander@27
  5632
    break;
dvander@27
  5633
  case tIF:
faluco@62
  5634
    lastst=doif();
dvander@27
  5635
    break;
dvander@27
  5636
  case tWHILE:
faluco@62
  5637
    lastst=dowhile();
dvander@27
  5638
    break;
dvander@27
  5639
  case tDO:
faluco@62
  5640
    lastst=dodo();
dvander@27
  5641
    break;
dvander@27
  5642
  case tFOR:
faluco@62
  5643
    lastst=dofor();
dvander@27
  5644
    break;
dvander@27
  5645
  case tSWITCH:
dvander@27
  5646
    doswitch();
dvander@27
  5647
    lastst=tSWITCH;
dvander@27
  5648
    break;
dvander@27
  5649
  case tCASE:
dvander@27
  5650
  case tDEFAULT:
dvander@27
  5651
    error(14);     /* not in switch */
dvander@27
  5652
    break;
dvander@27
  5653
  case tGOTO:
dvander@27
  5654
    dogoto();
dvander@27
  5655
    lastst=tGOTO;
dvander@27
  5656
    break;
dvander@27
  5657
  case tLABEL:
dvander@27
  5658
    dolabel();
dvander@27
  5659
    lastst=tLABEL;
dvander@27
  5660
    break;
dvander@27
  5661
  case tRETURN:
dvander@27
  5662
    doreturn();
dvander@27
  5663
    lastst=tRETURN;
dvander@27
  5664
    break;
dvander@27
  5665
  case tBREAK:
dvander@27
  5666
    dobreak();
dvander@27
  5667
    lastst=tBREAK;
dvander@27
  5668
    break;
dvander@27
  5669
  case tCONTINUE:
dvander@27
  5670
    docont();
dvander@27
  5671
    lastst=tCONTINUE;
dvander@27
  5672
    break;
dvander@27
  5673
  case tEXIT:
dvander@27
  5674
    doexit();
dvander@27
  5675
    lastst=tEXIT;
dvander@27
  5676
    break;
dvander@27
  5677
  case tASSERT:
dvander@27
  5678
    doassert();
dvander@27
  5679
    lastst=tASSERT;
dvander@27
  5680
    break;
dvander@27
  5681
  case tSLEEP:
dvander@27
  5682
    dosleep();
dvander@27
  5683
    lastst=tSLEEP;
dvander@27
  5684
    break;
dvander@27
  5685
  case tSTATE:
dvander@27
  5686
    dostate();
dvander@27
  5687
    lastst=tSTATE;
dvander@27
  5688
    break;
dvander@27
  5689
  case tCONST:
dvander@27
  5690
    decl_const(sLOCAL);
dvander@27
  5691
    break;
dvander@27
  5692
  case tENUM:
dvander@27
  5693
    decl_enum(sLOCAL);
dvander@27
  5694
    break;
dvander@27
  5695
  default:          /* non-empty expression */
dvander@27
  5696
    sc_allowproccall=optproccall;
dvander@27
  5697
    lexpush();      /* analyze token later */
dvander@27
  5698
    doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);
dvander@27
  5699
    needtoken(tTERM);
dvander@27
  5700
    lastst=tEXPR;
dvander@27
  5701
    sc_allowproccall=FALSE;
dvander@27
  5702
  } /* switch */
dvander@27
  5703
}
dvander@27
  5704
faluco@62
  5705
static void compound(int stmt_sameline,int starttok)
dvander@27
  5706
{
dvander@27
  5707
  int indent=-1;
dvander@27
  5708
  cell save_decl=declared;
dvander@27
  5709
  int count_stmt=0;
dvander@27
  5710
  int block_start=fline;  /* save line where the compound block started */
faluco@62
  5711
  int endtok;
dvander@27
  5712
dvander@117
  5713
  pushstacklist();
dvander@118
  5714
  pushheaplist();
dvander@27
  5715
  /* if there is more text on this line, we should adjust the statement indent */
dvander@27
  5716
  if (stmt_sameline) {
dvander@27
  5717
    int i;
dvander@27
  5718
    const unsigned char *p=lptr;
dvander@27
  5719
    /* go back to the opening brace */
faluco@62
  5720
    while (*p!=starttok) {
dvander@27
  5721
      assert(p>pline);
dvander@27
  5722
      p--;
dvander@27
  5723
    } /* while */
faluco@62
  5724
    assert(*p==starttok);  /* it should be found */
dvander@27
  5725
    /* go forward, skipping white-space */
dvander@27
  5726
    p++;
dvander@27
  5727
    while (*p<=' ' && *p!='\0')
dvander@27
  5728
      p++;
dvander@27
  5729
    assert(*p!='\0'); /* a token should be found */
dvander@27
  5730
    stmtindent=0;
dvander@27
  5731
    for (i=0; i<(int)(p-pline); i++)
dvander@27
  5732
      if (pline[i]=='\t' && sc_tabsize>0)
dvander@27
  5733
        stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
dvander@27
  5734
      else
dvander@27
  5735
        stmtindent++;
dvander@27
  5736
  } /* if */
dvander@27
  5737
faluco@62
  5738
  endtok=(starttok=='{') ? '}' : tEND;
dvander@27
  5739
  nestlevel+=1;                 /* increase compound statement level */
faluco@62
  5740
  while (matchtoken(endtok)==0){/* repeat until compound statement is closed */
dvander@27
  5741
    if (!freading){
dvander@27
  5742
      error(30,block_start);    /* compound block not closed at end of file */
dvander@27
  5743
      break;
dvander@27
  5744
    } else {
faluco@62
  5745
      if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE || lastst==tENDLESS))
dvander@27
  5746
        error(225);             /* unreachable code */
dvander@27
  5747
      statement(&indent,TRUE);  /* do a statement */
dvander@27
  5748
      count_stmt++;
dvander@27
  5749
    } /* if */
dvander@27
  5750
  } /* while */
dvander@27
  5751
  if (lastst!=tRETURN)
dvander@27
  5752
    destructsymbols(&loctab,nestlevel);
dvander@117
  5753
  if (lastst!=tRETURN && lastst!=tGOTO) {
dvander@1472
  5754
    popheaplist();
dvander@459
  5755
    popstacklist(1);
dvander@459
  5756
  } else {
dvander@1472
  5757
    popheaplist();
dvander@459
  5758
    popstacklist(0);
dvander@117
  5759
  }
dvander@27
  5760
  testsymbols(&loctab,nestlevel,FALSE,TRUE);        /* look for unused block locals */
dvander@27
  5761
  declared=save_decl;
dvander@27
  5762
  delete_symbols(&loctab,nestlevel,FALSE,TRUE);     /* erase local symbols, but
dvander@27
  5763
                                                     * retain block local labels
dvander@27
  5764
                                                     * (within the function) */
dvander@27
  5765
  nestlevel-=1;                 /* decrease compound statement level */
dvander@27
  5766
}
dvander@27
  5767
dvander@27
  5768
/*  doexpr
dvander@27
  5769
 *
dvander@27
  5770
 *  Global references: stgidx   (referred to only)
dvander@27
  5771
 */
dvander@27
  5772
static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
dvander@27
  5773
                  int *tag,symbol **symptr,int chkfuncresult)
dvander@27
  5774
{
dvander@152
  5775
  return doexpr2(comma,chkeffect,allowarray,mark_endexpr,tag,symptr,chkfuncresult,NULL);
dvander@152
  5776
}
dvander@152
  5777
dvander@152
  5778
/*  doexpr2
dvander@152
  5779
 *
dvander@152
  5780
 *  Global references: stgidx   (referred to only)
dvander@152
  5781
 */
dvander@152
  5782
static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
dvander@152
  5783
                  int *tag,symbol **symptr,int chkfuncresult,value *lval)
dvander@152
  5784
{
dvander@27
  5785
  int index,ident;
dvander@27
  5786
  int localstaging=FALSE;
dvander@27
  5787
  cell val;
dvander@27
  5788
dvander@27
  5789
  if (!staging) {
dvander@27
  5790
    stgset(TRUE);               /* start stage-buffering */
dvander@27
  5791
    localstaging=TRUE;
dvander@27
  5792
    assert(stgidx==0);
dvander@27
  5793
  } /* if */
dvander@27
  5794
  index=stgidx;
dvander@27
  5795
  errorset(sEXPRMARK,0);
dvander@27
  5796
  do {
dvander@27
  5797
    /* on second round through, mark the end of the previous expression */
dvander@27
  5798
    if (index!=stgidx)
dvander@27
  5799
      markexpr(sEXPR,NULL,0);
dvander@27
  5800
    sideeffect=FALSE;
dvander@152
  5801
    ident=expression(&val,tag,symptr,chkfuncresult,lval);
dvander@27
  5802
    if (!allowarray && (ident==iARRAY || ident==iREFARRAY))
dvander@27
  5803
      error(33,"-unknown-");    /* array must be indexed */
dvander@27
  5804
    if (chkeffect && !sideeffect)
dvander@27
  5805
      error(215);               /* expression has no effect */
dvander@27
  5806
    sc_allowproccall=FALSE;     /* cannot use "procedure call" syntax anymore */
dvander@27
  5807
  } while (comma && matchtoken(',')); /* more? */
dvander@27
  5808
  if (mark_endexpr)
dvander@27
  5809
    markexpr(sEXPR,NULL,0);     /* optionally, mark the end of the expression */
dvander@27
  5810
  errorset(sEXPRRELEASE,0);
dvander@27
  5811
  if (localstaging) {
dvander@27
  5812
    stgout(index);
dvander@27
  5813
    stgset(FALSE);              /* stop staging */
dvander@27
  5814
  } /* if */
dvander@27
  5815
  return ident;
dvander@27
  5816
}
dvander@27
  5817
dvander@27
  5818
/*  constexpr
dvander@27
  5819
 */
dvander@27
  5820
SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr)
dvander@27
  5821
{
dvander@27
  5822
  int ident,index;
dvander@27
  5823
  cell cidx;
dvander@27
  5824
dvander@27
  5825
  stgset(TRUE);         /* start stage-buffering */
dvander@27
  5826
  stgget(&index,&cidx); /* mark position in code generator */
dvander@27
  5827
  errorset(sEXPRMARK,0);
dvander@152
  5828
  ident=expression(val,tag,symptr,FALSE,NULL);
dvander@27
  5829
  stgdel(index,cidx);   /* scratch generated code */
dvander@27
  5830
  stgset(FALSE);        /* stop stage-buffering */
dvander@27
  5831
  if (ident!=iCONSTEXPR) {
dvander@27
  5832
    error(8);           /* must be constant expression */
dvander@27
  5833
    if (val!=NULL)
dvander@27
  5834
      *val=0;
dvander@27
  5835
    if (tag!=NULL)
dvander@27
  5836
      *tag=0;
dvander@27
  5837
    if (symptr!=NULL)
dvander@27
  5838
      *symptr=NULL;
dvander@27
  5839
  } /* if */
dvander@27
  5840
  errorset(sEXPRRELEASE,0);
dvander@27
  5841
  return (ident==iCONSTEXPR);
dvander@27
  5842
}
dvander@27
  5843
dvander@27
  5844
/*  test
dvander@27
  5845
 *
dvander@27
  5846
 *  In the case a "simple assignment" operator ("=") is used within a test,
dvander@27
  5847
 *  the warning "possibly unintended assignment" is displayed. This routine
dvander@27
  5848
 *  sets the global variable "sc_intest" to true, it is restored upon termination.
ds@1265
  5849
 *  In the case the assignment was intended, use parentheses around the
dvander@27
  5850
 *  expression to avoid the warning; primary() sets "sc_intest" to 0.
dvander@27
  5851
 *
dvander@27
  5852
 *  Global references: sc_intest (altered, but restored upon termination)
dvander@27
  5853
 */
faluco@62
  5854
static int test(int label,int parens,int invert)
dvander@27
  5855
{
dvander@27
  5856
  int index,tok;
dvander@27
  5857
  cell cidx;
dvander@27
  5858
  int ident,tag;
faluco@62
  5859
  int endtok;
dvander@27
  5860
  cell constval;
dvander@27
  5861
  symbol *sym;
dvander@27
  5862
  int localstaging=FALSE;
dvander@27
  5863
dvander@27
  5864
  if (!staging) {
dvander@27
  5865
    stgset(TRUE);               /* start staging */
dvander@27
  5866
    localstaging=TRUE;
dvander@27
  5867
    #if !defined NDEBUG
dvander@27
  5868
      stgget(&index,&cidx);     /* should start at zero if started locally */
dvander@27
  5869
      assert(index==0);
dvander@27
  5870
    #endif
dvander@27
  5871
  } /* if */
dvander@27
  5872
dvander@27
  5873
  PUSHSTK_I(sc_intest);
dvander@27
  5874
  sc_intest=TRUE;
faluco@62
  5875
  endtok=0;
faluco@62
  5876
  if (parens!=TEST_PLAIN) {
faluco@62
  5877
    if (matchtoken('('))
faluco@62
  5878
      endtok=')';
faluco@62
  5879
    else if (parens==TEST_THEN)
faluco@62
  5880
      endtok=tTHEN;
faluco@62
  5881
    else if (parens==TEST_DO)
faluco@62
  5882
      endtok=tDO;
faluco@62
  5883
  } /* if */
dvander@27
  5884
  do {
dvander@27
  5885
    stgget(&index,&cidx);       /* mark position (of last expression) in
dvander@27
  5886
                                 * code generator */
dvander@152
  5887
    ident=expression(&constval,&tag,&sym,TRUE,NULL);
dvander@27
  5888
    tok=matchtoken(',');
dvander@27
  5889
    if (tok)
dvander@27
  5890
      markexpr(sEXPR,NULL,0);
dvander@27
  5891
  } while (tok); /* do */
faluco@62
  5892
  if (endtok!=0)
faluco@62
  5893
    needtoken(endtok);
dvander@27
  5894
  if (ident==iARRAY || ident==iREFARRAY) {
dvander@27
  5895
    char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-";
dvander@27
  5896
    error(33,ptr);              /* array must be indexed */
dvander@27
  5897
  } /* if */
dvander@27
  5898
  if (ident==iCONSTEXPR) {      /* constant expression */
faluco@62
  5899
    int testtype=0;
dvander@27
  5900
    sc_intest=(short)POPSTK_I();/* restore stack */
dvander@27
  5901
    stgdel(index,cidx);
dvander@27
  5902
    if (constval) {             /* code always executed */
dvander@27
  5903
      error(206);               /* redundant test: always non-zero */
faluco@62
  5904
      testtype=tENDLESS;
dvander@27
  5905
    } else {
dvander@27
  5906
      error(205);               /* redundant code: never executed */
dvander@27
  5907
      jumplabel(label);
dvander@27
  5908
    } /* if */
dvander@27
  5909
    if (localstaging) {
dvander@27
  5910
      stgout(0);                /* write "jumplabel" code */
dvander@27
  5911
      stgset(FALSE);            /* stop staging */
dvander@27
  5912
    } /* if */
faluco@62
  5913
    return testtype;
dvander@27
  5914
  } /* if */
dvander@27
  5915
  if (tag!=0 && tag!=pc_addtag("bool"))
dvander@27
  5916
    if (check_userop(lneg,tag,0,1,NULL,&tag))
dvander@27
  5917
      invert= !invert;          /* user-defined ! operator inverted result */
dvander@27
  5918
  if (invert)
dvander@27
  5919
    jmp_ne0(label);             /* jump to label if true (different from 0) */
dvander@27
  5920
  else
dvander@27
  5921
    jmp_eq0(label);             /* jump to label if false (equal to 0) */
dvander@27
  5922
  markexpr(sEXPR,NULL,0);       /* end expression (give optimizer a chance) */
dvander@27
  5923
  sc_intest=(short)POPSTK_I();  /* double typecast to avoid warning with Microsoft C */
dvander@27
  5924
  if (localstaging) {
dvander@27
  5925
    stgout(0);                  /* output queue from the very beginning (see
dvander@27
  5926
                                 * assert() when localstaging is set to TRUE) */
dvander@27
  5927
    stgset(FALSE);              /* stop staging */
dvander@27
  5928
  } /* if */
faluco@62
  5929
  return 0;
dvander@27
  5930
}
dvander@27
  5931
faluco@62
  5932
static int doif(void)
dvander@27
  5933
{
dvander@27
  5934
  int flab1,flab2;
dvander@27
  5935
  int ifindent;
faluco@62
  5936
  int lastst_true;
dvander@27
  5937
dvander@27
  5938
  ifindent=stmtindent;          /* save the indent of the "if" instruction */
dvander@27
  5939
  flab1=getlabel();             /* get label number for false branch */
faluco@62
  5940
  test(flab1,TEST_THEN,FALSE);  /* get expression, branch to flab1 if false */
dvander@27
  5941
  statement(NULL,FALSE);        /* if true, do a statement */
faluco@62
  5942
  if (!matchtoken(tELSE)) {     /* if...else ? */
dvander@27
  5943
    setlabel(flab1);            /* no, simple if..., print false label */
dvander@27
  5944
  } else {
faluco@62
  5945
    lastst_true=lastst;         /* save last statement of the "true" branch */
dvander@27
  5946
    /* to avoid the "dangling else" error, we want a warning if the "else"
dvander@27
  5947
     * has a lower indent than the matching "if" */
dvander@27
  5948
    if (stmtindent<ifindent && sc_tabsize>0)
dvander@27
  5949
      error(217);               /* loose indentation */
dvander@27
  5950
    flab2=getlabel();
dvander@27
  5951
    if ((lastst!=tRETURN) && (lastst!=tGOTO))
faluco@62
  5952
      jumplabel(flab2);         /* "true" branch jumps around "else" clause, unless the "true" branch statement already jumped */
dvander@27
  5953
    setlabel(flab1);            /* print false label */
dvander@27
  5954
    statement(NULL,FALSE);      /* do "else" clause */
dvander@27
  5955
    setlabel(flab2);            /* print true label */
faluco@62
  5956
    /* if both the "true" branch and the "false" branch ended with the same
faluco@62
  5957
     * kind of statement, set the last statement id to that kind, rather than
faluco@62
  5958
     * to the generic tIF; this allows for better "unreachable code" checking
faluco@62
  5959
     */
faluco@62
  5960
    if (lastst==lastst_true)
faluco@62
  5961
      return lastst;
faluco@62
  5962
  } /* if */
faluco@62
  5963
  return tIF;
dvander@27
  5964
}
dvander@27
  5965
faluco@62
  5966
static int dowhile(void)
dvander@27
  5967
{
dvander@27
  5968
  int wq[wqSIZE];               /* allocate local queue */
faluco@62
  5969
  int save_endlessloop,retcode;
faluco@62
  5970
faluco@62
  5971
  save_endlessloop=endlessloop;
dvander@27
  5972
  addwhile(wq);                 /* add entry to queue for "break" */
dvander@27
  5973
  setlabel(wq[wqLOOP]);         /* loop label */
dvander@27
  5974
  /* The debugger uses the "break" opcode to be able to "break" out of
dvander@27
  5975
   * a loop. To make sure that each loop has a break opcode, even for the
dvander@27
  5976
   * tiniest loop, set it below the top of the loop
dvander@27
  5977
   */
dvander@27
  5978
  setline(TRUE);
faluco@62
  5979
  endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
dvander@27
  5980
  statement(NULL,FALSE);        /* if so, do a statement */
dvander@27
  5981
  jumplabel(wq[wqLOOP]);        /* and loop to "while" start */
dvander@27
  5982
  setlabel(wq[wqEXIT]);         /* exit label */
dvander@27
  5983
  delwhile();                   /* delete queue entry */
faluco@62
  5984
faluco@62
  5985
  retcode=endlessloop ? tENDLESS : tWHILE;
faluco@62
  5986
  endlessloop=save_endlessloop;
faluco@62
  5987
  return retcode;
dvander@27
  5988
}
dvander@27
  5989
dvander@27
  5990
/*
dvander@27
  5991
 *  Note that "continue" will in this case not jump to the top of the loop, but
dvander@27
  5992
 *  to the end: just before the TRUE-or-FALSE testing code.
dvander@27
  5993
 */
faluco@62
  5994
static int dodo(void)
dvander@27
  5995
{
dvander@27
  5996
  int wq[wqSIZE],top;
faluco@62
  5997
  int save_endlessloop,retcode;
faluco@62
  5998
faluco@62
  5999
  save_endlessloop=endlessloop;
dvander@27
  6000
  addwhile(wq);           /* see "dowhile" for more info */
dvander@27
  6001
  top=getlabel();         /* make a label first */
dvander@27
  6002
  setlabel(top);          /* loop label */
dvander@27
  6003
  statement(NULL,FALSE);
dvander@27
  6004
  needtoken(tWHILE);
dvander@27
  6005
  setlabel(wq[wqLOOP]);   /* "continue" always jumps to WQLOOP. */
dvander@27
  6006
  setline(TRUE);
faluco@62
  6007
  endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
dvander@27
  6008
  jumplabel(top);
dvander@27
  6009
  setlabel(wq[wqEXIT]);
dvander@27
  6010
  delwhile();
dvander@27
  6011
  needtoken(tTERM);
faluco@62
  6012
faluco@62
  6013
  retcode=endlessloop ? tENDLESS : tDO;
faluco@62
  6014
  endlessloop=save_endlessloop;
faluco@62
  6015
  return retcode;
dvander@27
  6016
}
dvander@27
  6017
faluco@62
  6018
static int dofor(void)
dvander@27
  6019
{
dvander@27
  6020
  int wq[wqSIZE],skiplab;
dvander@27
  6021
  cell save_decl;
faluco@62
  6022
  int save_nestlevel,save_endlessloop;
faluco@62
  6023
  int index,endtok;
dvander@27
  6024
  int *ptr;
dvander@27
  6025
dvander@27
  6026
  save_decl=declared;
dvander@27
  6027
  save_nestlevel=nestlevel;
faluco@62
  6028
  save_endlessloop=endlessloop;
dvander@117
  6029
  pushstacklist();
dvander@117
  6030
  
dvander@27
  6031
  addwhile(wq);
dvander@27
  6032
  skiplab=getlabel();
faluco@62
  6033
  endtok= matchtoken('(') ? ')' : tDO;
dvander@27
  6034
  if (matchtoken(';')==0) {
dvander@27
  6035
    /* new variable declarations are allowed here */
dvander@27
  6036
    if (matchtoken(tNEW)) {
dvander@27
  6037
      /* The variable in expr1 of the for loop is at a
dvander@27
  6038
       * 'compound statement' level of it own.
dvander@27
  6039
       */
dvander@27
  6040
      nestlevel++;
faluco@63
  6041
      autozero=1;
dvander@27
  6042
      declloc(FALSE); /* declare local variable */
dvander@27
  6043
    } else {
dvander@27
  6044
      doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);  /* expression 1 */
dvander@27
  6045
      needtoken(';');
dvander@27
  6046
    } /* if */
dvander@27
  6047
  } /* if */
dvander@27
  6048
  /* Adjust the "declared" field in the "while queue", in case that
dvander@27
  6049
   * local variables were declared in the first expression of the
dvander@27
  6050
   * "for" loop. These are deleted in separately, so a "break" or a "continue"
dvander@27
  6051
   * must ignore these fields.
dvander@27
  6052
   */
dvander@27
  6053
  ptr=readwhile();
dvander@27
  6054
  assert(ptr!=NULL);
dvander@117
  6055
  /*ptr[wqBRK]=(int)declared;
dvander@117
  6056
   *ptr[wqCONT]=(int)declared;
dvander@117
  6057
   */
dvander@117
  6058
  ptr[wqBRK] = stackusage->list_id;
dvander@117
  6059
  ptr[wqCONT] = stackusage->list_id;
dvander@27
  6060
  jumplabel(skiplab);               /* skip expression 3 1st time */
dvander@27
  6061
  setlabel(wq[wqLOOP]);             /* "continue" goes to this label: expr3 */
dvander@27
  6062
  setline(TRUE);
dvander@27
  6063
  /* Expressions 2 and 3 are reversed in the generated code: expression 3
dvander@27
  6064
   * precedes expression 2. When parsing, the code is buffered and marks for
dvander@27
  6065
   * the start of each expression are insterted in the buffer.
dvander@27
  6066
   */
dvander@27
  6067
  assert(!staging);
dvander@27
  6068
  stgset(TRUE);                     /* start staging */
dvander@27
  6069
  assert(stgidx==0);
dvander@27
  6070
  index=stgidx;
dvander@27
  6071
  stgmark(sSTARTREORDER);
dvander@27
  6072
  stgmark((char)(sEXPRSTART+0));    /* mark start of 2nd expression in stage */
dvander@27
  6073
  setlabel(skiplab);                /* jump to this point after 1st expression */
faluco@62
  6074
  if (matchtoken(';')) {
faluco@62
  6075
    endlessloop=1;
faluco@62
  6076
  } else {
faluco@62
  6077
    endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
dvander@27
  6078
    needtoken(';');
dvander@27
  6079
  } /* if */
dvander@27
  6080
  stgmark((char)(sEXPRSTART+1));    /* mark start of 3th expression in stage */
faluco@62
  6081
  if (!matchtoken(endtok)) {
dvander@27
  6082
    doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);    /* expression 3 */
faluco@62
  6083
    needtoken(endtok);
dvander@27
  6084
  } /* if */
dvander@27
  6085
  stgmark(sENDREORDER);             /* mark end of reversed evaluation */
dvander@27
  6086
  stgout(index);
dvander@27
  6087
  stgset(FALSE);                    /* stop staging */
dvander@27
  6088
  statement(NULL,FALSE);
dvander@27
  6089
  jumplabel(wq[wqLOOP]);
dvander@27
  6090
  setlabel(wq[wqEXIT]);
dvander@27
  6091
  delwhile();
dvander@27
  6092
dvander@27
  6093
  assert(nestlevel>=save_nestlevel);
dvander@27
  6094
  if (nestlevel>save_nestlevel) {
dvander@27
  6095
    /* Clean up the space and the symbol table for the local
dvander@27
  6096
     * variable in "expr1".
dvander@27
  6097
     */
dvander@27
  6098
    destructsymbols(&loctab,nestlevel);
dvander@459
  6099
    popstacklist(1);
dvander@27
  6100
    testsymbols(&loctab,nestlevel,FALSE,TRUE);  /* look for unused block locals */
dvander@27
  6101
    declared=save_decl;
dvander@27
  6102
    delete_symbols(&loctab,nestlevel,FALSE,TRUE);
dvander@27
  6103
    nestlevel=save_nestlevel;     /* reset 'compound statement' nesting level */
dvander@754
  6104
  } else {
dvander@754
  6105
    popstacklist(0);
dvander@27
  6106
  } /* if */
faluco@62
  6107
faluco@62
  6108
  index=endlessloop ? tENDLESS : tFOR;
faluco@62
  6109
  endlessloop=save_endlessloop;
faluco@62
  6110
  return index;
dvander@27
  6111
}
dvander@27
  6112
dvander@27
  6113
/* The switch statement is incompatible with its C sibling:
dvander@27
  6114
 * 1. the cases are not drop through
dvander@27
  6115
 * 2. only one instruction may appear below each case, use a compound
dvander@27
  6116
 *    instruction to execute multiple instructions
dvander@27
  6117
 * 3. the "case" keyword accepts a comma separated list of values to
dvander@117
  6118
 *    match
dvander@27
  6119
 *
dvander@27
  6120
 * SWITCH param
dvander@27
  6121
 *   PRI = expression result
dvander@27
  6122
 *   param = table offset (code segment)
dvander@27
  6123
 *
dvander@27
  6124
 */
dvander@27
  6125
static void doswitch(void)
dvander@27
  6126
{
dvander@27
  6127
  int lbl_table,lbl_exit,lbl_case;
faluco@62
  6128
  int swdefault,casecount;
faluco@62
  6129
  int tok,endtok;
dvander@27
  6130
  cell val;
dvander@27
  6131
  char *str;
dvander@27
  6132
  constvalue caselist = { NULL, "", 0, 0};   /* case list starts empty */
dvander@27
  6133
  constvalue *cse,*csp;
dvander@27
  6134
  char labelname[sNAMEMAX+1];
dvander@27
  6135
faluco@62
  6136
  endtok= matchtoken('(') ? ')' : tDO;
dvander@27
  6137
  doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */
faluco@62
  6138
  needtoken(endtok);
dvander@27
  6139
  /* generate the code for the switch statement, the label is the address
dvander@27
  6140
   * of the case table (to be generated later).
dvander@27
  6141
   */
dvander@27
  6142
  lbl_table=getlabel();
dvander@27
  6143
  lbl_case=0;                   /* just to avoid a compiler warning */
dvander@27
  6144
  ffswitch(lbl_table);
dvander@27
  6145
faluco@62
  6146
  if (matchtoken(tBEGIN)) {
faluco@62
  6147
    endtok=tEND;
faluco@62
  6148
  } else {
faluco@62
  6149
    endtok='}';
faluco@62
  6150
    needtoken('{');
faluco@62
  6151
  } /* if */
dvander@27
  6152
  lbl_exit=getlabel();          /* get label number for jumping out of switch */
dvander@27
  6153
  swdefault=FALSE;
dvander@27
  6154
  casecount=0;
dvander@27
  6155
  do {
dvander@27
  6156
    tok=lex(&val,&str);         /* read in (new) token */
dvander@27
  6157
    switch (tok) {
dvander@27
  6158
    case tCASE:
dvander@27
  6159
      if (swdefault!=FALSE)
dvander@27
  6160
        error(15);        /* "default" case must be last in switch statement */
dvander@27
  6161
      lbl_case=getlabel();
dvander@27
  6162
      PUSHSTK_I(sc_allowtags);
dvander@27
  6163
      sc_allowtags=FALSE; /* do not allow tagnames here */
dvander@27
  6164
      do {
dvander@27
  6165
        casecount++;
dvander@27
  6166
dvander@27
  6167
        /* ??? enforce/document that, in a switch, a statement cannot start
dvander@27
  6168
         *     with a label. Then, you can search for:
dvander@27
  6169
         *     * the first semicolon (marks the end of a statement)
dvander@27
  6170
         *     * an opening brace (marks the start of a compound statement)
dvander@27
  6171
         *     and search for the right-most colon before that statement
dvander@27
  6172
         *     Now, by replacing the ':' by a special COLON token, you can
dvander@27
  6173
         *     parse all expressions until that special token.
dvander@27
  6174
         */
dvander@27
  6175
dvander@27
  6176
        constexpr(&val,NULL,NULL);
dvander@27
  6177
        /* Search the insertion point (the table is kept in sorted order, so
dvander@27
  6178
         * that advanced abstract machines can sift the case table with a
dvander@27
  6179
         * binary search). Check for duplicate case values at the same time.
dvander@27
  6180
         */
dvander@27
  6181
        for (csp=&caselist, cse=caselist.next;
dvander@27
  6182
             cse!=NULL && cse->value<val;
dvander@27
  6183
             csp=cse, cse=cse->next)
dvander@27
  6184
          /* nothing */;
dvander@27
  6185
        if (cse!=NULL && cse->value==val)
dvander@27
  6186
          error(40,val);                /* duplicate "case" label */
dvander@27
  6187
        /* Since the label is stored as a string in the "constvalue", the
dvander@27
  6188
         * size of an identifier must be at least 8, as there are 8
dvander@27
  6189
         * hexadecimal digits in a 32-bit number.
dvander@27
  6190
         */
dvander@27
  6191
        #if sNAMEMAX < 8
dvander@27
  6192
          #error Length of identifier (sNAMEMAX) too small.
dvander@27
  6193
        #endif
dvander@27
  6194
        assert(csp!=NULL);
dvander@27
  6195
        assert(csp->next==cse);
dvander@27
  6196
        insert_constval(csp,cse,itoh(lbl_case),val,0);
dvander@27
  6197
        if (matchtoken(tDBLDOT)) {
dvander@101
  6198
          error(1, ":", "..");
dvander@27
  6199
        } /* if */
dvander@27
  6200
      } while (matchtoken(','));
dvander@27
  6201
      needtoken(':');                   /* ':' ends the case */
dvander@27
  6202
      sc_allowtags=(short)POPSTK_I();   /* reset */
dvander@27
  6203
      setlabel(lbl_case);
dvander@27
  6204
      statement(NULL,FALSE);
dvander@27
  6205
      jumplabel(lbl_exit);
dvander@27
  6206
      break;
dvander@27
  6207
    case tDEFAULT:
dvander@27
  6208
      if (swdefault!=FALSE)
dvander@27
  6209
        error(16);         /* multiple defaults in switch */
dvander@27
  6210
      lbl_case=getlabel();
dvander@27
  6211
      setlabel(lbl_case);
dvander@27
  6212
      needtoken(':');
dvander@27
  6213
      swdefault=TRUE;
dvander@27
  6214
      statement(NULL,FALSE);
dvander@27
  6215
      /* Jump to lbl_exit, even thouh this is the last clause in the
dvander@27
  6216
       * switch, because the jump table is generated between the last
dvander@27
  6217
       * clause of the switch and the exit label.
dvander@27
  6218
       */
dvander@27
  6219
      jumplabel(lbl_exit);
dvander@27
  6220
      break;
dvander@27
  6221
    default:
faluco@62
  6222
      if (tok!=endtok) {
faluco@62
  6223
        error(2);
faluco@62
  6224
        indent_nowarn=TRUE; /* disable this check */
faluco@62
  6225
        tok=endtok;     /* break out of the loop after an error */
faluco@62
  6226
      } /* if */
dvander@27
  6227
    } /* switch */
faluco@62
  6228
  } while (tok!=endtok);
dvander@27
  6229
dvander@27
  6230
  #if !defined NDEBUG
dvander@27
  6231
    /* verify that the case table is sorted (unfortunatly, duplicates can
dvander@27
  6232
     * occur; there really shouldn't be duplicate cases, but the compiler
dvander@27
  6233
     * may not crash or drop into an assertion for a user error). */
dvander@27
  6234
    for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next)
dvander@27
  6235
      assert(cse->value <= cse->next->value);
dvander@27
  6236
  #endif
dvander@27
  6237
  /* generate the table here, before lbl_exit (general jump target) */
dvander@27
  6238
  setlabel(lbl_table);
dvander@27
  6239
  assert(swdefault==FALSE || swdefault==TRUE);
dvander@27
  6240
  if (swdefault==FALSE) {
dvander@27
  6241
    /* store lbl_exit as the "none-matched" label in the switch table */
dvander@27
  6242
    strcpy(labelname,itoh(lbl_exit));
dvander@27
  6243
  } else {
dvander@27
  6244
    /* lbl_case holds the label of the "default" clause */
dvander@27
  6245
    strcpy(labelname,itoh(lbl_case));
dvander@27
  6246
  } /* if */
dvander@27
  6247
  ffcase(casecount,labelname,TRUE);
dvander@27
  6248
  /* generate the rest of the table */
dvander@27
  6249
  for (cse=caselist.next; cse!=NULL; cse=cse->next)
dvander@27
  6250
    ffcase(cse->value,cse->name,FALSE);
dvander@27
  6251
dvander@27
  6252
  setlabel(lbl_exit);
dvander@27
  6253
  delete_consttable(&caselist); /* clear list of case labels */
dvander@27
  6254
}
dvander@27
  6255
dvander@27
  6256
static void doassert(void)
dvander@27
  6257
{
dvander@27
  6258
  int flab1,index;
dvander@27
  6259
  cell cidx;
dvander@27
  6260
dvander@27
  6261
  if ((sc_debug & sCHKBOUNDS)!=0) {
dvander@27
  6262
    flab1=getlabel();           /* get label number for "OK" branch */
faluco@62
  6263
    test(flab1,TEST_PLAIN,TRUE);/* get expression and branch to flab1 if true */
dvander@27
  6264
    insert_dbgline(fline);      /* make sure we can find the correct line number */
dvander@27
  6265
    ffabort(xASSERTION);
dvander@27
  6266
    setlabel(flab1);
dvander@27
  6267
  } else {
dvander@27
  6268
    stgset(TRUE);               /* start staging */
dvander@27
  6269
    stgget(&index,&cidx);       /* mark position in code generator */
dvander@27
  6270
    do {
dvander@152
  6271
      expression(NULL,NULL,NULL,FALSE,NULL);
dvander@27
  6272
      stgdel(index,cidx);       /* just scrap the code */
dvander@27
  6273
    } while (matchtoken(','));
dvander@27
  6274
    stgset(FALSE);              /* stop staging */
dvander@27
  6275
  } /* if */
dvander@27
  6276
  needtoken(tTERM);
dvander@27
  6277
}
dvander@27
  6278
dvander@27
  6279
static void dogoto(void)
dvander@27
  6280
{
dvander@27
  6281
  char *st;
dvander@27
  6282
  cell val;
dvander@27
  6283
  symbol *sym;
dvander@27
  6284
faluco@62
  6285
  /* if we were inside an endless loop, assume that we jump out of it */
faluco@62
  6286
  endlessloop=0;
faluco@62
  6287
dvander@2202
  6288
  error(4, "goto");
dvander@2202
  6289
dvander@27
  6290
  if (lex(&val,&st)==tSYMBOL) {
dvander@27
  6291
    sym=fetchlab(st);
dvander@27
  6292
    jumplabel((int)sym->addr);
dvander@27
  6293
    sym->usage|=uREAD;  /* set "uREAD" bit */
dvander@27
  6294
    // ??? if the label is defined (check sym->usage & uDEFINE), check
dvander@27
  6295
    //     sym->compound (nesting level of the label) against nestlevel;
dvander@27
  6296
    //     if sym->compound < nestlevel, call the destructor operator
dvander@27
  6297
  } else {
dvander@27
  6298
    error(20,st);       /* illegal symbol name */
dvander@27
  6299
  } /* if */
dvander@27
  6300
  needtoken(tTERM);
dvander@27
  6301
}
dvander@27
  6302
dvander@27
  6303
static void dolabel(void)
dvander@27
  6304
{
dvander@27
  6305
  char *st;
dvander@27
  6306
  cell val;
dvander@27
  6307
  symbol *sym;
dvander@27
  6308
dvander@27
  6309
  tokeninfo(&val,&st);  /* retrieve label name again */
dvander@27
  6310
  if (find_constval(&tagname_tab,st,0)!=NULL)
dvander@27
  6311
    error(221,st);      /* label name shadows tagname */
dvander@27
  6312
  sym=fetchlab(st);
dvander@27
  6313
  setlabel((int)sym->addr);
dvander@27
  6314
  /* since one can jump around variable declarations or out of compound
dvander@27
  6315
   * blocks, the stack must be manually adjusted
dvander@27
  6316
   */
dvander@117
  6317
  //:TODO: This is actually generated, egads!
dvander@117
  6318
  //We have to support this and LCTRL/SCTRL
dvander@27
  6319
  setstk(-declared*sizeof(cell));
dvander@27
  6320
  sym->usage|=uDEFINE;  /* label is now defined */
dvander@27
  6321
}
dvander@27
  6322
dvander@27
  6323
/*  fetchlab
dvander@27
  6324
 *
dvander@27
  6325
 *  Finds a label from the (local) symbol table or adds one to it.
dvander@27
  6326
 *  Labels are local in scope.
dvander@27
  6327
 *
dvander@27
  6328
 *  Note: The "_usage" bit is set to zero. The routines that call "fetchlab()"
dvander@27
  6329
 *        must set this bit accordingly.
dvander@27
  6330
 */
dvander@27
  6331
static symbol *fetchlab(char *name)
dvander@27
  6332
{
dvander@27
  6333
  symbol *sym;
dvander@27
  6334
dvander@27
  6335
  sym=findloc(name);            /* labels are local in scope */
dvander@27
  6336
  if (sym){
dvander@27
  6337
    if (sym->ident!=iLABEL)
dvander@27
  6338
      error(19,sym->name);      /* not a label: ... */
dvander@27
  6339
  } else {
dvander@27
  6340
    sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0);
dvander@27
  6341
    assert(sym!=NULL);          /* fatal error 103 must be given on error */
dvander@27
  6342
    sym->x.declared=(int)declared;
dvander@27
  6343
    sym->compound=nestlevel;
dvander@27
  6344
  } /* if */
dvander@27
  6345
  return sym;
dvander@27
  6346
}
dvander@27
  6347
dvander@27
  6348
/*  doreturn
dvander@27
  6349
 *
dvander@27
  6350
 *  Global references: rettype  (altered)
dvander@27
  6351
 */
dvander@27
  6352
static void doreturn(void)
dvander@27
  6353
{
dvander@27
  6354
  int tag,ident;
dvander@27
  6355
  int level;
dvander@27
  6356
  symbol *sym,*sub;
dvander@27
  6357
dvander@27
  6358
  if (!matchtoken(tTERM)) {
dvander@27
  6359
    /* "return <value>" */
dvander@27
  6360
    if ((rettype & uRETNONE)!=0)
dvander@27
  6361
      error(78);                        /* mix "return;" and "return value;" */
dvander@103
  6362
    ident=doexpr(TRUE,FALSE,TRUE,FALSE,&tag,&sym,TRUE);
dvander@27
  6363
    needtoken(tTERM);
dvander@27
  6364
    /* see if this function already has a sub type (an array attached) */
dvander@27
  6365
    sub=finddepend(curfunc);
dvander@27
  6366
    assert(sub==NULL || sub->ident==iREFARRAY);
dvander@27
  6367
    if ((rettype & uRETVALUE)!=0) {
dvander@27
  6368
      int retarray=(ident==iARRAY || ident==iREFARRAY);
dvander@27
  6369
      /* there was an earlier "return" statement in this function */
dvander@27
  6370
      if (sub==NULL && retarray || sub!=NULL && !retarray)
dvander@27
  6371
        error(79);                      /* mixing "return array;" and "return value;" */
dvander@27
  6372
      if (retarray && (curfunc->usage & uPUBLIC)!=0)
dvander@27
  6373
        error(90,curfunc->name);        /* public function may not return array */
dvander@27
  6374
    } /* if */
dvander@27
  6375
    rettype|=uRETVALUE;                 /* function returns a value */
dvander@27
  6376
    /* check tagname with function tagname */
dvander@27
  6377
    assert(curfunc!=NULL);
dvander@151
  6378
    if (!matchtag_string(ident, tag) && !matchtag(curfunc->tag,tag,TRUE))
dvander@27
  6379
      error(213);                       /* tagname mismatch */
dvander@27
  6380
    if (ident==iARRAY || ident==iREFARRAY) {
dvander@27
  6381
      int dim[sDIMEN_MAX],numdim;
dvander@27
  6382
      cell arraysize;
dvander@27
  6383
      assert(sym!=NULL);
dvander@27
  6384
      if (sub!=NULL) {
dvander@27
  6385
        assert(sub->ident==iREFARRAY);
dvander@27
  6386
        /* this function has an array attached already; check that the current
dvander@27
  6387
         * "return" statement returns exactly the same array
dvander@27
  6388
         */
dvander@27
  6389
        level=sym->dim.array.level;
dvander@27
  6390
        if (sub->dim.array.level!=level) {
dvander@27
  6391
          error(48);                    /* array dimensions must match */
dvander@27
  6392
        } else {
dvander@27
  6393
          for (numdim=0; numdim<=level; numdim++) {
dvander@27
  6394
            dim[numdim]=(int)sub->dim.array.length;
dvander@27
  6395
            if (sym->dim.array.length!=dim[numdim])
dvander@27
  6396
              error(47);    /* array sizes must match */
dvander@27
  6397
            if (numdim<level) {
dvander@27
  6398
              sym=finddepend(sym);
dvander@27
  6399
              sub=finddepend(sub);
dvander@27
  6400
              assert(sym!=NULL && sub!=NULL);
dvander@27
  6401
              /* ^^^ both arrays have the same dimensions (this was checked
dvander@27
  6402
               *     earlier) so the dependend should always be found
dvander@27
  6403
               */
dvander@27
  6404
            } /* if */
dvander@27
  6405
          } /* for */
dvander@27
  6406
        } /* if */
dvander@27
  6407
      } else {
dvander@27
  6408
        int idxtag[sDIMEN_MAX];
dvander@27
  6409
        int argcount;
dvander@27
  6410
        /* this function does not yet have an array attached; clone the
dvander@27
  6411
         * returned symbol beneath the current function
dvander@27
  6412
         */
dvander@27
  6413
        sub=sym;
dvander@27
  6414
        assert(sub!=NULL);
dvander@27
  6415
        level=sub->dim.array.level;
dvander@27
  6416
        for (numdim=0; numdim<=level; numdim++) {
dvander@27
  6417
          dim[numdim]=(int)sub->dim.array.length;
dvander@27
  6418
          idxtag[numdim]=sub->x.tags.index;
dvander@27
  6419
          if (numdim<level) {
dvander@27
  6420
            sub=finddepend(sub);
dvander@27
  6421
            assert(sub!=NULL);
dvander@27
  6422
          } /* if */
dvander@27
  6423
          /* check that all dimensions are known */
dvander@27
  6424
          if (dim[numdim]<=0)
dvander@27
  6425
            error(46,sym->name);
dvander@27
  6426
        } /* for */
dvander@27
  6427
        /* the address of the array is stored in a hidden parameter; the address
dvander@27
  6428
         * of this parameter is 1 + the number of parameters (times the size of
dvander@27
  6429
         * a cell) + the size of the stack frame and the return address
dvander@27
  6430
         *   base + 0*sizeof(cell)         == previous "base"
dvander@27
  6431
         *   base + 1*sizeof(cell)         == function return address
dvander@27
  6432
         *   base + 2*sizeof(cell)         == number of arguments
dvander@27
  6433
         *   base + 3*sizeof(cell)         == first argument of the function
dvander@27
  6434
         *   ...
dvander@27
  6435
         *   base + ((n-1)+3)*sizeof(cell) == last argument of the function
dvander@27
  6436
         *   base + (n+3)*sizeof(cell)     == hidden parameter with array address
dvander@27
  6437
         */
dvander@27
  6438
        assert(curfunc!=NULL);
dvander@27
  6439
        assert(curfunc->dim.arglist!=NULL);
dvander@27
  6440
        for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++)
dvander@27
  6441
          /* nothing */;
dvander@27
  6442
        sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag);
dvander@27
  6443
        sub->parent=curfunc;
dvander@27
  6444
      } /* if */
dvander@27
  6445
      /* get the hidden parameter, copy the array (the array is on the heap;
dvander@27
  6446
       * it stays on the heap for the moment, and it is removed -usually- at
dvander@27
  6447
       * the end of the expression/statement, see expression() in SC3.C)
dvander@27
  6448
       */
dvander@27
  6449
      address(sub,sALT);                /* ALT = destination */
dvander@27
  6450
      arraysize=calc_arraysize(dim,numdim,0);
dvander@27
  6451
      memcopy(arraysize*sizeof(cell));  /* source already in PRI */
dvander@27
  6452
      /* moveto1(); is not necessary, callfunction() does a popreg() */
dvander@27
  6453
    } /* if */
dvander@27
  6454
  } else {
dvander@27
  6455
    /* this return statement contains no expression */
dvander@27
  6456
    ldconst(0,sPRI);
dvander@27
  6457
    if ((rettype & uRETVALUE)!=0) {
dvander@27
  6458
      char symname[2*sNAMEMAX+16];      /* allow space for user defined operators */
dvander@27
  6459
      assert(curfunc!=NULL);
dvander@27
  6460
      funcdisplayname(symname,curfunc->name);
dvander@27
  6461
      error(209,symname);               /* function should return a value */
dvander@27
  6462
    } /* if */
dvander@27
  6463
    rettype|=uRETNONE;                  /* function does not return anything */
dvander@27
  6464
  } /* if */
dvander@27
  6465
  destructsymbols(&loctab,0);           /* call destructor for *all* locals */
dvander@118
  6466
  genheapfree(-1);
dvander@117
  6467
  genstackfree(-1);						/* free everything on the stack */
dvander@27
  6468
  ffret(strcmp(curfunc->name,uENTRYFUNC)!=0);
dvander@27
  6469
}
dvander@27
  6470
dvander@27
  6471
static void dobreak(void)
dvander@27
  6472
{
dvander@27
  6473
  int *ptr;
dvander@27
  6474
faluco@62
  6475
  endlessloop=0;      /* if we were inside an endless loop, we just jumped out */
dvander@27
  6476
  ptr=readwhile();      /* readwhile() gives an error if not in loop */
dvander@27
  6477
  needtoken(tTERM);
dvander@27
  6478
  if (ptr==NULL)
dvander@27
  6479
    return;
dvander@27
  6480
  destructsymbols(&loctab,nestlevel);
dvander@117
  6481
  genstackfree(ptr[wqBRK]);
dvander@27
  6482
  jumplabel(ptr[wqEXIT]);
dvander@27
  6483
}
dvander@27
  6484
dvander@27
  6485
static void docont(void)
dvander@27
  6486
{
dvander@27
  6487
  int *ptr;
dvander@27
  6488
dvander@27
  6489
  ptr=readwhile();      /* readwhile() gives an error if not in loop */
dvander@27
  6490
  needtoken(tTERM);
dvander@27
  6491
  if (ptr==NULL)
dvander@27
  6492
    return;
dvander@27
  6493
  destructsymbols(&loctab,nestlevel);
dvander@117
  6494
  genstackfree(ptr[wqCONT]);
dvander@128
  6495
  genheapfree(ptr[wqCONT]);
dvander@27
  6496
  jumplabel(ptr[wqLOOP]);
dvander@27
  6497
}
dvander@27
  6498
dvander@27
  6499
SC_FUNC void exporttag(int tag)
dvander@27
  6500
{
dvander@27
  6501
  /* find the tag by value in the table, then set the top bit to mark it
dvander@27
  6502
   * "public"
dvander@27
  6503
   */
dvander@27
  6504
  if (tag!=0 && (tag & PUBLICTAG)==0) {
dvander@27
  6505
    constvalue *ptr;
dvander@27
  6506
    for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next)
dvander@27
  6507
      /* nothing */;
dvander@27
  6508
    if (ptr!=NULL)
dvander@27
  6509
      ptr->value |= PUBLICTAG;
dvander@27
  6510
  } /* if */
dvander@27
  6511
}
dvander@27
  6512
dvander@27
  6513
static void doexit(void)
dvander@27
  6514
{
dvander@27
  6515
  int tag=0;
dvander@27
  6516
dvander@27
  6517
  if (matchtoken(tTERM)==0){
dvander@27
  6518
    doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
dvander@27
  6519
    needtoken(tTERM);
dvander@27
  6520
  } else {
dvander@27
  6521
    ldconst(0,sPRI);
dvander@27
  6522
  } /* if */
dvander@27
  6523
  ldconst(tag,sALT);
dvander@27
  6524
  exporttag(tag);
dvander@27
  6525
  destructsymbols(&loctab,0);           /* call destructor for *all* locals */
dvander@27
  6526
  ffabort(xEXIT);
dvander@27
  6527
}
dvander@27
  6528
dvander@27
  6529
static void dosleep(void)
dvander@27
  6530
{
dvander@27
  6531
  int tag=0;
dvander@27
  6532
dvander@27
  6533
  if (matchtoken(tTERM)==0){
dvander@27
  6534
    doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
dvander@27
  6535
    needtoken(tTERM);
dvander@27
  6536
  } else {
dvander@27
  6537
    ldconst(0,sPRI);
dvander@27
  6538
  } /* if */
dvander@27
  6539
  ldconst(tag,sALT);
dvander@27
  6540
  exporttag(tag);
dvander@27
  6541
  ffabort(xSLEEP);
dvander@27
  6542
dvander@27
  6543
  /* for stack usage checking, mark the use of the sleep instruction */
dvander@27
  6544
  pc_memflags |= suSLEEP_INSTR;
dvander@27
  6545
}
dvander@27
  6546
dvander@27
  6547
static void dostate(void)
dvander@27
  6548
{
dvander@27
  6549
  char name[sNAMEMAX+1];
dvander@27
  6550
  constvalue *automaton;
dvander@27
  6551
  constvalue *state;
dvander@27
  6552
  constvalue *stlist;
dvander@27
  6553
  int flabel;
dvander@27
  6554
  symbol *sym;
dvander@27
  6555
  #if !defined SC_LIGHT
dvander@27
  6556
    int length,index,listid,listindex,stateindex;
dvander@27
  6557
    char *doc;
dvander@27
  6558
  #endif
dvander@27
  6559
dvander@27
  6560
  /* check for an optional condition */
dvander@27
  6561
  if (matchtoken('(')) {
dvander@27
  6562
    flabel=getlabel();          /* get label number for "false" branch */
dvander@27
  6563
    pc_docexpr=TRUE;            /* attach expression as a documentation string */
faluco@62
  6564
    test(flabel,TEST_PLAIN,FALSE);/* get expression, branch to flabel if false */
dvander@27
  6565
    pc_docexpr=FALSE;
dvander@27
  6566
    needtoken(')');
dvander@27
  6567
  } else {
dvander@27
  6568
    flabel=-1;
dvander@27
  6569
  } /* if */
dvander@27
  6570
dvander@27
  6571
  if (!sc_getstateid(&automaton,&state)) {
dvander@27
  6572
    delete_autolisttable();
dvander@27
  6573
    return;
dvander@27
  6574
  } /* if */
dvander@27
  6575
  needtoken(tTERM);
dvander@27
  6576
dvander@27
  6577
  /* store the new state id */
dvander@27
  6578
  assert(state!=NULL);
dvander@27
  6579
  ldconst(state->value,sPRI);
dvander@27
  6580
  assert(automaton!=NULL);
dvander@27
  6581
  assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
dvander@27
  6582
  storereg(automaton->value,sPRI);
dvander@27
  6583
dvander@27
  6584
  /* find the optional entry() function for the state */
dvander@27
  6585
  sym=findglb(uENTRYFUNC,sGLOBAL);
dvander@27
  6586
  if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) {
dvander@27
  6587
    for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) {
dvander@27
  6588
      assert(strlen(stlist->name)!=0);
dvander@27
  6589
      if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value))
dvander@27
  6590
        break;      /* found! */
dvander@27
  6591
    } /* for */
dvander@27
  6592
    assert(stlist==NULL || state_inlist(stlist->index,state->value));
dvander@27
  6593
    if (stlist!=NULL) {
dvander@27
  6594
      /* the label to jump to is in stlist->name */
dvander@27
  6595
      ffcall(sym,stlist->name,0);
dvander@27
  6596
    } /* if */
dvander@27
  6597
  } /* if */
dvander@27
  6598
dvander@27
  6599
  if (flabel>=0)
dvander@27
  6600
    setlabel(flabel);           /* condition was false, jump around the state switch */
dvander@27
  6601
dvander@27
  6602
  #if !defined SC_LIGHT
dvander@27
  6603
    /* mark for documentation */
dvander@27
  6604
    if (sc_status==statFIRST) {
dvander@27
  6605
      char *str;
dvander@27
  6606
      /* get the last list id attached to the function, this contains the source states */
dvander@27
  6607
      assert(curfunc!=NULL);
dvander@27
  6608
      if (curfunc->states!=NULL) {
dvander@27
  6609
        stlist=curfunc->states->next;
dvander@27
  6610
        assert(stlist!=NULL);
dvander@27
  6611
        while (stlist->next!=NULL)
dvander@27
  6612
          stlist=stlist->next;
dvander@27
  6613
        listid=stlist->index;
dvander@27
  6614
      } else {
dvander@27
  6615
        listid=-1;
dvander@27
  6616
      } /* if */
dvander@27
  6617
      listindex=0;
dvander@27
  6618
      length=strlen(name)+70; /* +70 for the fixed part "<transition ... />\n" */
dvander@27
  6619
      /* see if there are any condition strings to attach */
dvander@27
  6620
      for (index=0; (str=get_autolist(index))!=NULL; index++)
dvander@27
  6621
        length+=strlen(str);
dvander@27
  6622
      if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) {
dvander@27
  6623
        do {
dvander@27
  6624
          sprintf(doc,"<transition target=\"%s\"",name);
dvander@27
  6625
          if (listid>=0) {
dvander@27
  6626
            /* get the source state */
dvander@27
  6627
            stateindex=state_listitem(listid,listindex);
dvander@27
  6628
            state=state_findid(stateindex);
dvander@27
  6629
            assert(state!=NULL);
dvander@27
  6630
            sprintf(doc+strlen(doc)," source=\"%s\"",state->name);
dvander@27
  6631
          } /* if */
dvander@27
  6632
          if (get_autolist(0)!=NULL) {
dvander@27
  6633
            /* add the condition */
dvander@27
  6634
            strcat(doc," condition=\"");
dvander@27
  6635
            for (index=0; (str=get_autolist(index))!=NULL; index++) {
dvander@27
  6636
              /* remove the ')' token that may be appended before detecting that the expression has ended */
dvander@27
  6637
              if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL)
dvander@27
  6638
                strcat(doc,str);
dvander@27
  6639
            } /* for */
dvander@27
  6640
            strcat(doc,"\"");
dvander@27
  6641
          } /* if */
dvander@27
  6642
          strcat(doc,"/>\n");
dvander@27
  6643
          insert_docstring(doc);
dvander@27
  6644
        } while (listid>=0 && ++listindex<state_count(listid));
dvander@27
  6645
        free(doc);
dvander@27
  6646
      } /* if */
dvander@27
  6647
    } /* if */
dvander@27
  6648
  #endif
dvander@27
  6649
  delete_autolisttable();
dvander@27
  6650
}
dvander@27
  6651
dvander@27
  6652
dvander@27
  6653
static void addwhile(int *ptr)
dvander@27
  6654
{
dvander@27
  6655
  int k;
dvander@27
  6656
dvander@640
  6657
  ptr[wqBRK]=stackusage->list_id;     /* stack pointer (for "break") */
dvander@640
  6658
  ptr[wqCONT]=stackusage->list_id;    /* for "continue", possibly adjusted later */
dvander@27
  6659
  ptr[wqLOOP]=getlabel();
dvander@27
  6660
  ptr[wqEXIT]=getlabel();
dvander@27
  6661
  if (wqptr>=(wq+wqTABSZ-wqSIZE))
ds@841
  6662
    error(122,"loop table");    /* loop table overflow (too many active loops)*/
dvander@27
  6663
  k=0;
dvander@27
  6664
  while (k<wqSIZE){     /* copy "ptr" to while queue table */
dvander@27
  6665
    *wqptr=*ptr;
dvander@27
  6666
    wqptr+=1;
dvander@27
  6667
    ptr+=1;
dvander@27
  6668
    k+=1;
dvander@27
  6669
  } /* while */
dvander@27
  6670
}
dvander@27
  6671
dvander@27
  6672
static void delwhile(void)
dvander@27
  6673
{
dvander@27
  6674
  if (wqptr>wq)
dvander@27
  6675
    wqptr-=wqSIZE;
dvander@27
  6676
}
dvander@27
  6677
dvander@27
  6678
static int *readwhile(void)
dvander@27
  6679
{
dvander@27
  6680
  if (wqptr<=wq){
dvander@27
  6681
    error(24);          /* out of context */
dvander@27
  6682
    return NULL;
dvander@27
  6683
  } else {
dvander@27
  6684
    return (wqptr-wqSIZE);
dvander@27
  6685
  } /* if */
dvander@27
  6686
}
dvander@27
  6687