sourcepawn/compiler/sc2.c
author Nicholas Hastings <skamonkey@gmail.com>
Mon Feb 08 19:57:20 2010 -0800 (2010-02-08)
changeset 2914 bf5f9aa53abe
parent 2783 5f333027ef04
child 3065 4b211d3cccb8
permissions -rw-r--r--
Fixed FindEntityByClassname crash in Synergy (bug 4235, r=dvander).
ds@1952
     1
/*  Pawn compiler - File input, preprocessing and lexical analysis functions
ds@1952
     2
 *
ds@1952
     3
 *  Copyright (c) ITB CompuPhase, 1997-2006
ds@1952
     4
 *
ds@1952
     5
 *  This software is provided "as-is", without any express or implied warranty.
ds@1952
     6
 *  In no event will the authors be held liable for any damages arising from
ds@1952
     7
 *  the use of this software.
ds@1952
     8
 *
ds@1952
     9
 *  Permission is granted to anyone to use this software for any purpose,
ds@1952
    10
 *  including commercial applications, and to alter it and redistribute it
ds@1952
    11
 *  freely, subject to the following restrictions:
ds@1952
    12
 *
ds@1952
    13
 *  1.  The origin of this software must not be misrepresented; you must not
ds@1952
    14
 *      claim that you wrote the original software. If you use this software in
ds@1952
    15
 *      a product, an acknowledgment in the product documentation would be
ds@1952
    16
 *      appreciated but is not required.
ds@1952
    17
 *  2.  Altered source versions must be plainly marked as such, and must not be
ds@1952
    18
 *      misrepresented as being the original software.
ds@1952
    19
 *  3.  This notice may not be removed or altered from any source distribution.
ds@1952
    20
 *
ds@1952
    21
 *  Version: $Id$
ds@1952
    22
 */
ds@1952
    23
#include <assert.h>
ds@1952
    24
#include <stdio.h>
ds@1952
    25
#include <stdlib.h>
ds@1952
    26
#include <string.h>
ds@1952
    27
#include <ctype.h>
ds@1952
    28
#include <math.h>
ds@1952
    29
#include "lstring.h"
ds@1952
    30
#include "sc.h"
ds@1952
    31
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
ds@1952
    32
  #include <sclinux.h>
ds@1952
    33
#endif
ds@1952
    34
ds@1952
    35
#if defined FORTIFY
ds@1952
    36
  #include <alloc/fortify.h>
ds@1952
    37
#endif
ds@1952
    38
ds@1952
    39
/* flags for litchar() */
ds@1952
    40
#define RAWMODE         1
ds@1952
    41
#define UTF8MODE        2
ds@1952
    42
static cell litchar(const unsigned char **lptr,int flags);
ds@1952
    43
static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag);
ds@1952
    44
ds@1952
    45
static void substallpatterns(unsigned char *line,int buffersize);
ds@1952
    46
static int match(char *st,int end);
ds@1952
    47
static int alpha(char c);
ds@1952
    48
ds@1952
    49
#define SKIPMODE      1 /* bit field in "#if" stack */
ds@1952
    50
#define PARSEMODE     2 /* bit field in "#if" stack */
ds@1952
    51
#define HANDLED_ELSE  4 /* bit field in "#if" stack */
ds@1952
    52
#define SKIPPING      (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE)
ds@1952
    53
ds@1952
    54
static short icomment;  /* currently in multiline comment? */
ds@1952
    55
static char ifstack[sCOMP_STACK]; /* "#if" stack */
ds@1952
    56
static short iflevel;   /* nesting level if #if/#else/#endif */
ds@1952
    57
static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */
ds@1952
    58
static unsigned char term_expr[] = "";
ds@1952
    59
static int listline=-1; /* "current line" for the list file */
ds@1952
    60
dvander@2783
    61
#if defined __GNUC__
dvander@2783
    62
static double pow10(double d)
dvander@2783
    63
{
dvander@2783
    64
  return pow(10, d);
dvander@2783
    65
}
dvander@2783
    66
#endif
dvander@2783
    67
ds@1952
    68
ds@1952
    69
/*  pushstk & popstk
ds@1952
    70
 *
ds@1952
    71
 *  Uses a LIFO stack to store information. The stack is used by doinclude(),
ds@1952
    72
 *  doswitch() (to hold the state of "swactive") and some other routines.
ds@1952
    73
 *
ds@1952
    74
 *  Porting note: I made the bold assumption that an integer will not be
ds@1952
    75
 *  larger than a pointer (it may be smaller). That is, the stack element
ds@1952
    76
 *  is typedef'ed as a pointer type, but I also store integers on it. See
ds@1952
    77
 *  SC.H for "stkitem"
ds@1952
    78
 *
ds@1952
    79
 *  Global references: stack,stkidx,stktop (private to pushstk(), popstk()
ds@1952
    80
 *                     and clearstk())
ds@1952
    81
 */
ds@1952
    82
static stkitem *stack=NULL;
ds@1952
    83
static int stkidx=0,stktop=0;
ds@1952
    84
ds@1952
    85
SC_FUNC void pushstk(stkitem val)
ds@1952
    86
{
ds@1952
    87
  assert(stkidx<=stktop);
ds@1952
    88
  if (stkidx==stktop) {
ds@1952
    89
    stkitem *newstack;
ds@1952
    90
    int newsize= (stktop==0) ? 16 : 2*stktop;
ds@1952
    91
    /* try to resize the stack */
ds@1952
    92
    assert(newsize>stktop);
ds@1952
    93
    newstack=(stkitem*)malloc(newsize*sizeof(stkitem));
ds@1952
    94
    if (newstack==NULL)
ds@1952
    95
      error(122,"parser stack");  /* stack overflow (recursive include?) */
ds@1952
    96
    /* swap the stacks */
ds@1952
    97
    memcpy(newstack,stack,stkidx*sizeof(stkitem));
ds@1952
    98
    if (stack!=NULL)
ds@1952
    99
      free(stack);
ds@1952
   100
    stack=newstack;
ds@1952
   101
    stktop=newsize;
ds@1952
   102
  } /* if */
ds@1952
   103
  assert(stkidx<stktop);
ds@1952
   104
  stack[stkidx]=val;
ds@1952
   105
  stkidx+=1;
ds@1952
   106
}
ds@1952
   107
ds@1952
   108
SC_FUNC stkitem popstk(void)
ds@1952
   109
{
ds@1952
   110
  if (stkidx==0) {
ds@1952
   111
    stkitem s;
ds@1952
   112
    s.i=-1;             /* stack is empty */
ds@1952
   113
    return s;
ds@1952
   114
  } /* if */
ds@1952
   115
  stkidx--;
ds@1952
   116
  assert(stack!=NULL);
ds@1952
   117
  return stack[stkidx];
ds@1952
   118
}
ds@1952
   119
ds@1952
   120
SC_FUNC void clearstk(void)
ds@1952
   121
{
ds@1952
   122
  assert(stack!=NULL || stktop==0);
ds@1952
   123
  if (stack!=NULL) {
ds@1952
   124
    free(stack);
ds@1952
   125
    stack=NULL;
ds@1952
   126
    stktop=0;
ds@1952
   127
  } /* if */
ds@1952
   128
  assert(stktop==0);
ds@1952
   129
}
ds@1952
   130
ds@1952
   131
SC_FUNC int plungequalifiedfile(char *name)
ds@1952
   132
{
ds@1952
   133
static char *extensions[] = { ".inc", ".p", ".pawn" };
ds@1952
   134
  FILE *fp;
ds@1952
   135
  char *ext;
ds@1952
   136
  int ext_idx;
ds@1952
   137
ds@1952
   138
  ext_idx=0;
ds@1952
   139
  do {
ds@1952
   140
    fp=(FILE*)pc_opensrc(name);
ds@1952
   141
    ext=strchr(name,'\0');      /* save position */
ds@1952
   142
    if (fp==NULL) {
ds@1952
   143
      /* try to append an extension */
ds@1952
   144
      strcpy(ext,extensions[ext_idx]);
ds@1952
   145
      fp=(FILE*)pc_opensrc(name);
ds@1952
   146
      if (fp==NULL)
ds@1952
   147
        *ext='\0';              /* on failure, restore filename */
ds@1952
   148
    } /* if */
ds@1952
   149
    ext_idx++;
ds@1952
   150
  } while (fp==NULL && ext_idx<(sizeof extensions / sizeof extensions[0]));
ds@1952
   151
  if (fp==NULL) {
ds@1952
   152
    *ext='\0';                  /* restore filename */
ds@1952
   153
    return FALSE;
ds@1952
   154
  } /* if */
ds@1952
   155
  PUSHSTK_P(inpf);
ds@1952
   156
  PUSHSTK_P(inpfname);          /* pointer to current file name */
ds@1952
   157
  PUSHSTK_P(curlibrary);
ds@1952
   158
  PUSHSTK_I(iflevel);
ds@1952
   159
  assert(!SKIPPING);
ds@1952
   160
  assert(skiplevel==iflevel);   /* these two are always the same when "parsing" */
ds@1952
   161
  PUSHSTK_I(sc_is_utf8);
ds@1952
   162
  PUSHSTK_I(icomment);
ds@1952
   163
  PUSHSTK_I(fcurrent);
ds@1952
   164
  PUSHSTK_I(fline);
ds@1952
   165
  inpfname=duplicatestring(name);/* set name of include file */
ds@1952
   166
  if (inpfname==NULL)
ds@1952
   167
    error(123);             /* insufficient memory */
ds@1952
   168
  inpf=fp;                  /* set input file pointer to include file */
ds@1952
   169
  fnumber++;
ds@1952
   170
  fline=0;                  /* set current line number to 0 */
ds@1952
   171
  fcurrent=fnumber;
ds@1952
   172
  icomment=0;               /* not in a comment */
ds@1952
   173
  insert_dbgfile(inpfname);
ds@1952
   174
  setfiledirect(inpfname);
ds@1952
   175
  listline=-1;              /* force a #line directive when changing the file */
ds@1952
   176
  sc_is_utf8=(short)scan_utf8(inpf,name);
ds@1952
   177
  return TRUE;
ds@1952
   178
}
ds@1952
   179
ds@1952
   180
SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths)
ds@1952
   181
{
ds@1952
   182
  int result=FALSE;
ds@1952
   183
ds@1952
   184
  if (try_currentpath) {
ds@1952
   185
    result=plungequalifiedfile(name);
ds@1952
   186
    if (!result) {
ds@1952
   187
      /* failed to open the file in the active directory, try to open the file
ds@1952
   188
       * in the same directory as the current file --but first check whether
ds@1952
   189
       * there is a (relative) path for the current file
ds@1952
   190
       */
ds@1952
   191
      char *ptr;
ds@1952
   192
      if ((ptr=strrchr(inpfname,DIRSEP_CHAR))!=0) {
ds@1952
   193
        int len=(int)(ptr-inpfname)+1;
ds@1952
   194
        if (len+strlen(name)<_MAX_PATH) {
ds@1952
   195
          char path[_MAX_PATH];
ds@1952
   196
          strlcpy(path,inpfname,len+1);
ds@1952
   197
          strlcat(path,name,sizeof path);
ds@1952
   198
          result=plungequalifiedfile(path);
ds@1952
   199
        } /* if */
ds@1952
   200
      } /* if */
ds@1952
   201
    } /* if */
ds@1952
   202
  } /* if */
ds@1952
   203
ds@1952
   204
  if (try_includepaths && name[0]!=DIRSEP_CHAR) {
ds@1952
   205
    int i;
ds@1952
   206
    char *ptr;
ds@1952
   207
    for (i=0; !result && (ptr=get_path(i))!=NULL; i++) {
ds@1952
   208
      char path[_MAX_PATH];
ds@1952
   209
      strlcpy(path,ptr,sizeof path);
ds@1952
   210
      strlcat(path,name,sizeof path);
ds@1952
   211
      result=plungequalifiedfile(path);
ds@1952
   212
    } /* while */
ds@1952
   213
  } /* if */
ds@1952
   214
  return result;
ds@1952
   215
}
ds@1952
   216
ds@1952
   217
static void check_empty(const unsigned char *lptr)
ds@1952
   218
{
ds@1952
   219
  /* verifies that the string contains only whitespace */
ds@1952
   220
  while (*lptr<=' ' && *lptr!='\0')
ds@1952
   221
    lptr++;
ds@1952
   222
  if (*lptr!='\0')
ds@1952
   223
    error(38);          /* extra characters on line */
ds@1952
   224
}
ds@1952
   225
ds@1952
   226
/*  doinclude
ds@1952
   227
 *
ds@1952
   228
 *  Gets the name of an include file, pushes the old file on the stack and
ds@1952
   229
 *  sets some options. This routine doesn't use lex(), since lex() doesn't
ds@1952
   230
 *  recognize file names (and directories).
ds@1952
   231
 *
ds@1952
   232
 *  Global references: inpf     (altered)
ds@1952
   233
 *                     inpfname (altered)
ds@1952
   234
 *                     fline    (altered)
ds@1952
   235
 *                     lptr     (altered)
ds@1952
   236
 */
ds@1952
   237
static void doinclude(int silent)
ds@1952
   238
{
ds@1952
   239
  char name[_MAX_PATH];
ds@1952
   240
  char symname[sNAMEMAX];
ds@1952
   241
  char *ptr;
ds@1952
   242
  char c;
ds@1952
   243
  int i, result;
ds@1952
   244
ds@1952
   245
  while (*lptr<=' ' && *lptr!='\0')         /* skip leading whitespace */
ds@1952
   246
    lptr++;
ds@1952
   247
  if (*lptr=='<' || *lptr=='\"'){
ds@1952
   248
    c=(char)((*lptr=='\"') ? '\"' : '>');   /* termination character */
ds@1952
   249
    lptr++;
ds@1952
   250
    while (*lptr<=' ' && *lptr!='\0')       /* skip whitespace after quote */
ds@1952
   251
      lptr++;
ds@1952
   252
  } else {
ds@1952
   253
    c='\0';
ds@1952
   254
  } /* if */
ds@1952
   255
ds@1952
   256
  i=0;
ds@1952
   257
  while (*lptr!=c && *lptr!='\0' && i<sizeof name - 1)  /* find the end of the string */
ds@1952
   258
    name[i++]=*lptr++;
ds@1952
   259
  while (i>0 && name[i-1]<=' ')
ds@1952
   260
    i--;                        /* strip trailing whitespace */
ds@1952
   261
  assert(i>=0 && i<sizeof name);
ds@1952
   262
  name[i]='\0';                 /* zero-terminate the string */
ds@1952
   263
ds@1952
   264
  if (*lptr!=c) {               /* verify correct string termination */
ds@1952
   265
    error(37);                  /* invalid string */
ds@1952
   266
    return;
ds@1952
   267
  } /* if */
ds@1952
   268
  if (c!='\0')
ds@1952
   269
    check_empty(lptr+1);        /* verify that the rest of the line is whitespace */
ds@1952
   270
ds@1952
   271
  /* create a symbol from the name of the include file; this allows the system
ds@1952
   272
   * to test for multiple inclusions
ds@1952
   273
   */
ds@1952
   274
  strcpy(symname,"_inc_");
ds@1952
   275
  if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
ds@1952
   276
    strlcat(symname,ptr+1,sizeof symname);
ds@1952
   277
  else
ds@1952
   278
    strlcat(symname,name,sizeof symname);
ds@1952
   279
  if (find_symbol(&glbtab,symname,fcurrent,-1,NULL)==NULL) {
ds@1952
   280
    /* constant is not present, so this file has not been included yet */
ds@1952
   281
ds@1952
   282
    /* Include files between "..." or without quotes are read from the current
ds@1952
   283
     * directory, or from a list of "include directories". Include files
ds@1952
   284
     * between <...> are only read from the list of include directories.
ds@1952
   285
     */
ds@1952
   286
    result=plungefile(name,(c!='>'),TRUE);
ds@1952
   287
    if (result)
ds@1952
   288
      add_constant(symname,1,sGLOBAL,0);
ds@1952
   289
    else if (!silent)
ds@1952
   290
      error(120,name);            /* cannot read from ... (fatal error) */
ds@1952
   291
  } /* if */
ds@1952
   292
}
ds@1952
   293
ds@1952
   294
/*  readline
ds@1952
   295
 *
ds@1952
   296
 *  Reads in a new line from the input file pointed to by "inpf". readline()
ds@1952
   297
 *  concatenates lines that end with a \ with the next line. If no more data
ds@1952
   298
 *  can be read from the file, readline() attempts to pop off the previous file
ds@1952
   299
 *  from the stack. If that fails too, it sets "freading" to 0.
ds@1952
   300
 *
ds@1952
   301
 *  Global references: inpf,fline,inpfname,freading,icomment (altered)
ds@1952
   302
 */
ds@1952
   303
static void readline(unsigned char *line)
ds@1952
   304
{
ds@1952
   305
  int i,num,cont;
ds@1952
   306
  unsigned char *ptr;
ds@1952
   307
ds@1952
   308
  if (lptr==term_expr)
ds@1952
   309
    return;
ds@1952
   310
  num=sLINEMAX;
ds@1952
   311
  cont=FALSE;
ds@1952
   312
  do {
ds@1952
   313
    if (inpf==NULL || pc_eofsrc(inpf)) {
ds@1952
   314
      if (cont)
ds@1952
   315
        error(49);        /* invalid line continuation */
ds@1952
   316
      if (inpf!=NULL && inpf!=inpf_org)
ds@1952
   317
        pc_closesrc(inpf);
ds@1952
   318
      i=POPSTK_I();
ds@1952
   319
      if (i==-1) {        /* All's done; popstk() returns "stack is empty" */
ds@1952
   320
        freading=FALSE;
ds@1952
   321
        *line='\0';
ds@1952
   322
        /* when there is nothing more to read, the #if/#else stack should
ds@1952
   323
         * be empty and we should not be in a comment
ds@1952
   324
         */
ds@1952
   325
        assert(iflevel>=0);
ds@1952
   326
        if (iflevel>0)
ds@1952
   327
          error(1,"#endif","-end of file-");
ds@1952
   328
        else if (icomment!=0)
ds@1952
   329
          error(1,"*/","-end of file-");
ds@1952
   330
        return;
ds@1952
   331
      } /* if */
ds@1952
   332
      fline=i;
ds@1952
   333
      fcurrent=(short)POPSTK_I();
ds@1952
   334
      icomment=(short)POPSTK_I();
ds@1952
   335
      sc_is_utf8=(short)POPSTK_I();
ds@1952
   336
      iflevel=(short)POPSTK_I();
ds@1952
   337
      skiplevel=iflevel;        /* this condition held before including the file */
ds@1952
   338
      assert(!SKIPPING);        /* idem ditto */
ds@1952
   339
      curlibrary=(constvalue *)POPSTK_P();
ds@1952
   340
      free(inpfname);           /* return memory allocated for the include file name */
ds@1952
   341
      inpfname=(char *)POPSTK_P();
ds@1952
   342
      inpf=(FILE *)POPSTK_P();
ds@1952
   343
      insert_dbgfile(inpfname);
ds@1952
   344
      setfiledirect(inpfname);
ds@1952
   345
      listline=-1;              /* force a #line directive when changing the file */
ds@1952
   346
    } /* if */
ds@1952
   347
ds@1952
   348
    if (pc_readsrc(inpf,line,num)==NULL) {
ds@1952
   349
      *line='\0';     /* delete line */
ds@1952
   350
      cont=FALSE;
ds@1952
   351
    } else {
ds@1952
   352
      /* check whether to erase leading spaces */
ds@1952
   353
      if (cont) {
ds@1952
   354
        unsigned char *ptr=line;
ds@1952
   355
        while (*ptr<=' ' && *ptr!='\0')
ds@1952
   356
          ptr++;
ds@1952
   357
        if (ptr!=line)
ds@1952
   358
          memmove(line,ptr,strlen((char*)ptr)+1);
ds@1952
   359
      } /* if */
ds@1952
   360
      cont=FALSE;
ds@1952
   361
      /* check whether a full line was read */
ds@1952
   362
      if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf))
ds@1952
   363
        error(75);      /* line too long */
ds@1952
   364
      /* check if the next line must be concatenated to this line */
ds@1952
   365
      if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL)
ds@1952
   366
        ptr=(unsigned char*)strchr((char*)line,'\r');
ds@1952
   367
      if (ptr!=NULL && ptr>line) {
ds@1952
   368
        assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */
ds@1952
   369
        while (ptr>line && *ptr<=' ')
ds@1952
   370
          ptr--;        /* skip trailing whitespace */
ds@1952
   371
        if (*ptr=='\\') {
ds@1952
   372
          cont=TRUE;
ds@1952
   373
          /* set '\a' at the position of '\\' to make it possible to check
ds@1952
   374
           * for a line continuation in a single line comment (error 49)
ds@1952
   375
           */
ds@1952
   376
          *ptr++='\a';
ds@1952
   377
          *ptr='\0';    /* erase '\n' (and any trailing whitespace) */
ds@1952
   378
        } /* if */
ds@1952
   379
      } /* if */
ds@1952
   380
      num-=strlen((char*)line);
ds@1952
   381
      line+=strlen((char*)line);
ds@1952
   382
    } /* if */
ds@1952
   383
    fline+=1;
ds@1952
   384
  } while (num>=0 && cont);
ds@1952
   385
}
ds@1952
   386
ds@1952
   387
/*  stripcom
ds@1952
   388
 *
ds@1952
   389
 *  Replaces all comments from the line by space characters. It updates
ds@1952
   390
 *  a global variable ("icomment") for multiline comments.
ds@1952
   391
 *
ds@1952
   392
 *  This routine also supports the C++ extension for single line comments.
ds@1952
   393
 *  These comments are started with "//" and end at the end of the line.
ds@1952
   394
 *
ds@1952
   395
 *  The function also detects (and manages) "documentation comments". The
ds@1952
   396
 *  global variable "icomment" is set to 2 for documentation comments.
ds@1952
   397
 *
ds@1952
   398
 *  Global references: icomment  (private to "stripcom")
ds@1952
   399
 */
ds@1952
   400
static void stripcom(unsigned char *line)
ds@1952
   401
{
ds@1952
   402
  char c;
ds@1952
   403
  #if !defined SC_LIGHT
ds@1952
   404
    #define COMMENT_LIMIT 100
ds@1952
   405
    #define COMMENT_MARGIN 40   /* length of the longest word */
ds@1952
   406
    char comment[COMMENT_LIMIT+COMMENT_MARGIN];
ds@1952
   407
    int commentidx=0;
ds@1952
   408
    int skipstar=TRUE;
ds@1952
   409
    static int prev_singleline=FALSE;
ds@1952
   410
    int singleline=prev_singleline;
ds@1952
   411
ds@1952
   412
    prev_singleline=FALSE;  /* preset */
ds@1952
   413
  #endif
ds@1952
   414
ds@1952
   415
  while (*line){
ds@1952
   416
    if (icomment!=0) {
ds@1952
   417
      if (*line=='*' && *(line+1)=='/') {
ds@1952
   418
        #if !defined SC_LIGHT
ds@1952
   419
          if (icomment==2) {
ds@1952
   420
            assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
ds@1952
   421
            comment[commentidx]='\0';
ds@1952
   422
            if (strlen(comment)>0)
ds@1952
   423
              insert_docstring(comment);
ds@1952
   424
          } /* if */
ds@1952
   425
        #endif
ds@1952
   426
        icomment=0;     /* comment has ended */
ds@1952
   427
        *line=' ';      /* replace '*' and '/' characters by spaces */
ds@1952
   428
        *(line+1)=' ';
ds@1952
   429
        line+=2;
ds@1952
   430
      } else {
ds@1952
   431
        if (*line=='/' && *(line+1)=='*')
ds@1952
   432
          error(216);   /* nested comment */
ds@1952
   433
        #if !defined SC_LIGHT
ds@1952
   434
          /* collect the comment characters in a string */
ds@1952
   435
          if (icomment==2) {
ds@1952
   436
            if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) {
ds@1952
   437
              /* ignore leading whitespace and '*' characters */
ds@1952
   438
            } else if (commentidx<COMMENT_LIMIT+COMMENT_MARGIN-1) {
ds@1952
   439
              comment[commentidx++]=(char)((*line!='\n') ? *line : ' ');
ds@1952
   440
              if (commentidx>COMMENT_LIMIT && *line!='\0' && *line<=' ') {
ds@1952
   441
                comment[commentidx]='\0';
ds@1952
   442
                insert_docstring(comment);
ds@1952
   443
                commentidx=0;
ds@1952
   444
              } /* if */
ds@1952
   445
              skipstar=FALSE;
ds@1952
   446
            } /* if */
ds@1952
   447
          } /* if */
ds@1952
   448
        #endif
ds@1952
   449
        *line=' ';      /* replace comments by spaces */
ds@1952
   450
        line+=1;
ds@1952
   451
      } /* if */
ds@1952
   452
    } else {
ds@1952
   453
      if (*line=='/' && *(line+1)=='*'){
ds@1952
   454
        icomment=1;     /* start comment */
ds@1952
   455
        #if !defined SC_LIGHT
ds@1952
   456
          /* there must be two "*" behind the slash and then white space */
ds@1952
   457
          if (*(line+2)=='*' && *(line+3)<=' ') {
ds@1952
   458
            /* if we are not in a function, we must attach the previous block
ds@1952
   459
             * to the global documentation
ds@1952
   460
             */
ds@1952
   461
            if (curfunc==NULL && get_docstring(0)!=NULL)
ds@1952
   462
              sc_attachdocumentation(NULL);
ds@1952
   463
            icomment=2; /* documentation comment */
ds@1952
   464
          } /* if */
ds@1952
   465
          commentidx=0;
ds@1952
   466
          skipstar=TRUE;
ds@1952
   467
        #endif
ds@1952
   468
        *line=' ';      /* replace '/' and '*' characters by spaces */
ds@1952
   469
        *(line+1)=' ';
ds@1952
   470
        line+=2;
ds@1952
   471
        if (icomment==2)
ds@1952
   472
          *line++=' ';
ds@1952
   473
      } else if (*line=='/' && *(line+1)=='/'){  /* comment to end of line */
ds@1952
   474
        if (strchr((char*)line,'\a')!=NULL)
ds@1952
   475
          error(49);    /* invalid line continuation */
ds@1952
   476
        #if !defined SC_LIGHT
ds@1952
   477
          if (*(line+2)=='/' && *(line+3)<=' ') {
ds@1952
   478
            /* documentation comment */
ds@1952
   479
            char *str=(char*)line+3;
ds@1952
   480
            char *end;
ds@1952
   481
            while (*str<=' ' && *str!='\0')
ds@1952
   482
              str++;    /* skip leading whitespace */
ds@1952
   483
            if ((end=strrchr(str,'\n'))!=NULL)
ds@1952
   484
              *end='\0';/* erase trailing '\n' */
ds@1952
   485
            /* if there is a disjunct block, we may need to attach the previous
ds@1952
   486
             * block to the global documentation
ds@1952
   487
             */
ds@1952
   488
            if (!singleline && curfunc==NULL && get_docstring(0)!=NULL)
ds@1952
   489
              sc_attachdocumentation(NULL);
ds@1952
   490
            insert_docstring(str);
ds@1952
   491
            prev_singleline=TRUE;
ds@1952
   492
          } /* if */
ds@1952
   493
        #endif
ds@1952
   494
        *line++='\n';   /* put "newline" at first slash */
ds@1952
   495
        *line='\0';     /* put "zero-terminator" at second slash */
ds@1952
   496
      } else {
ds@1952
   497
        if (*line=='\"' || *line=='\''){        /* leave literals unaltered */
ds@1952
   498
          c=*line;      /* ending quote, single or double */
ds@1952
   499
          line+=1;
ds@1952
   500
          while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0')
ds@1952
   501
            line+=1;
ds@1952
   502
          line+=1;      /* skip final quote */
ds@1952
   503
        } else {
ds@1952
   504
          line+=1;
ds@1952
   505
        } /* if */
ds@1952
   506
      } /* if */
ds@1952
   507
    } /* if */
ds@1952
   508
  } /* while */
ds@1952
   509
  #if !defined SC_LIGHT
ds@1952
   510
    if (icomment==2) {
ds@1952
   511
      assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
ds@1952
   512
      comment[commentidx]='\0';
ds@1952
   513
      if (strlen(comment)>0)
ds@1952
   514
        insert_docstring(comment);
ds@1952
   515
    } /* if */
ds@1952
   516
  #endif
ds@1952
   517
}
ds@1952
   518
ds@1952
   519
/*  btoi
ds@1952
   520
 *
ds@1952
   521
 *  Attempts to interpret a numeric symbol as a boolean value. On success
ds@1952
   522
 *  it returns the number of characters processed (so the line pointer can be
ds@1952
   523
 *  adjusted) and the value is stored in "val". Otherwise it returns 0 and
ds@1952
   524
 *  "val" is garbage.
ds@1952
   525
 *
ds@1952
   526
 *  A boolean value must start with "0b"
ds@1952
   527
 */
ds@1952
   528
static int btoi(cell *val,const unsigned char *curptr)
ds@1952
   529
{
ds@1952
   530
  const unsigned char *ptr;
ds@1952
   531
ds@1952
   532
  *val=0;
ds@1952
   533
  ptr=curptr;
ds@1952
   534
  if (*ptr=='0' && *(ptr+1)=='b') {
ds@1952
   535
    ptr+=2;
ds@1952
   536
    while (*ptr=='0' || *ptr=='1' || *ptr=='_') {
ds@1952
   537
      if (*ptr!='_')
ds@1952
   538
        *val=(*val<<1) | (*ptr-'0');
ds@1952
   539
      ptr++;
ds@1952
   540
    } /* while */
ds@1952
   541
  } else {
ds@1952
   542
    return 0;
ds@1952
   543
  } /* if */
ds@1952
   544
  if (alphanum(*ptr))   /* number must be delimited by non-alphanumeric char */
ds@1952
   545
    return 0;
ds@1952
   546
  else
ds@1952
   547
    return (int)(ptr-curptr);
ds@1952
   548
}
ds@1952
   549
ds@1952
   550
/*  dtoi
ds@1952
   551
 *
ds@1952
   552
 *  Attempts to interpret a numeric symbol as a decimal value. On success
ds@1952
   553
 *  it returns the number of characters processed and the value is stored in
ds@1952
   554
 *  "val". Otherwise it returns 0 and "val" is garbage.
ds@1952
   555
 */
ds@1952
   556
static int dtoi(cell *val,const unsigned char *curptr)
ds@1952
   557
{
ds@1952
   558
  const unsigned char *ptr;
ds@1952
   559
ds@1952
   560
  *val=0;
ds@1952
   561
  ptr=curptr;
ds@1952
   562
  if (!isdigit(*ptr))   /* should start with digit */
ds@1952
   563
    return 0;
ds@1952
   564
  while (isdigit(*ptr) || *ptr=='_') {
ds@1952
   565
    if (*ptr!='_')
ds@1952
   566
      *val=(*val*10)+(*ptr-'0');
ds@1952
   567
    ptr++;
ds@1952
   568
  } /* while */
ds@1952
   569
  if (alphanum(*ptr))   /* number must be delimited by non-alphanumerical */
ds@1952
   570
    return 0;
ds@1952
   571
  if (*ptr=='.' && isdigit(*(ptr+1)))
ds@1952
   572
    return 0;           /* but a fractional part must not be present */
ds@1952
   573
  return (int)(ptr-curptr);
ds@1952
   574
}
ds@1952
   575
ds@1952
   576
/*  htoi
ds@1952
   577
 *
ds@1952
   578
 *  Attempts to interpret a numeric symbol as a hexadecimal value. On
ds@1952
   579
 *  success it returns the number of characters processed and the value is
ds@1952
   580
 *  stored in "val". Otherwise it return 0 and "val" is garbage.
ds@1952
   581
 */
ds@1952
   582
static int htoi(cell *val,const unsigned char *curptr)
ds@1952
   583
{
ds@1952
   584
  const unsigned char *ptr;
ds@1952
   585
ds@1952
   586
  *val=0;
ds@1952
   587
  ptr=curptr;
ds@1952
   588
  if (!isdigit(*ptr))   /* should start with digit */
ds@1952
   589
    return 0;
ds@1952
   590
  if (*ptr=='0' && *(ptr+1)=='x') {     /* C style hexadecimal notation */
ds@1952
   591
    ptr+=2;
ds@1952
   592
    while (ishex(*ptr) || *ptr=='_') {
ds@1952
   593
      if (*ptr!='_') {
ds@1952
   594
        assert(ishex(*ptr));
ds@1952
   595
        *val= *val<<4;
ds@1952
   596
        if (isdigit(*ptr))
ds@1952
   597
          *val+= (*ptr-'0');
ds@1952
   598
        else
ds@1952
   599
          *val+= (tolower(*ptr)-'a'+10);
ds@1952
   600
      } /* if */
ds@1952
   601
      ptr++;
ds@1952
   602
    } /* while */
ds@1952
   603
  } else {
ds@1952
   604
    return 0;
ds@1952
   605
  } /* if */
ds@1952
   606
  if (alphanum(*ptr))
ds@1952
   607
    return 0;
ds@1952
   608
  else
ds@1952
   609
    return (int)(ptr-curptr);
ds@1952
   610
}
ds@1952
   611
ds@1952
   612
/*  ftoi
ds@1952
   613
 *
ds@1952
   614
 *  Attempts to interpret a numeric symbol as a rational number, either as
ds@1952
   615
 *  IEEE 754 single/double precision floating point or as a fixed point integer.
ds@1952
   616
 *  On success it returns the number of characters processed and the value is
ds@1952
   617
 *  stored in "val". Otherwise it returns 0 and "val" is unchanged.
ds@1952
   618
 *
ds@1952
   619
 *  Pawn has stricter definition for rational numbers than most:
ds@1952
   620
 *  o  the value must start with a digit; ".5" is not a valid number, you
ds@1952
   621
 *     should write "0.5"
ds@1952
   622
 *  o  a period must appear in the value, even if an exponent is given; "2e3"
ds@1952
   623
 *     is not a valid number, you should write "2.0e3"
ds@1952
   624
 *  o  at least one digit must follow the period; "6." is not a valid number,
ds@1952
   625
 *     you should write "6.0"
ds@1952
   626
 */
ds@1952
   627
static int ftoi(cell *val,const unsigned char *curptr)
ds@1952
   628
{
ds@1952
   629
  const unsigned char *ptr;
ds@1952
   630
  double fnum,ffrac,fmult;
ds@1952
   631
  unsigned long dnum,dbase;
ds@1952
   632
  int i, ignore;
ds@1952
   633
ds@1952
   634
  assert(rational_digits>=0 && rational_digits<9);
ds@1952
   635
  for (i=0,dbase=1; i<rational_digits; i++)
ds@1952
   636
    dbase*=10;
ds@1952
   637
  fnum=0.0;
ds@1952
   638
  dnum=0L;
ds@1952
   639
  ptr=curptr;
ds@1952
   640
  if (!isdigit(*ptr))   /* should start with digit */
ds@1952
   641
    return 0;
ds@1952
   642
  while (isdigit(*ptr) || *ptr=='_') {
ds@1952
   643
    if (*ptr!='_') {
ds@1952
   644
      fnum=(fnum*10.0)+(*ptr-'0');
ds@1952
   645
      dnum=(dnum*10L)+(*ptr-'0')*dbase;
ds@1952
   646
    } /* if */
ds@1952
   647
    ptr++;
ds@1952
   648
  } /* while */
ds@1952
   649
  if (*ptr!='.')
ds@1952
   650
    return 0;           /* there must be a period */
ds@1952
   651
  ptr++;
ds@1952
   652
  if (!isdigit(*ptr))   /* there must be at least one digit after the dot */
ds@1952
   653
    return 0;
ds@1952
   654
  ffrac=0.0;
ds@1952
   655
  fmult=1.0;
ds@1952
   656
  ignore=FALSE;
ds@1952
   657
  while (isdigit(*ptr) || *ptr=='_') {
ds@1952
   658
    if (*ptr!='_') {
ds@1952
   659
      ffrac=(ffrac*10.0)+(*ptr-'0');
ds@1952
   660
      fmult=fmult/10.0;
ds@1952
   661
      dbase /= 10L;
ds@1952
   662
      dnum += (*ptr-'0')*dbase;
ds@1952
   663
      if (dbase==0L && sc_rationaltag && rational_digits>0 && !ignore) {
ds@1952
   664
        error(222);     /* number of digits exceeds rational number precision */
ds@1952
   665
        ignore=TRUE;
ds@1952
   666
      } /* if */
ds@1952
   667
    } /* if */
ds@1952
   668
    ptr++;
ds@1952
   669
  } /* while */
ds@1952
   670
  fnum += ffrac*fmult;  /* form the number so far */
ds@1952
   671
  if (*ptr=='e') {      /* optional fractional part */
ds@1952
   672
    int exp,sign;
ds@1952
   673
    ptr++;
ds@1952
   674
    if (*ptr=='-') {
ds@1952
   675
      sign=-1;
ds@1952
   676
      ptr++;
ds@1952
   677
    } else {
ds@1952
   678
      sign=1;
ds@1952
   679
    } /* if */
ds@1952
   680
    if (!isdigit(*ptr)) /* 'e' should be followed by a digit */
ds@1952
   681
      return 0;
ds@1952
   682
    exp=0;
ds@1952
   683
    while (isdigit(*ptr)) {
ds@1952
   684
      exp=(exp*10)+(*ptr-'0');
ds@1952
   685
      ptr++;
ds@1952
   686
    } /* while */
ds@1952
   687
    #if defined __GNUC__
ds@1952
   688
      fmult=pow10(exp*sign);
ds@1952
   689
    #else
ds@1952
   690
      fmult=pow(10,exp*sign);
ds@1952
   691
    #endif
ds@1952
   692
    fnum *= fmult;
ds@1952
   693
    dnum *= (unsigned long)(fmult+0.5);
ds@1952
   694
  } /* if */
ds@1952
   695
ds@1952
   696
  /* decide how to store the number */
ds@1952
   697
  if (sc_rationaltag==0) {
ds@1952
   698
    error(70);          /* rational number support was not enabled */
ds@1952
   699
    *val=0;
ds@1952
   700
  } else if (rational_digits==0) {
ds@1952
   701
    /* floating point */
ds@1952
   702
    #if PAWN_CELL_SIZE==32
ds@1952
   703
      float value=(float)fnum;
ds@1952
   704
      *val=*((cell *)&value);
ds@1952
   705
      #if 0 /* SourceMod - not needed */
ds@1952
   706
        /* I assume that the C/C++ compiler stores "float" values in IEEE 754
ds@1952
   707
         * format (as mandated in the ANSI standard). Test this assumption
ds@1952
   708
         * anyway.
ds@1952
   709
         * Note: problems have been reported with GCC 3.2.x, version 3.3.x works.
ds@1952
   710
         */
ds@1952
   711
        { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
ds@1952
   712
          uint32_t bit = 1;
ds@1952
   713
          /* test 0.0 == all bits 0 */
ds@1952
   714
          assert(*(uint32_t*)&test1==0x00000000L);
ds@1952
   715
          /* test sign & magnitude format */
ds@1952
   716
          assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
ds@1952
   717
          /* test a known value */
ds@1952
   718
          assert(*(uint32_t*)&test2==0x42480000L);
ds@1952
   719
        }
ds@1952
   720
      #endif
ds@1952
   721
    #elif PAWN_CELL_SIZE==64
ds@1952
   722
      *val=*((cell *)&fnum);
ds@1952
   723
      #if 0 /* SourceMod - not needed */
ds@1952
   724
        /* I assume that the C/C++ compiler stores "double" values in IEEE 754
ds@1952
   725
         * format (as mandated in the ANSI standard).
ds@1952
   726
         */
ds@1952
   727
        { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
ds@1952
   728
          uint64_t bit = 1;
ds@1952
   729
          /* test 0.0 == all bits 0 */
ds@1952
   730
          assert(*(uint64_t*)&test1==0x00000000L);
ds@1952
   731
          /* test sign & magnitude format */
ds@1952
   732
          assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
ds@1952
   733
        }
ds@1952
   734
      #endif
ds@1952
   735
    #else
ds@1952
   736
      #error Unsupported cell size
ds@1952
   737
    #endif
ds@1952
   738
  } else {
ds@1952
   739
    /* fixed point */
ds@1952
   740
    *val=(cell)dnum;
ds@1952
   741
  } /* if */
ds@1952
   742
ds@1952
   743
  return (int)(ptr-curptr);
ds@1952
   744
}
ds@1952
   745
ds@1952
   746
/*  number
ds@1952
   747
 *
ds@1952
   748
 *  Reads in a number (binary, decimal or hexadecimal). It returns the number
ds@1952
   749
 *  of characters processed or 0 if the symbol couldn't be interpreted as a
ds@1952
   750
 *  number (in this case the argument "val" remains unchanged). This routine
ds@1952
   751
 *  relies on the 'early dropout' implementation of the logical or (||)
ds@1952
   752
 *  operator.
ds@1952
   753
 *
ds@1952
   754
 *  Note: the routine doesn't check for a sign (+ or -). The - is checked
ds@1952
   755
 *        for at "hier2()" (in fact, it is viewed as an operator, not as a
ds@1952
   756
 *        sign) and the + is invalid (as in K&R C, and unlike ANSI C).
ds@1952
   757
 */
ds@1952
   758
static int number(cell *val,const unsigned char *curptr)
ds@1952
   759
{
ds@1952
   760
  int i;
ds@1952
   761
  cell value;
ds@1952
   762
ds@1952
   763
  if ((i=btoi(&value,curptr))!=0      /* binary? */
ds@1952
   764
      || (i=htoi(&value,curptr))!=0   /* hexadecimal? */
ds@1952
   765
      || (i=dtoi(&value,curptr))!=0)  /* decimal? */
ds@1952
   766
  {
ds@1952
   767
    *val=value;
ds@1952
   768
    return i;
ds@1952
   769
  } else {
ds@1952
   770
    return 0;                      /* else not a number */
ds@1952
   771
  } /* if */
ds@1952
   772
}
ds@1952
   773
ds@1952
   774
static void chrcat(char *str,char chr)
ds@1952
   775
{
ds@1952
   776
  str=strchr(str,'\0');
ds@1952
   777
  *str++=chr;
ds@1952
   778
  *str='\0';
ds@1952
   779
}
ds@1952
   780
ds@1952
   781
static int preproc_expr(cell *val,int *tag)
ds@1952
   782
{
ds@1952
   783
  int result;
ds@1952
   784
  int index;
ds@1952
   785
  cell code_index;
ds@1952
   786
  char *term;
ds@1952
   787
ds@1952
   788
  /* Disable staging; it should be disabled already because
ds@1952
   789
   * expressions may not be cut off half-way between conditional
ds@1952
   790
   * compilations. Reset the staging index, but keep the code
ds@1952
   791
   * index.
ds@1952
   792
   */
ds@1952
   793
  if (stgget(&index,&code_index)) {
ds@1952
   794
    error(57);                          /* unfinished expression */
ds@1952
   795
    stgdel(0,code_index);
ds@1952
   796
    stgset(FALSE);
ds@1952
   797
  } /* if */
ds@1952
   798
  assert((lptr-pline)<(int)strlen((char*)pline));   /* lptr must point inside the string */
ds@1952
   799
  #if !defined NO_DEFINE
ds@1952
   800
    /* preprocess the string */
ds@1952
   801
    substallpatterns(pline,sLINEMAX);
ds@1952
   802
    assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */
ds@1952
   803
  #endif
ds@1952
   804
  /* append a special symbol to the string, so the expression
ds@1952
   805
   * analyzer won't try to read a next line when it encounters
ds@1952
   806
   * an end-of-line
ds@1952
   807
   */
ds@1952
   808
  assert(strlen((char*)pline)<sLINEMAX);
ds@1952
   809
  term=strchr((char*)pline,'\0');
ds@1952
   810
  assert(term!=NULL);
ds@1952
   811
  chrcat((char*)pline,PREPROC_TERM);    /* the "DEL" code (see SC.H) */
ds@1952
   812
  result=constexpr(val,tag,NULL);       /* get value (or 0 on error) */
ds@1952
   813
  *term='\0';                           /* erase the token (if still present) */
ds@1952
   814
  lexclr(FALSE);                        /* clear any "pushed" tokens */
ds@1952
   815
  return result;
ds@1952
   816
}
ds@1952
   817
ds@1952
   818
/* getstring
ds@1952
   819
 * Returns returns a pointer behind the closing quote or to the other
ds@1952
   820
 * character that caused the input to be ended.
ds@1952
   821
 */
ds@1952
   822
static const unsigned char *getstring(unsigned char *dest,int max,const unsigned char *line)
ds@1952
   823
{
ds@1952
   824
  assert(dest!=NULL && line!=NULL);
ds@1952
   825
  *dest='\0';
ds@1952
   826
  while (*line<=' ' && *line!='\0')
ds@1952
   827
    line++;             /* skip whitespace */
ds@1952
   828
  if (*line=='"') {
ds@1952
   829
    int len=0;
ds@1952
   830
    line++;             /* skip " */
ds@1952
   831
    while (*line!='"' && *line!='\0') {
ds@1952
   832
      if (len<max-1)
ds@1952
   833
        dest[len++]=*line;
ds@1952
   834
      line++;
ds@1952
   835
    } /* if */
ds@1952
   836
    dest[len]='\0';
ds@1952
   837
    if (*line=='"')
ds@1952
   838
      line++;           /* skip closing " */
ds@1952
   839
    else
ds@1952
   840
      error(37);        /* invalid string */
ds@1952
   841
  } else {
ds@1952
   842
    error(37);          /* invalid string */
ds@1952
   843
  } /* if */
ds@1952
   844
  return line;
ds@1952
   845
}
ds@1952
   846
ds@1952
   847
enum {
ds@1952
   848
  CMD_NONE,
ds@1952
   849
  CMD_TERM,
ds@1952
   850
  CMD_EMPTYLINE,
ds@1952
   851
  CMD_CONDFALSE,
ds@1952
   852
  CMD_INCLUDE,
ds@1952
   853
  CMD_DEFINE,
ds@1952
   854
  CMD_IF,
ds@1952
   855
  CMD_DIRECTIVE,
ds@1952
   856
};
ds@1952
   857
ds@1952
   858
/*  command
ds@1952
   859
 *
ds@1952
   860
 *  Recognizes the compiler directives. The function returns:
ds@1952
   861
 *     CMD_NONE         the line must be processed
ds@1952
   862
 *     CMD_TERM         a pending expression must be completed before processing further lines
ds@1952
   863
 *     Other value: the line must be skipped, because:
ds@1952
   864
 *     CMD_CONDFALSE    false "#if.." code
ds@1952
   865
 *     CMD_EMPTYLINE    line is empty
ds@1952
   866
 *     CMD_INCLUDE      the line contains a #include directive
ds@1952
   867
 *     CMD_DEFINE       the line contains a #subst directive
ds@1952
   868
 *     CMD_IF           the line contains a #if/#else/#endif directive
ds@1952
   869
 *     CMD_DIRECTIVE    the line contains some other compiler directive
ds@1952
   870
 *
ds@1952
   871
 *  Global variables: iflevel, ifstack (altered)
ds@1952
   872
 *                    lptr      (altered)
ds@1952
   873
 */
ds@1952
   874
static int command(void)
ds@1952
   875
{
ds@1952
   876
  int tok,ret;
ds@1952
   877
  cell val;
ds@1952
   878
  char *str;
ds@1952
   879
  int index;
ds@1952
   880
  cell code_index;
ds@1952
   881
ds@1952
   882
  while (*lptr<=' ' && *lptr!='\0')
ds@1952
   883
    lptr+=1;
ds@1952
   884
  if (*lptr=='\0')
ds@1952
   885
    return CMD_EMPTYLINE;       /* empty line */
ds@1952
   886
  if (*lptr!='#')
ds@1952
   887
    return SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* it is not a compiler directive */
ds@1952
   888
  /* compiler directive found */
ds@1952
   889
  indent_nowarn=TRUE;           /* allow loose indentation" */
ds@1952
   890
  lexclr(FALSE);                /* clear any "pushed" tokens */
ds@1952
   891
  /* on a pending expression, force to return a silent ';' token and force to
ds@1952
   892
   * re-read the line
ds@1952
   893
   */
ds@1952
   894
  if (!sc_needsemicolon && stgget(&index,&code_index)) {
ds@1952
   895
    lptr=term_expr;
ds@1952
   896
    return CMD_TERM;
ds@1952
   897
  } /* if */
ds@1952
   898
  tok=lex(&val,&str);
ds@1952
   899
  ret=SKIPPING ? CMD_CONDFALSE : CMD_DIRECTIVE;  /* preset 'ret' to CMD_DIRECTIVE (most common case) */
ds@1952
   900
  switch (tok) {
ds@1952
   901
  case tpIF:                    /* conditional compilation */
ds@1952
   902
    ret=CMD_IF;
ds@1952
   903
    assert(iflevel>=0);
ds@1952
   904
    if (iflevel>=sCOMP_STACK)
ds@1952
   905
      error(122,"Conditional compilation stack"); /* table overflow */
ds@1952
   906
    iflevel++;
ds@1952
   907
    if (SKIPPING)
ds@1952
   908
      break;                    /* break out of switch */
ds@1952
   909
    skiplevel=iflevel;
ds@1952
   910
    preproc_expr(&val,NULL);    /* get value (or 0 on error) */
ds@1952
   911
    ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
ds@1952
   912
    check_empty(lptr);
ds@1952
   913
    break;
ds@1952
   914
  case tpELSE:
ds@1952
   915
  case tpELSEIF:
ds@1952
   916
    ret=CMD_IF;
ds@1952
   917
    assert(iflevel>=0);
ds@1952
   918
    if (iflevel==0) {
ds@1952
   919
      error(26);                /* no matching #if */
ds@1952
   920
      errorset(sRESET,0);
ds@1952
   921
    } else {
ds@1952
   922
      /* check for earlier #else */
ds@1952
   923
      if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) {
ds@1952
   924
        if (tok==tpELSEIF)
ds@1952
   925
          error(61);            /* #elseif directive may not follow an #else */
ds@1952
   926
        else
ds@1952
   927
          error(60);            /* multiple #else directives between #if ... #endif */
ds@1952
   928
        errorset(sRESET,0);
ds@1952
   929
      } else {
ds@1952
   930
        assert(iflevel>0);
ds@1952
   931
        /* if there has been a "parse mode" on this level, set "skip mode",
ds@1952
   932
         * otherwise, clear "skip mode"
ds@1952
   933
         */
ds@1952
   934
        if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) {
ds@1952
   935
          /* there has been a parse mode already on this level, so skip the rest */
ds@1952
   936
          ifstack[iflevel-1] |= (char)SKIPMODE;
ds@1952
   937
          /* if we were already skipping this section, allow expressions with
ds@1952
   938
           * undefined symbols; otherwise check the expression to catch errors
ds@1952
   939
           */
ds@1952
   940
          if (tok==tpELSEIF) {
ds@1952
   941
            if (skiplevel==iflevel)
ds@1952
   942
              preproc_expr(&val,NULL);  /* get, but ignore the expression */
ds@1952
   943
            else
dvander@2783
   944
              lptr=(unsigned char*)strchr((char*)lptr,'\0');
ds@1952
   945
          } /* if */
ds@1952
   946
        } else {
ds@1952
   947
          /* previous conditions were all FALSE */
ds@1952
   948
          if (tok==tpELSEIF) {
ds@1952
   949
            /* if we were already skipping this section, allow expressions with
ds@1952
   950
             * undefined symbols; otherwise check the expression to catch errors
ds@1952
   951
             */
ds@1952
   952
            if (skiplevel==iflevel) {
ds@1952
   953
              preproc_expr(&val,NULL);  /* get value (or 0 on error) */
ds@1952
   954
            } else {
dvander@2783
   955
              lptr=(unsigned char*)strchr((char*)lptr,'\0');
ds@1952
   956
              val=0;
ds@1952
   957
            } /* if */
ds@1952
   958
            ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
ds@1952
   959
          } else {
ds@1952
   960
            /* a simple #else, clear skip mode */
ds@1952
   961
            ifstack[iflevel-1] &= (char)~SKIPMODE;
ds@1952
   962
          } /* if */
ds@1952
   963
        } /* if */
ds@1952
   964
      } /* if */
ds@1952
   965
    } /* if */
ds@1952
   966
    check_empty(lptr);
ds@1952
   967
    break;
ds@1952
   968
  case tpENDIF:
ds@1952
   969
    ret=CMD_IF;
ds@1952
   970
    if (iflevel==0){
ds@1952
   971
      error(26);        /* no matching "#if" */
ds@1952
   972
      errorset(sRESET,0);
ds@1952
   973
    } else {
ds@1952
   974
      iflevel--;
ds@1952
   975
      if (iflevel<skiplevel)
ds@1952
   976
        skiplevel=iflevel;
ds@1952
   977
    } /* if */
ds@1952
   978
    check_empty(lptr);
ds@1952
   979
    break;
ds@1952
   980
  case tINCLUDE:                /* #include directive */
ds@1952
   981
  case tpTRYINCLUDE:
ds@1952
   982
    ret=CMD_INCLUDE;
ds@1952
   983
    if (!SKIPPING)
ds@1952
   984
      doinclude(tok==tpTRYINCLUDE);
ds@1952
   985
    break;
ds@1952
   986
  case tpFILE:
ds@1952
   987
    if (!SKIPPING) {
ds@1952
   988
      char pathname[_MAX_PATH];
ds@1952
   989
      lptr=getstring((unsigned char*)pathname,sizeof pathname,lptr);
ds@1952
   990
      if (strlen(pathname)>0) {
ds@1952
   991
        free(inpfname);
ds@1952
   992
        inpfname=duplicatestring(pathname);
ds@1952
   993
        if (inpfname==NULL)
ds@1952
   994
          error(123);           /* insufficient memory */
ds@1952
   995
        fline=0;
ds@1952
   996
      } /* if */
ds@1952
   997
    } /* if */
ds@1952
   998
    check_empty(lptr);
ds@1952
   999
    break;
ds@1952
  1000
  case tpLINE:
ds@1952
  1001
    if (!SKIPPING) {
ds@1952
  1002
      if (lex(&val,&str)!=tNUMBER)
ds@1952
  1003
        error(8);               /* invalid/non-constant expression */
ds@1952
  1004
      fline=(int)val;
ds@1952
  1005
    } /* if */
ds@1952
  1006
    check_empty(lptr);
ds@1952
  1007
    break;
ds@1952
  1008
  case tpASSERT:
ds@1952
  1009
    if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) {
ds@1952
  1010
      for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++)
ds@1952
  1011
        /* nothing */;          /* save start of expression */
ds@1952
  1012
      preproc_expr(&val,NULL);  /* get constant expression (or 0 on error) */
ds@1952
  1013
      if (!val)
ds@1952
  1014
        error(130,str);         /* assertion failed */
ds@1952
  1015
      check_empty(lptr);
ds@1952
  1016
    } /* if */
ds@1952
  1017
    break;
ds@1952
  1018
  case tpPRAGMA:
ds@1952
  1019
    if (!SKIPPING) {
ds@1952
  1020
      if (lex(&val,&str)==tSYMBOL) {
ds@1952
  1021
        if (strcmp(str,"amxlimit")==0) {
ds@1952
  1022
          preproc_expr(&pc_amxlimit,NULL);
ds@1952
  1023
        } else if (strcmp(str,"amxram")==0) {
ds@1952
  1024
          preproc_expr(&pc_amxram,NULL);
ds@1952
  1025
        } else if (strcmp(str,"codepage")==0) {
ds@1952
  1026
          char name[sNAMEMAX+1];
ds@1952
  1027
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1028
            lptr++;
ds@1952
  1029
          if (*lptr=='"') {
ds@1952
  1030
            lptr=getstring((unsigned char*)name,sizeof name,lptr);
ds@1952
  1031
          } else {
ds@1952
  1032
            int i;
ds@1952
  1033
            for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
ds@1952
  1034
              name[i]=*lptr;
ds@1952
  1035
            name[i]='\0';
ds@1952
  1036
          } /* if */
ds@1952
  1037
          if (!cp_set(name))
ds@1952
  1038
            error(128);         /* codepage mapping file not found */
ds@1952
  1039
        } else if (strcmp(str,"compress")==0) {
ds@1952
  1040
          cell val;
ds@1952
  1041
          preproc_expr(&val,NULL);
ds@1952
  1042
          sc_compress=(int)val; /* switch code packing on/off */
ds@1952
  1043
        } else if (strcmp(str,"ctrlchar")==0) {
ds@1952
  1044
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1045
            lptr++;
ds@1952
  1046
          if (*lptr=='\0') {
ds@1952
  1047
            sc_ctrlchar=sc_ctrlchar_org;
ds@1952
  1048
          } else {
ds@1952
  1049
            if (lex(&val,&str)!=tNUMBER)
ds@1952
  1050
              error(27);          /* invalid character constant */
ds@1952
  1051
            sc_ctrlchar=(char)val;
ds@1952
  1052
          } /* if */
ds@1952
  1053
        } else if (strcmp(str,"deprecated")==0) {
ds@1952
  1054
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1055
            lptr++;
ds@1952
  1056
          pc_deprecate=(char*)malloc(strlen((char*)lptr)+1);
ds@1952
  1057
          if (pc_deprecate!=NULL)
ds@1952
  1058
            strcpy(pc_deprecate,(char*)lptr);
ds@1952
  1059
          lptr=(unsigned char*)strchr((char*)lptr,'\0'); /* skip to end (ignore "extra characters on line") */
ds@1952
  1060
        } else if (strcmp(str,"dynamic")==0) {
ds@1952
  1061
          preproc_expr(&pc_stksize,NULL);
ds@1952
  1062
        } else if (!strcmp(str,"library")
ds@1952
  1063
          ||!strcmp(str,"reqclass")
ds@1952
  1064
          ||!strcmp(str,"loadlib")
ds@1952
  1065
          ||!strcmp(str,"explib")
ds@1952
  1066
          ||!strcmp(str,"expclass")
ds@1952
  1067
          ||!strcmp(str,"defclasslib")) {
ds@1952
  1068
          char name[sNAMEMAX+1],sname[sNAMEMAX+1];
ds@1952
  1069
           const char *prefix="";
ds@1952
  1070
          sname[0]='\0';
ds@1952
  1071
          sname[1]='\0';
ds@1952
  1072
          if (!strcmp(str,"library"))
ds@1952
  1073
            prefix="??li_";
ds@1952
  1074
          else if (!strcmp(str,"reqclass"))
ds@1952
  1075
            prefix="??rc_";
ds@1952
  1076
          else if (!strcmp(str,"loadlib"))
ds@1952
  1077
            prefix="??f_";
ds@1952
  1078
          else if (!strcmp(str,"explib"))
ds@1952
  1079
            prefix="??el_";
ds@1952
  1080
          else if (!strcmp(str,"expclass"))
ds@1952
  1081
            prefix="??ec_";
ds@1952
  1082
          else if (!strcmp(str,"defclasslib"))
ds@1952
  1083
            prefix="??d_";
ds@1952
  1084
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1085
            lptr++;
ds@1952
  1086
          if (*lptr=='"') {
ds@1952
  1087
            lptr=getstring((unsigned char*)name,sizeof name,lptr);
ds@1952
  1088
          } else {
ds@1952
  1089
            int i;
ds@1952
  1090
            for (i=0; i<sizeof name && (alphanum(*lptr) || *lptr=='-'); i++,lptr++)
ds@1952
  1091
              name[i]=*lptr;
ds@1952
  1092
            name[i]='\0';
ds@1952
  1093
            if (!strncmp(str,"exp",3) || !strncmp(str,"def",3)) {
ds@1952
  1094
              while (*lptr && isspace(*lptr))
ds@1952
  1095
                lptr++;
ds@1952
  1096
              for (i=1; i<sizeof sname && alphanum(*lptr); i++,lptr++)
ds@1952
  1097
                sname[i]=*lptr;
ds@1952
  1098
              sname[i]='\0';
ds@1952
  1099
              if (!sname[1]) {
ds@1952
  1100
                error(45);
ds@1952
  1101
              } else {
ds@1952
  1102
                sname[0]='_';
ds@1952
  1103
              }
ds@1952
  1104
            }
ds@1952
  1105
          } /* if */
ds@1952
  1106
          if (strlen(name)==0) {
ds@1952
  1107
            curlibrary=NULL;
ds@1952
  1108
          } else if (strcmp(name,"-")==0) {
ds@1952
  1109
            pc_addlibtable=FALSE;
ds@1952
  1110
          } else {
ds@1952
  1111
            /* add the name if it does not yet exist in the table */
ds@1952
  1112
            char newname[sNAMEMAX+1];
ds@1952
  1113
            if (strlen(name)+strlen(prefix)+strlen(sname)<=sNAMEMAX) {
ds@1952
  1114
              strcpy(newname,prefix);
ds@1952
  1115
              strcat(newname,name);
ds@1952
  1116
              strcat(newname,sname);
ds@1952
  1117
              if (find_constval(&libname_tab,newname,0)==NULL) {
ds@1952
  1118
                if (!strcmp(str,"library") || !strcmp(str,"reqclass")) {
ds@1952
  1119
                  curlibrary=append_constval(&libname_tab,newname,1,0);
ds@1952
  1120
                } else {
ds@1952
  1121
                  append_constval(&libname_tab,newname,1,0);
ds@1952
  1122
                }
ds@1952
  1123
              }
ds@1952
  1124
            }
ds@1952
  1125
          } /* if */
ds@1952
  1126
#if 0	/* more unused */
ds@1952
  1127
        } else if (strcmp(str,"pack")==0) {
ds@1952
  1128
          cell val;
ds@1952
  1129
          preproc_expr(&val,NULL);      /* default = packed/unpacked */
ds@1952
  1130
          sc_packstr=(int)val;
ds@1952
  1131
#endif
ds@1952
  1132
        } else if (strcmp(str,"rational")==0) {
ds@1952
  1133
          char name[sNAMEMAX+1];
ds@1952
  1134
          cell digits=0;
ds@1952
  1135
          int i;
ds@1952
  1136
          /* first gather all information, start with the tag name */
ds@1952
  1137
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1138
            lptr++;
ds@1952
  1139
          for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
ds@1952
  1140
            name[i]=*lptr;
ds@1952
  1141
          name[i]='\0';
ds@1952
  1142
          /* then the precision (for fixed point arithmetic) */
ds@1952
  1143
          while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1144
            lptr++;
ds@1952
  1145
          if (*lptr=='(') {
ds@1952
  1146
            preproc_expr(&digits,NULL);
ds@1952
  1147
            if (digits<=0 || digits>9) {
ds@1952
  1148
              error(68);        /* invalid rational number precision */
ds@1952
  1149
              digits=0;
ds@1952
  1150
            } /* if */
ds@1952
  1151
            if (*lptr==')')
ds@1952
  1152
              lptr++;
ds@1952
  1153
          } /* if */
ds@1952
  1154
          /* add the tag (make it public) and check the values */
ds@1952
  1155
          i=pc_addtag(name);
ds@1952
  1156
          exporttag(i);
ds@1952
  1157
          if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) {
ds@1952
  1158
            sc_rationaltag=i;
ds@1952
  1159
            rational_digits=(int)digits;
ds@1952
  1160
          } else {
ds@1952
  1161
            error(69);          /* rational number format already set, can only be set once */
ds@1952
  1162
          } /* if */
ds@1952
  1163
        } else if (strcmp(str,"semicolon")==0) {
ds@1952
  1164
          cell val;
ds@1952
  1165
          preproc_expr(&val,NULL);
ds@1952
  1166
          sc_needsemicolon=(int)val;
ds@1952
  1167
        } else if (strcmp(str,"tabsize")==0) {
ds@1952
  1168
          cell val;
ds@1952
  1169
          preproc_expr(&val,NULL);
ds@1952
  1170
          sc_tabsize=(int)val;
ds@1952
  1171
        } else if (strcmp(str,"align")==0) {
ds@1952
  1172
          sc_alignnext=TRUE;
ds@1952
  1173
        } else if (strcmp(str,"unused")==0) {
ds@1952
  1174
          char name[sNAMEMAX+1];
ds@1952
  1175
          int i,comma;
ds@1952
  1176
          symbol *sym;
ds@1952
  1177
          do {
ds@1952
  1178
            /* get the name */
ds@1952
  1179
            while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1180
              lptr++;
ds@1952
  1181
            for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
ds@1952
  1182
              name[i]=*lptr;
ds@1952
  1183
            name[i]='\0';
ds@1952
  1184
            /* get the symbol */
ds@1952
  1185
            sym=findloc(name);
ds@1952
  1186
            if (sym==NULL)
ds@1952
  1187
              sym=findglb(name,sSTATEVAR);
ds@1952
  1188
            if (sym!=NULL) {
ds@1952
  1189
              sym->usage |= uREAD;
ds@1952
  1190
              if (sym->ident==iVARIABLE || sym->ident==iREFERENCE
ds@1952
  1191
                  || sym->ident==iARRAY || sym->ident==iREFARRAY)
ds@1952
  1192
                sym->usage |= uWRITTEN;
ds@1952
  1193
            } else {
ds@1952
  1194
              error(17,name);     /* undefined symbol */
ds@1952
  1195
            } /* if */
ds@1952
  1196
            /* see if a comma follows the name */
ds@1952
  1197
            while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1198
              lptr++;
ds@1952
  1199
            comma= (*lptr==',');
ds@1952
  1200
            if (comma)
ds@1952
  1201
              lptr++;
ds@1952
  1202
          } while (comma);
ds@1952
  1203
        } else {
ds@1952
  1204
          error(207);           /* unknown #pragma */
ds@1952
  1205
        } /* if */
ds@1952
  1206
      } else {
ds@1952
  1207
        error(207);             /* unknown #pragma */
ds@1952
  1208
      } /* if */
ds@1952
  1209
      check_empty(lptr);
ds@1952
  1210
    } /* if */
ds@1952
  1211
    break;
ds@1952
  1212
  case tpENDINPUT:
ds@1952
  1213
  case tpENDSCRPT:
ds@1952
  1214
    if (!SKIPPING) {
ds@1952
  1215
      check_empty(lptr);
ds@1952
  1216
      assert(inpf!=NULL);
ds@1952
  1217
      if (inpf!=inpf_org)
ds@1952
  1218
        pc_closesrc(inpf);
ds@1952
  1219
      inpf=NULL;
ds@1952
  1220
    } /* if */
ds@1952
  1221
    break;
ds@1952
  1222
#if !defined NOEMIT
ds@1952
  1223
  case tpEMIT: {
ds@1952
  1224
    /* write opcode to output file */
ds@1952
  1225
    char name[40];
ds@1952
  1226
    int i;
ds@1952
  1227
    while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1228
      lptr++;
ds@1952
  1229
    for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++)
ds@1952
  1230
      name[i]=(char)tolower(*lptr);
ds@1952
  1231
    name[i]='\0';
ds@1952
  1232
    stgwrite("\t");
ds@1952
  1233
    stgwrite(name);
ds@1952
  1234
    stgwrite(" ");
ds@1952
  1235
    code_idx+=opcodes(1);
ds@1952
  1236
    /* write parameter (if any) */
ds@1952
  1237
    while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1238
      lptr++;
ds@1952
  1239
    if (*lptr!='\0') {
ds@1952
  1240
      symbol *sym;
ds@1952
  1241
      tok=lex(&val,&str);
ds@1952
  1242
      switch (tok) {
ds@1952
  1243
      case tNUMBER:
ds@1952
  1244
      case tRATIONAL:
ds@1952
  1245
        outval(val,FALSE);
ds@1952
  1246
        code_idx+=opargs(1);
ds@1952
  1247
        break;
ds@1952
  1248
      case tSYMBOL:
ds@1952
  1249
        sym=findloc(str);
ds@1952
  1250
        if (sym==NULL)
ds@1952
  1251
          sym=findglb(str,sSTATEVAR);
ds@1952
  1252
        if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) {
ds@1952
  1253
          error(17,str);        /* undefined symbol */
ds@1952
  1254
        } else {
ds@1952
  1255
          outval(sym->addr,FALSE);
ds@1952
  1256
          /* mark symbol as "used", unknown whether for read or write */
ds@1952
  1257
          markusage(sym,uREAD | uWRITTEN);
ds@1952
  1258
          code_idx+=opargs(1);
ds@1952
  1259
        } /* if */
ds@1952
  1260
        break;
ds@1952
  1261
      default: {
ds@1952
  1262
        char s2[20];
ds@1952
  1263
        extern char *sc_tokens[];/* forward declaration */
ds@1952
  1264
        if (tok<256)
ds@1952
  1265
          sprintf(s2,"%c",(char)tok);
ds@1952
  1266
        else
ds@1952
  1267
          strcpy(s2,sc_tokens[tok-tFIRST]);
ds@1952
  1268
        error(1,sc_tokens[tSYMBOL-tFIRST],s2);
ds@1952
  1269
        break;
ds@1952
  1270
      } /* case */
ds@1952
  1271
      } /* switch */
ds@1952
  1272
    } /* if */
ds@1952
  1273
    stgwrite("\n");
ds@1952
  1274
    check_empty(lptr);
ds@1952
  1275
    break;
ds@1952
  1276
  } /* case */
ds@1952
  1277
#endif
ds@1952
  1278
#if !defined NO_DEFINE
ds@1952
  1279
  case tpDEFINE: {
ds@1952
  1280
    ret=CMD_DEFINE;
ds@1952
  1281
    if (!SKIPPING) {
ds@1952
  1282
      char *pattern,*substitution;
ds@1952
  1283
      const unsigned char *start,*end;
ds@1952
  1284
      int count,prefixlen;
ds@1952
  1285
      stringpair *def;
ds@1952
  1286
      /* find the pattern to match */
ds@1952
  1287
      while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1288
        lptr++;
ds@1952
  1289
      start=lptr;       /* save starting point of the match pattern */
ds@1952
  1290
      count=0;
ds@1952
  1291
      while (*lptr>' ' && *lptr!='\0') {
ds@1952
  1292
        litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */
ds@1952
  1293
        count++;
ds@1952
  1294
      } /* while */
ds@1952
  1295
      end=lptr;
ds@1952
  1296
      /* check pattern to match */
ds@1952
  1297
      if (!alpha(*start)) {
ds@1952
  1298
        error(74);      /* pattern must start with an alphabetic character */
ds@1952
  1299
        break;
ds@1952
  1300
      } /* if */
ds@1952
  1301
      /* store matched pattern */
ds@1952
  1302
      pattern=(char*)malloc(count+1);
ds@1952
  1303
      if (pattern==NULL)
ds@1952
  1304
        error(123);     /* insufficient memory */
ds@1952
  1305
      lptr=start;
ds@1952
  1306
      count=0;
ds@1952
  1307
      while (lptr!=end) {
ds@1952
  1308
        assert(lptr<end);
ds@1952
  1309
        assert(*lptr!='\0');
ds@1952
  1310
        pattern[count++]=(char)litchar(&lptr,0);
ds@1952
  1311
      } /* while */
ds@1952
  1312
      pattern[count]='\0';
ds@1952
  1313
      /* special case, erase trailing variable, because it could match anything */
ds@1952
  1314
      if (count>=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%')
ds@1952
  1315
        pattern[count-2]='\0';
ds@1952
  1316
      /* find substitution string */
ds@1952
  1317
      while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1318
        lptr++;
ds@1952
  1319
      start=lptr;       /* save starting point of the match pattern */
ds@1952
  1320
      count=0;
ds@1952
  1321
      end=NULL;
ds@1952
  1322
      while (*lptr!='\0') {
ds@1952
  1323
        /* keep position of the start of trailing whitespace */
ds@1952
  1324
        if (*lptr<=' ') {
ds@1952
  1325
          if (end==NULL)
ds@1952
  1326
            end=lptr;
ds@1952
  1327
        } else {
ds@1952
  1328
          end=NULL;
ds@1952
  1329
        } /* if */
ds@1952
  1330
        count++;
ds@1952
  1331
        lptr++;
ds@1952
  1332
      } /* while */
ds@1952
  1333
      if (end==NULL)
ds@1952
  1334
        end=lptr;
ds@1952
  1335
      /* store matched substitution */
ds@1952
  1336
      substitution=(char*)malloc(count+1);  /* +1 for '\0' */
ds@1952
  1337
      if (substitution==NULL)
ds@1952
  1338
        error(123);     /* insufficient memory */
ds@1952
  1339
      lptr=start;
ds@1952
  1340
      count=0;
ds@1952
  1341
      while (lptr!=end) {
ds@1952
  1342
        assert(lptr<end);
ds@1952
  1343
        assert(*lptr!='\0');
ds@1952
  1344
        substitution[count++]=*lptr++;
ds@1952
  1345
      } /* while */
ds@1952
  1346
      substitution[count]='\0';
ds@1952
  1347
      /* check whether the definition already exists */
ds@1952
  1348
      for (prefixlen=0,start=(unsigned char*)pattern; alphanum(*start); prefixlen++,start++)
ds@1952
  1349
        /* nothing */;
ds@1952
  1350
      assert(prefixlen>0);
ds@1952
  1351
      if ((def=find_subst(pattern,prefixlen))!=NULL) {
ds@1952
  1352
        if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0)
ds@1952
  1353
          error(201,pattern);   /* redefinition of macro (non-identical) */
ds@1952
  1354
        delete_subst(pattern,prefixlen);
ds@1952
  1355
      } /* if */
ds@1952
  1356
      /* add the pattern/substitution pair to the list */
ds@1952
  1357
      assert(strlen(pattern)>0);
ds@1952
  1358
      insert_subst(pattern,substitution,prefixlen);
ds@1952
  1359
      free(pattern);
ds@1952
  1360
      free(substitution);
ds@1952
  1361
    } /* if */
ds@1952
  1362
    break;
ds@1952
  1363
  } /* case */
ds@1952
  1364
  case tpUNDEF:
ds@1952
  1365
    if (!SKIPPING) {
ds@1952
  1366
      if (lex(&val,&str)==tSYMBOL) {
ds@1952
  1367
        ret=delete_subst(str,strlen(str));
ds@1952
  1368
        if (!ret) {
ds@1952
  1369
          /* also undefine normal constants */
ds@1952
  1370
          symbol *sym=findconst(str,NULL);
ds@1952
  1371
          if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) {
ds@1952
  1372
            delete_symbol(&glbtab,sym);
ds@1952
  1373
            ret=TRUE;
ds@1952
  1374
          } /* if */
ds@1952
  1375
        } /* if */
ds@1952
  1376
        if (!ret)
ds@1952
  1377
          error(17,str);        /* undefined symbol */
ds@1952
  1378
      } else {
ds@1952
  1379
        error(20,str);          /* invalid symbol name */
ds@1952
  1380
      } /* if */
ds@1952
  1381
      check_empty(lptr);
ds@1952
  1382
    } /* if */
ds@1952
  1383
    break;
ds@1952
  1384
#endif
ds@1952
  1385
  case tpERROR:
ds@1952
  1386
    while (*lptr<=' ' && *lptr!='\0')
ds@1952
  1387
      lptr++;
ds@1952
  1388
    if (!SKIPPING)
ds@1952
  1389
      error(131,lptr);  /* user error */
ds@1952
  1390
    break;
ds@1952
  1391
  default:
ds@1952
  1392
    error(31);          /* unknown compiler directive */
ds@1952
  1393
    ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE;  /* process as normal line */
ds@1952
  1394
  } /* switch */
ds@1952
  1395
  return ret;
ds@1952
  1396
}
ds@1952
  1397
ds@1952
  1398
#if !defined NO_DEFINE
ds@1952
  1399
static int is_startstring(const unsigned char *string)
ds@1952
  1400
{
ds@1952
  1401
  if (*string=='\"' || *string=='\'')
ds@1952
  1402
    return TRUE;                        /* "..." */
ds@1952
  1403
ds@1952
  1404
  if (*string=='!') {
ds@1952
  1405
    string++;
ds@1952
  1406
    if (*string=='\"' || *string=='\'')
ds@1952
  1407
      return TRUE;                      /* !"..." */
ds@1952
  1408
    if (*string==sc_ctrlchar) {
ds@1952
  1409
      string++;
ds@1952
  1410
      if (*string=='\"' || *string=='\'')
ds@1952
  1411
        return TRUE;                    /* !\"..." */
ds@1952
  1412
    } /* if */
ds@1952
  1413
  } else if (*string==sc_ctrlchar) {
ds@1952
  1414
    string++;
ds@1952
  1415
    if (*string=='\"' || *string=='\'')
ds@1952
  1416
      return TRUE;                      /* \"..." */
ds@1952
  1417
    if (*string=='!') {
ds@1952
  1418
      string++;
ds@1952
  1419
      if (*string=='\"' || *string=='\'')
ds@1952
  1420
        return TRUE;                    /* \!"..." */
ds@1952
  1421
    } /* if */
ds@1952
  1422
  } /* if */
ds@1952
  1423
ds@1952
  1424
  return FALSE;
ds@1952
  1425
}
ds@1952
  1426
ds@1952
  1427
static const unsigned char *skipstring(const unsigned char *string)
ds@1952
  1428
{
ds@1952
  1429
  char endquote;
ds@1952
  1430
  int flags=0;
ds@1952
  1431
ds@1952
  1432
  while (*string=='!' || *string==sc_ctrlchar) {
ds@1952
  1433
    if (*string==sc_ctrlchar)
ds@1952
  1434
      flags=RAWMODE;
ds@1952
  1435
    string++;
ds@1952
  1436
  } /* while */
ds@1952
  1437
ds@1952
  1438
  endquote=*string;
ds@1952
  1439
  assert(endquote=='"' || endquote=='\'');
ds@1952
  1440
  string++;             /* skip open quote */
ds@1952
  1441
  while (*string!=endquote && *string!='\0')
ds@1952
  1442
    litchar(&string,flags);
ds@1952
  1443
  return string;
ds@1952
  1444
}
ds@1952
  1445
ds@1952
  1446
static const unsigned char *skippgroup(const unsigned char *string)
ds@1952
  1447
{
ds@1952
  1448
  int nest=0;
ds@1952
  1449
  char open=*string;
ds@1952
  1450
  char close;
ds@1952
  1451
ds@1952
  1452
  switch (open) {
ds@1952
  1453
  case '(':
ds@1952
  1454
    close=')';
ds@1952
  1455
    break;
ds@1952
  1456
  case '{':
ds@1952
  1457
    close='}';
ds@1952
  1458
    break;
ds@1952
  1459
  case '[':
ds@1952
  1460
    close=']';
ds@1952
  1461
    break;
ds@1952
  1462
  case '<':
ds@1952
  1463
    close='>';
ds@1952
  1464
    break;
ds@1952
  1465
  default:
ds@1952
  1466
    assert(0);
ds@1952
  1467
	close='\0';         /* only to avoid a compiler warning */
ds@1952
  1468
  }/* switch */
ds@1952
  1469
ds@1952
  1470
  string++;
ds@1952
  1471
  while (*string!=close || nest>0) {
ds@1952
  1472
    if (*string==open)
ds@1952
  1473
      nest++;
ds@1952
  1474
    else if (*string==close)
ds@1952
  1475
      nest--;
ds@1952
  1476
    else if (is_startstring(string))
ds@1952
  1477
      string=skipstring(string);
ds@1952
  1478
    if (*string=='\0')
ds@1952
  1479
      break;
ds@1952
  1480
    string++;
ds@1952
  1481
  } /* while */
ds@1952
  1482
  return string;
ds@1952
  1483
}
ds@1952
  1484
ds@1952
  1485
static char *strdel(char *str,size_t len)
ds@1952
  1486
{
ds@1952
  1487
  size_t length=strlen(str);
ds@1952
  1488
  if (len>length)
ds@1952
  1489
    len=length;
ds@1952
  1490
  memmove(str, str+len, length-len+1);  /* include EOS byte */
ds@1952
  1491
  return str;
ds@1952
  1492
}
ds@1952
  1493
ds@1952
  1494
static char *strins(char *dest,char *src,size_t srclen)
ds@1952
  1495
{
ds@1952
  1496
  size_t destlen=strlen(dest);
ds@1952
  1497
  assert(srclen<=strlen(src));
ds@1952
  1498
  memmove(dest+srclen, dest, destlen+1);/* include EOS byte */
ds@1952
  1499
  memcpy(dest, src, srclen);
ds@1952
  1500
  return dest;
ds@1952
  1501
}
ds@1952
  1502
ds@1952
  1503
static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution)
ds@1952
  1504
{
ds@1952
  1505
  int prefixlen;
ds@1952
  1506
  const unsigned char *p,*s,*e;
ds@1952
  1507
  unsigned char *args[10];
ds@1952
  1508
  int match,arg,len,argsnum=0;
ds@1952
  1509
ds@1952
  1510
  memset(args,0,sizeof args);
ds@1952
  1511
ds@1952
  1512
  /* check the length of the prefix */
ds@1952
  1513
  for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++)
ds@1952
  1514
    /* nothing */;
ds@1952
  1515
  assert(prefixlen>0);
ds@1952
  1516
  assert(strncmp((char*)line,pattern,prefixlen)==0);
ds@1952
  1517
ds@1952
  1518
  /* pattern prefix matches; match the rest of the pattern, gather
ds@1952
  1519
   * the parameters
ds@1952
  1520
   */
ds@1952
  1521
  s=line+prefixlen;
ds@1952
  1522
  p=(unsigned char*)pattern+prefixlen;
ds@1952
  1523
  match=TRUE;         /* so far, pattern matches */
ds@1952
  1524
  while (match && *s!='\0' && *p!='\0') {
ds@1952
  1525
    if (*p=='%') {
ds@1952
  1526
      p++;            /* skip '%' */
ds@1952
  1527
      if (isdigit(*p)) {
ds@1952
  1528
        arg=*p-'0';
ds@1952
  1529
        assert(arg>=0 && arg<=9);
ds@1952
  1530
        p++;          /* skip parameter id */
ds@1952
  1531
        assert(*p!='\0');
ds@1952
  1532
        /* match the source string up to the character after the digit
ds@1952
  1533
         * (skipping strings in the process
ds@1952
  1534
         */
ds@1952
  1535
        e=s;
ds@1952
  1536
        while (*e!=*p && *e!='\0' && *e!='\n') {
ds@1952
  1537
          if (is_startstring(e))              /* skip strings */
ds@1952
  1538
            e=skipstring(e);
ds@1952
  1539
          else if (strchr("({[",*e)!=NULL)    /* skip parenthized groups */
ds@1952
  1540
            e=skippgroup(e);
ds@1952
  1541
          if (*e!='\0')
ds@1952
  1542
            e++;      /* skip non-alphapetic character (or closing quote of
ds@1952
  1543
                       * a string, or the closing paranthese of a group) */
ds@1952
  1544
        } /* while */
ds@1952
  1545
        /* store the parameter (overrule any earlier) */
ds@1952
  1546
        if (args[arg]!=NULL)
ds@1952
  1547
          free(args[arg]);
ds@1952
  1548
		else
ds@1952
  1549
          argsnum++;
ds@1952
  1550
        len=(int)(e-s);
ds@1952
  1551
        args[arg]=(unsigned char*)malloc(len+1);
ds@1952
  1552
        if (args[arg]==NULL)
ds@1952
  1553
          error(123); /* insufficient memory */
ds@1952
  1554
        strlcpy((char*)args[arg],(char*)s,len+1);
ds@1952
  1555
        /* character behind the pattern was matched too */
ds@1952
  1556
        if (*e==*p) {
ds@1952
  1557
          s=e+1;
ds@1952
  1558
        } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
ds@1952
  1559
          s=e;    /* allow a trailing ; in the pattern match to end of line */
ds@1952
  1560
        } else {
ds@1952
  1561
          assert(*e=='\0' || *e=='\n');
ds@1952
  1562
          match=FALSE;
ds@1952
  1563
          s=e;
ds@1952
  1564
        } /* if */
ds@1952
  1565
        p++;
ds@1952
  1566
      } else {
ds@1952
  1567
        match=FALSE;
ds@1952
  1568
      } /* if */
ds@1952
  1569
    } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
ds@1952
  1570
      /* source may be ';' or end of the line */
ds@1952
  1571
      while (*s<=' ' && *s!='\0')
ds@1952
  1572
        s++;          /* skip white space */
ds@1952
  1573
      if (*s!=';' && *s!='\0')
ds@1952
  1574
        match=FALSE;
ds@1952
  1575
      p++;            /* skip the semicolon in the pattern */
ds@1952
  1576
    } else {
ds@1952
  1577
      cell ch;
ds@1952
  1578
      /* skip whitespace between two non-alphanumeric characters, except
ds@1952
  1579
       * for two identical symbols
ds@1952
  1580
       */
ds@1952
  1581
      assert((char*)p>pattern);
ds@1952
  1582
      if (!alphanum(*p) && *(p-1)!=*p)
ds@1952
  1583
        while (*s<=' ' && *s!='\0')
ds@1952
  1584
          s++;                  /* skip white space */
ds@1952
  1585
      ch=litchar(&p,0);         /* this increments "p" */
ds@1952
  1586
      if (*s!=ch)
ds@1952
  1587
        match=FALSE;
ds@1952
  1588
      else
ds@1952
  1589
        s++;                    /* this character matches */
ds@1952
  1590
    } /* if */
ds@1952
  1591
  } /* while */
ds@1952
  1592
ds@1952
  1593
  if (match && *p=='\0') {
ds@1952
  1594
    /* if the last character to match is an alphanumeric character, the
ds@1952
  1595
     * current character in the source may not be alphanumeric
ds@1952
  1596
     */
ds@1952
  1597
    assert(p>(unsigned char*)pattern);
ds@1952
  1598
    if (alphanum(*(p-1)) && alphanum(*s))
ds@1952
  1599
      match=FALSE;
ds@1952
  1600
  } /* if */
ds@1952
  1601
ds@1952
  1602
  if (match) {
ds@1952
  1603
    /* calculate the length of the substituted string */
ds@1952
  1604
    for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) {
ds@1952
  1605
      if (*e=='%' && isdigit(*(e+1)) && argsnum) {
ds@1952
  1606
        arg=*(e+1)-'0';
ds@1952
  1607
        assert(arg>=0 && arg<=9);
ds@1952
  1608
        if (args[arg]!=NULL) {
ds@1952
  1609
          len+=strlen((char*)args[arg]);
ds@1952
  1610
          e++;
ds@1952
  1611
        } else {
ds@1952
  1612
          len++;
ds@1952
  1613
        }
ds@1952
  1614
      } else {
ds@1952
  1615
        len++;
ds@1952
  1616
      } /* if */
ds@1952
  1617
    } /* for */
ds@1952
  1618
    /* check length of the string after substitution */
ds@1952
  1619
    if (strlen((char*)line) + len - (int)(s-line) > buffersize) {
ds@1952
  1620
      error(75);      /* line too long */
ds@1952
  1621
    } else {
ds@1952
  1622
      /* substitute pattern */
ds@1952
  1623
      strdel((char*)line,(int)(s-line));
ds@1952
  1624
      for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) {
ds@1952
  1625
        if (*e=='%' && isdigit(*(e+1))) {
ds@1952
  1626
          arg=*(e+1)-'0';
ds@1952
  1627
          assert(arg>=0 && arg<=9);
ds@1952
  1628
          if (args[arg]!=NULL) {
ds@1952
  1629
            strins((char*)s,(char*)args[arg],strlen((char*)args[arg]));
ds@1952
  1630
            s+=strlen((char*)args[arg]);
ds@1952
  1631
          } else {
ds@1952
  1632
            error(236); /* parameter does not exist, incorrect #define pattern */
ds@1952
  1633
            strins((char*)s,(char*)e,2);
ds@1952
  1634
            s+=2;
ds@1952
  1635
          } /* if */
ds@1952
  1636
          e++;          /* skip %, digit is skipped later */
ds@1952
  1637
        } else if (*e == '"') {
ds@1952
  1638
          p=e;
ds@1952
  1639
          if (is_startstring(e)) {
ds@1952
  1640
            e=skipstring(e);
ds@1952
  1641
            strins((char*)s,(char *)p,(e-p+1));
ds@1952
  1642
            s+=(e-p+1);
ds@1952
  1643
          } else {
ds@1952
  1644
            strins((char*)s,(char*)e,1);
ds@1952
  1645
            s++;
ds@1952
  1646
          }
ds@1952
  1647
        } else {
ds@1952
  1648
          strins((char*)s,(char*)e,1);
ds@1952
  1649
          s++;
ds@1952
  1650
        } /* if */
ds@1952
  1651
      } /* for */
ds@1952
  1652
    } /* if */
ds@1952
  1653
  } /* if */
ds@1952
  1654
ds@1952
  1655
  for (arg=0; arg<10; arg++)
ds@1952
  1656
    if (args[arg]!=NULL)
ds@1952
  1657
      free(args[arg]);
ds@1952
  1658
ds@1952
  1659
  return match;
ds@1952
  1660
}
ds@1952
  1661
ds@1952
  1662
static void substallpatterns(unsigned char *line,int buffersize)
ds@1952
  1663
{
ds@1952
  1664
  unsigned char *start, *end;
ds@1952
  1665
  int prefixlen;
ds@1952
  1666
  stringpair *subst;
ds@1952
  1667
ds@1952
  1668
  start=line;
ds@1952
  1669
  while (*start!='\0') {
ds@1952
  1670
    /* find the start of a prefix (skip all non-alphabetic characters),
ds@1952
  1671
     * also skip strings
ds@1952
  1672
     */
ds@1952
  1673
    while (!alpha(*start) && *start!='\0') {
ds@1952
  1674
      /* skip strings */
ds@1952
  1675
      if (is_startstring(start)) {
ds@1952
  1676
        start=(unsigned char *)skipstring(start);
ds@1952
  1677
        if (*start=='\0')
ds@1952
  1678
          break;        /* abort loop on error */
ds@1952
  1679
      } /* if */
ds@1952
  1680
      start++;          /* skip non-alphapetic character (or closing quote of a string) */
ds@1952
  1681
    } /* while */
ds@1952
  1682
    if (*start=='\0')
ds@1952
  1683
      break;            /* abort loop on error */
ds@1952
  1684
    /* if matching the operator "defined", skip it plus the symbol behind it */
ds@1952
  1685
	if (strncmp((char*)start,"defined",7)==0 && !isalpha((char)*(start+7))) {
ds@1952
  1686
      start+=7;         /* skip "defined" */
ds@1952
  1687
      /* skip white space & parantheses */
ds@1952
  1688
      while (*start<=' ' && *start!='\0' || *start=='(')
ds@1952
  1689
        start++;
ds@1952
  1690
      /* skip the symbol behind it */
ds@1952
  1691
      while (alphanum(*start))
ds@1952
  1692
        start++;
ds@1952
  1693
      /* drop back into the main loop */
ds@1952
  1694
      continue;
ds@1952
  1695
    } /* if */
ds@1952
  1696
    /* get the prefix (length), look for a matching definition */
ds@1952
  1697
    prefixlen=0;
ds@1952
  1698
    end=start;
ds@1952
  1699
    while (alphanum(*end)) {
ds@1952
  1700
      prefixlen++;
ds@1952
  1701
      end++;
ds@1952
  1702
    } /* while */
ds@1952
  1703
    assert(prefixlen>0);
ds@1952
  1704
    subst=find_subst((char*)start,prefixlen);
ds@1952
  1705
    if (subst!=NULL) {
ds@1952
  1706
      /* properly match the pattern and substitute */
ds@1952
  1707
      if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second))
ds@1952
  1708
        start=end;      /* match failed, skip this prefix */
ds@1952
  1709
      /* match succeeded: do not update "start", because the substitution text
ds@1952
  1710
       * may be matched by other macros
ds@1952
  1711
       */
ds@1952
  1712
    } else {
ds@1952
  1713
      start=end;        /* no macro with this prefix, skip this prefix */
ds@1952
  1714
    } /* if */
ds@1952
  1715
  } /* while */
ds@1952
  1716
}
ds@1952
  1717
#endif
ds@1952
  1718
ds@1952
  1719
/*  preprocess
ds@1952
  1720
 *
ds@1952
  1721
 *  Reads a line by readline() into "pline" and performs basic preprocessing:
ds@1952
  1722
 *  deleting comments, skipping lines with false "#if.." code and recognizing
ds@1952
  1723
 *  other compiler directives. There is an indirect recursion: lex() calls
ds@1952
  1724
 *  preprocess() if a new line must be read, preprocess() calls command(),
ds@1952
  1725
 *  which at his turn calls lex() to identify the token.
ds@1952
  1726
 *
ds@1952
  1727
 *  Global references: lptr     (altered)
ds@1952
  1728
 *                     pline    (altered)
ds@1952
  1729
 *                     freading (referred to only)
ds@1952
  1730
 */
ds@1952
  1731
SC_FUNC void preprocess(void)
ds@1952
  1732
{
ds@1952
  1733
  int iscommand;
ds@1952
  1734
ds@1952
  1735
  if (!freading)
ds@1952
  1736
    return;
ds@1952
  1737
  do {
ds@1952
  1738
    readline(pline);
ds@1952
  1739
    stripcom(pline);    /* ??? no need for this when reading back from list file (in the second pass) */
ds@1952
  1740
    lptr=pline;         /* set "line pointer" to start of the parsing buffer */
ds@1952
  1741
    iscommand=command();
ds@1952
  1742
    if (iscommand!=CMD_NONE)
ds@1952
  1743
      errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */
ds@1952
  1744
    #if !defined NO_DEFINE
ds@1952
  1745
      if (iscommand==CMD_NONE) {
ds@1952
  1746
        assert(lptr!=term_expr);
ds@1952
  1747
        substallpatterns(pline,sLINEMAX);
ds@1952
  1748
        lptr=pline;       /* reset "line pointer" to start of the parsing buffer */
ds@1952
  1749
      } /* if */
ds@1952
  1750
    #endif
ds@1952
  1751
    if (sc_status==statFIRST && sc_listing && freading
ds@1952
  1752
        && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE))
ds@1952
  1753
    {
ds@1952
  1754
      listline++;
ds@1952
  1755
      if (fline!=listline) {
ds@1952
  1756
        listline=fline;
ds@1952
  1757
        setlinedirect(fline);
ds@1952
  1758
      } /* if */
ds@1952
  1759
      if (iscommand==CMD_EMPTYLINE)
ds@1952
  1760
        pc_writeasm(outf,"\n");
ds@1952
  1761
      else
ds@1952
  1762
        pc_writeasm(outf,(char*)pline);
ds@1952
  1763
    } /* if */
ds@1952
  1764
  } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */
ds@1952
  1765
}
ds@1952
  1766
ds@1952
  1767
static const unsigned char *unpackedstring(const unsigned char *lptr,int flags)
ds@1952
  1768
{
ds@1952
  1769
  while (*lptr!='\"' && *lptr!='\0') {
ds@1952
  1770
    if (*lptr=='\a') {          /* ignore '\a' (which was inserted at a line concatenation) */
ds@1952
  1771
      lptr++;
ds@1952
  1772
      continue;
ds@1952
  1773
    } /* if */
ds@1952
  1774
    litadd(litchar(&lptr,flags | UTF8MODE));  /* litchar() alters "lptr" */
ds@1952
  1775
  } /* while */
ds@1952
  1776
  litadd(0);                    /* terminate string */
ds@1952
  1777
  return lptr;
ds@1952
  1778
}
ds@1952
  1779
ds@1952
  1780
static const unsigned char *packedstring(const unsigned char *lptr,int flags)
ds@1952
  1781
{
ds@1952
  1782
  int i;
ds@1952
  1783
  ucell val,c;
ds@1952
  1784
ds@1952
  1785
  i=0; /* start at least significant byte */
ds@1952
  1786
  val=0;
ds@1952
  1787
  glbstringread=1;
ds@1952
  1788
  while (*lptr!='\"' && *lptr!='\0') {
ds@1952
  1789
    if (*lptr=='\a') {          /* ignore '\a' (which was inserted at a line concatenation) */
ds@1952
  1790
      lptr++;
ds@1952
  1791
      continue;
ds@1952
  1792
    } /* if */
ds@1952
  1793
    c=litchar(&lptr,flags);     /* litchar() alters "lptr" */
ds@1952
  1794
    if (c>=(ucell)(1 << sCHARBITS))
ds@1952
  1795
      error(43);                /* character constant exceeds range */
ds@1952
  1796
    val |= (c << 8*i);
ds@1952
  1797
    glbstringread++;
ds@1952
  1798
    if (i==sizeof(ucell)-(sCHARBITS/8)) {
ds@1952
  1799
      litadd(val);
ds@1952
  1800
      val=0;
ds@1952
  1801
      i=0;
ds@1952
  1802
    } else {
ds@1952
  1803
      i=i+1;
ds@1952
  1804
    }
ds@1952
  1805
  } /* if */
ds@1952
  1806
  /* save last code; make sure there is at least one terminating zero character */
ds@1952
  1807
  if (i!=0)
ds@1952
  1808
    litadd(val);        /* at least one zero character in "val" */
ds@1952
  1809
  else
ds@1952
  1810
    litadd(0);          /* add full cell of zeros */
ds@1952
  1811
  return lptr;
ds@1952
  1812
}
ds@1952
  1813
ds@1952
  1814
/*  lex(lexvalue,lexsym)        Lexical Analysis
ds@1952
  1815
 *
ds@1952
  1816
 *  lex() first deletes leading white space, then checks for multi-character
ds@1952
  1817
 *  operators, keywords (including most compiler directives), numbers,
ds@1952
  1818
 *  labels, symbols and literals (literal characters are converted to a number
ds@1952
  1819
 *  and are returned as such). If every check fails, the line must contain
ds@1952
  1820
 *  a single-character operator. So, lex() returns this character. In the other
ds@1952
  1821
 *  case (something did match), lex() returns the number of the token. All
ds@1952
  1822
 *  these tokens have been assigned numbers above 255.
ds@1952
  1823
 *
ds@1952
  1824
 *  Some tokens have "attributes":
ds@1952
  1825
 *     tNUMBER        the value of the number is return in "lexvalue".
ds@1952
  1826
 *     tRATIONAL      the value is in IEEE 754 encoding or in fixed point
ds@1952
  1827
 *                    encoding in "lexvalue".
ds@1952
  1828
 *     tSYMBOL        the first sNAMEMAX characters of the symbol are
ds@1952
  1829
 *                    stored in a buffer, a pointer to this buffer is
ds@1952
  1830
 *                    returned in "lexsym".
ds@1952
  1831
 *     tLABEL         the first sNAMEMAX characters of the label are
ds@1952
  1832
 *                    stored in a buffer, a pointer to this buffer is
ds@1952
  1833
 *                    returned in "lexsym".
ds@1952
  1834
 *     tSTRING        the string is stored in the literal pool, the index
ds@1952
  1835
 *                    in the literal pool to this string is stored in
ds@1952
  1836
 *                    "lexvalue".
ds@1952
  1837
 *
ds@1952
  1838
 *  lex() stores all information (the token found and possibly its attribute)
ds@1952
  1839
 *  in global variables. This allows a token to be examined twice. If "_pushed"
ds@1952
  1840
 *  is true, this information is returned.
ds@1952
  1841
 *
ds@1952
  1842
 *  Global references: lptr          (altered)
ds@1952
  1843
 *                     fline         (referred to only)
ds@1952
  1844
 *                     litidx        (referred to only)
ds@1952
  1845
 *                     _lextok, _lexval, _lexstr
ds@1952
  1846
 *                     _pushed
ds@1952
  1847
 */
ds@1952
  1848
ds@1952
  1849
static int _pushed;
ds@1952
  1850
static int _lextok;
ds@1952
  1851
static cell _lexval;
ds@1952
  1852
static char _lexstr[sLINEMAX+1];
ds@1952
  1853
static int _lexnewline;
ds@1952
  1854
ds@1952
  1855
SC_FUNC void lexinit(void)
ds@1952
  1856
{
ds@1952
  1857
  stkidx=0;             /* index for pushstk() and popstk() */
ds@1952
  1858
  iflevel=0;            /* preprocessor: nesting of "#if" is currently 0 */
ds@1952
  1859
  skiplevel=0;          /* preprocessor: not currently skipping */
ds@1952
  1860
  icomment=0;           /* currently not in a multiline comment */
ds@1952
  1861
  _pushed=FALSE;        /* no token pushed back into lex */
ds@1952
  1862
  _lexnewline=FALSE;
ds@1952
  1863
}
ds@1952
  1864
ds@1952
  1865
char *sc_tokens[] = {
ds@1952
  1866
         "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=",
ds@1952
  1867
         "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--",
ds@1952
  1868
         "...", "..", "::",
ds@1952
  1869
         "assert", "*begin", "break", "case", "cellsof", "chars", "const", "continue", "default",
ds@1952
  1870
         "defined", "do", "else", "*end", "enum", "exit", "for", "forward", "funcenum", "functag", "goto",
ds@1952
  1871
         "if", "native", "new", "decl", "operator", "public", "return", "sizeof",
ds@1952
  1872
         "sleep", "state", "static", "stock", "struct", "switch", "tagof", "*then", "while",
ds@1952
  1873
         "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput",
ds@1952
  1874
         "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma",
ds@1952
  1875
         "#tryinclude", "#undef",
ds@1952
  1876
         ";", ";", "-integer value-", "-rational value-", "-identifier-",
ds@1952
  1877
         "-label-", "-string-"
ds@1952
  1878
       };
ds@1952
  1879
ds@1952
  1880
SC_FUNC int lex(cell *lexvalue,char **lexsym)
ds@1952
  1881
{
ds@1952
  1882
  int i,toolong,newline,stringflags;
ds@1952
  1883
  char **tokptr;
ds@1952
  1884
  const unsigned char *starttoken;
ds@1952
  1885
ds@1952
  1886
  if (_pushed) {
ds@1952
  1887
    _pushed=FALSE;      /* reset "_pushed" flag */
ds@1952
  1888
    *lexvalue=_lexval;
ds@1952
  1889
    *lexsym=_lexstr;
ds@1952
  1890
    return _lextok;
ds@1952
  1891
  } /* if */
ds@1952
  1892
ds@1952
  1893
  _lextok=0;            /* preset all values */
ds@1952
  1894
  _lexval=0;
ds@1952
  1895
  _lexstr[0]='\0';
ds@1952
  1896
  *lexvalue=_lexval;
ds@1952
  1897
  *lexsym=_lexstr;
ds@1952
  1898
  _lexnewline=FALSE;
ds@1952
  1899
  if (!freading)
ds@1952
  1900
    return 0;
ds@1952
  1901
ds@1952
  1902
  newline= (lptr==pline);       /* does lptr point to start of line buffer */
ds@1952
  1903
  while (*lptr<=' ') {          /* delete leading white space */
ds@1952
  1904
    if (*lptr=='\0') {
ds@1952
  1905
      preprocess();             /* preprocess resets "lptr" */
ds@1952
  1906
      if (!freading)
ds@1952
  1907
        return 0;
ds@1952
  1908
      if (lptr==term_expr)      /* special sequence to terminate a pending expression */
ds@1952
  1909
        return (_lextok=tENDEXPR);
ds@1952
  1910
      _lexnewline=TRUE;         /* set this after preprocess(), because
ds@1952
  1911
                                 * preprocess() calls lex() recursively */
ds@1952
  1912
      newline=TRUE;
ds@1952
  1913
    } else {
ds@1952
  1914
      lptr+=1;
ds@1952
  1915
    } /* if */
ds@1952
  1916
  } /* while */
ds@1952
  1917
  if (newline) {
ds@1952
  1918
    stmtindent=0;
ds@1952
  1919
    for (i=0; i<(int)(lptr-pline); i++)
ds@1952
  1920
      if (pline[i]=='\t' && sc_tabsize>0)
ds@1952
  1921
        stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
ds@1952
  1922
      else
ds@1952
  1923
        stmtindent++;
ds@1952
  1924
  } /* if */
ds@1952
  1925
ds@1952
  1926
  i=tFIRST;
ds@1952
  1927
  tokptr=sc_tokens;
ds@1952
  1928
  while (i<=tMIDDLE) {  /* match multi-character operators */
ds@1952
  1929
    if (*lptr==**tokptr && match(*tokptr,FALSE)) {
ds@1952
  1930
      _lextok=i;
ds@1952
  1931
      if (pc_docexpr)   /* optionally concatenate to documentation string */
ds@1952
  1932
        insert_autolist(*tokptr);
ds@1952
  1933
      return _lextok;
ds@1952
  1934
    } /* if */
ds@1952
  1935
    i+=1;
ds@1952
  1936
    tokptr+=1;
ds@1952
  1937
  } /* while */
ds@1952
  1938
  while (i<=tLAST) {    /* match reserved words and compiler directives */
ds@1952
  1939
    if (*lptr==**tokptr && match(*tokptr,TRUE)) {
ds@1952
  1940
      _lextok=i;
ds@1952
  1941
      errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
ds@1952
  1942
      if (pc_docexpr)   /* optionally concatenate to documentation string */
ds@1952
  1943
        insert_autolist(*tokptr);
ds@1952
  1944
      return _lextok;
ds@1952
  1945
    } /* if */
ds@1952
  1946
    i+=1;
ds@1952
  1947
    tokptr+=1;
ds@1952
  1948
  } /* while */
ds@1952
  1949
ds@1952
  1950
  starttoken=lptr;      /* save start pointer (for concatenating to documentation string) */
ds@1952
  1951
  if ((i=number(&_lexval,lptr))!=0) {   /* number */
ds@1952
  1952
    _lextok=tNUMBER;
ds@1952
  1953
    *lexvalue=_lexval;
ds@1952
  1954
    lptr+=i;
ds@1952
  1955
  } else if ((i=ftoi(&_lexval,lptr))!=0) {
ds@1952
  1956
    _lextok=tRATIONAL;
ds@1952
  1957
    *lexvalue=_lexval;
ds@1952
  1958
    lptr+=i;
ds@1952
  1959
  } else if (alpha(*lptr)) {            /* symbol or label */
ds@1952
  1960
    /*  Note: only sNAMEMAX characters are significant. The compiler
ds@1952
  1961
     *        generates a warning if a symbol exceeds this length.
ds@1952
  1962
     */
ds@1952
  1963
    _lextok=tSYMBOL;
ds@1952
  1964
    i=0;
ds@1952
  1965
    toolong=0;
ds@1952
  1966
    while (alphanum(*lptr)){
ds@1952
  1967
      _lexstr[i]=*lptr;
ds@1952
  1968
      lptr+=1;
ds@1952
  1969
      if (i<sNAMEMAX)
ds@1952
  1970
        i+=1;
ds@1952
  1971
      else
ds@1952
  1972
        toolong=1;
ds@1952
  1973
    } /* while */
ds@1952
  1974
    _lexstr[i]='\0';
ds@1952
  1975
    if (toolong)
ds@1952
  1976
      error(200,_lexstr,sNAMEMAX);  /* symbol too long, truncated to sNAMEMAX chars */
ds@1952
  1977
    if (_lexstr[0]==PUBLIC_CHAR && _lexstr[1]=='\0') {
ds@1952
  1978
      _lextok=PUBLIC_CHAR;  /* '@' all alone is not a symbol, it is an operator */
ds@1952
  1979
    } else if (_lexstr[0]=='_' && _lexstr[1]=='\0') {
ds@1952
  1980
      _lextok='_';      /* '_' by itself is not a symbol, it is a placeholder */
ds@1952
  1981
    } /* if */
ds@1952
  1982
    if (*lptr==':' && *(lptr+1)!=':' && _lextok!=PUBLIC_CHAR) {
ds@1952
  1983
      if (sc_allowtags) {
ds@1952
  1984
        _lextok=tLABEL; /* it wasn't a normal symbol, it was a label/tagname */
ds@1952
  1985
        lptr+=1;        /* skip colon */
ds@1952
  1986
      } else if (find_constval(&tagname_tab,_lexstr,0)!=NULL) {
ds@1952
  1987
        /* this looks like a tag override (because a tag with this name
ds@1952
  1988
         * exists), but tags are not allowed right now, so it is probably an
ds@1952
  1989
         * error
ds@1952
  1990
         */
ds@1952
  1991
        error(220);
ds@1952
  1992
      } /* if */
ds@1952
  1993
    } /* if */
ds@1952
  1994
  } else if (*lptr=='\"' || *lptr==sc_ctrlchar && *(lptr+1)=='\"')
ds@1952
  1995
  {                                     /* unpacked string literal */
ds@1952
  1996
    _lextok=tSTRING;
ds@1952
  1997
    stringflags= (*lptr==sc_ctrlchar) ? RAWMODE : 0;
ds@1952
  1998
    *lexvalue=_lexval=litidx;
ds@1952
  1999
    lptr+=1;            /* skip double quote */
ds@1952
  2000
    if ((stringflags & RAWMODE)!=0)
ds@1952
  2001
      lptr+=1;          /* skip "escape" character too */
ds@1952
  2002
    /* Note that this should always be packedstring() for SourcePawn */
ds@1952
  2003
    lptr=sc_packstr ? packedstring(lptr,stringflags) : unpackedstring(lptr,stringflags);
ds@1952
  2004
    if (*lptr=='\"')
ds@1952
  2005
      lptr+=1;          /* skip final quote */
ds@1952
  2006
    else
ds@1952
  2007
      error(37);        /* invalid (non-terminated) string */
ds@1952
  2008
  } else if (*lptr=='!' && *(lptr+1)=='\"'
ds@1952
  2009
             || *lptr=='!' && *(lptr+1)==sc_ctrlchar && *(lptr+2)=='\"'
ds@1952
  2010
             || *lptr==sc_ctrlchar && *(lptr+1)=='!' && *(lptr+2)=='\"')
ds@1952
  2011
  {                                     /* packed string literal */
ds@1952
  2012
    _lextok=tSTRING;
ds@1952
  2013
    stringflags= (*lptr==sc_ctrlchar || *(lptr+1)==sc_ctrlchar) ? RAWMODE : 0;
ds@1952
  2014
    *lexvalue=_lexval=litidx;
ds@1952
  2015
    lptr+=2;            /* skip exclamation point and double quote */
ds@1952
  2016
    if ((stringflags & RAWMODE)!=0)
ds@1952
  2017
      lptr+=1;          /* skip "escape" character too */
ds@1952
  2018
    lptr=sc_packstr ? unpackedstring(lptr,stringflags) : packedstring(lptr,stringflags);
ds@1952
  2019
    if (*lptr=='\"')
ds@1952
  2020
      lptr+=1;          /* skip final quote */
ds@1952
  2021
    else
ds@1952
  2022
      error(37);        /* invalid (non-terminated) string */
ds@1952
  2023
  } else if (*lptr=='\'') {             /* character literal */
ds@1952
  2024
    lptr+=1;            /* skip quote */
ds@1952
  2025
    _lextok=tNUMBER;
ds@1952
  2026
    *lexvalue=_lexval=litchar(&lptr,UTF8MODE);
ds@1952
  2027
    if (*lptr=='\'')
ds@1952
  2028
      lptr+=1;          /* skip final quote */
ds@1952
  2029
    else
ds@1952
  2030
      error(27);        /* invalid character constant (must be one character) */
ds@1952
  2031
  } else if (*lptr==';') {      /* semicolumn resets "error" flag */
ds@1952
  2032
    _lextok=';';
ds@1952
  2033
    lptr+=1;
ds@1952
  2034
    errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
ds@1952
  2035
  } else {
ds@1952
  2036
    _lextok=*lptr;      /* if every match fails, return the character */
ds@1952
  2037
    lptr+=1;            /* increase the "lptr" pointer */
ds@1952
  2038
  } /* if */
ds@1952
  2039
ds@1952
  2040
  if (pc_docexpr) {     /* optionally concatenate to documentation string */
ds@1952
  2041
    char *docstr=(char*)malloc(((int)(lptr-starttoken)+1)*sizeof(char));
ds@1952
  2042
    if (docstr!=NULL) {
ds@1952
  2043
      strlcpy(docstr,(char*)starttoken,(int)(lptr-starttoken)+1);
ds@1952
  2044
      insert_autolist(docstr);
ds@1952
  2045
      free(docstr);
ds@1952
  2046
    } /* if */
ds@1952
  2047
  } /* if */
ds@1952
  2048
  return _lextok;
ds@1952
  2049
}
ds@1952
  2050
ds@1952
  2051
/*  lexpush
ds@1952
  2052
 *
ds@1952
  2053
 *  Pushes a token back, so the next call to lex() will return the token
ds@1952
  2054
 *  last examined, instead of a new token.
ds@1952
  2055
 *
ds@1952
  2056
 *  Only one token can be pushed back.
ds@1952
  2057
 *
ds@1952
  2058
 *  In fact, lex() already stores the information it finds into global
ds@1952
  2059
 *  variables, so all that is to be done is set a flag that informs lex()
ds@1952
  2060
 *  to read and return the information from these variables, rather than
ds@1952
  2061
 *  to read in a new token from the input file.
ds@1952
  2062
 */
ds@1952
  2063
SC_FUNC void lexpush(void)
ds@1952
  2064
{
ds@1952
  2065
  assert(_pushed==FALSE);
ds@1952
  2066
  _pushed=TRUE;
ds@1952
  2067
}
ds@1952
  2068
ds@1952
  2069
/*  lexclr
ds@1952
  2070
 *
ds@1952
  2071
 *  Sets the variable "_pushed" to 0 to make sure lex() will read in a new
ds@1952
  2072
 *  symbol (a not continue with some old one). This is required upon return
ds@1952
  2073
 *  from Assembler mode, and in a few cases after detecting an syntax error.
ds@1952
  2074
 */
ds@1952
  2075
SC_FUNC void lexclr(int clreol)
ds@1952
  2076
{
ds@1952
  2077
  _pushed=FALSE;
ds@1952
  2078
  if (clreol) {
ds@1952
  2079
    lptr=(unsigned char*)strchr((char*)pline,'\0');
ds@1952
  2080
    assert(lptr!=NULL);
ds@1952
  2081
  } /* if */
ds@1952
  2082
}
ds@1952
  2083
ds@1952
  2084
/*  matchtoken
ds@1952
  2085
 *
ds@1952
  2086
 *  This routine is useful if only a simple check is needed. If the token
ds@1952
  2087
 *  differs from the one expected, it is pushed back.
ds@1952
  2088
 *  This function returns 1 for "token found" and 2 for "implied statement
ds@1952
  2089
 *  termination token" found --the statement termination is an end of line in
ds@1952
  2090
 *  an expression where there is no pending operation. Such an implied token
ds@1952
  2091
 *  (i.e. not present in the source code) should not be pushed back, which is
ds@1952
  2092
 *  why it is sometimes important to distinguish the two.
ds@1952
  2093
 */
ds@1952
  2094
SC_FUNC int matchtoken(int token)
ds@1952
  2095
{
ds@1952
  2096
  cell val;
ds@1952
  2097
  char *str;
ds@1952
  2098
  int tok;
ds@1952
  2099
ds@1952
  2100
  tok=lex(&val,&str);
ds@1952
  2101
  if (tok==token || token==tTERM && (tok==';' || tok==tENDEXPR)) {
ds@1952
  2102
    return 1;
ds@1952
  2103
  } else if (!sc_needsemicolon && token==tTERM && (_lexnewline || !freading)) {
ds@1952
  2104
    /* Push "tok" back, because it is the token following the implicit statement
ds@1952
  2105
     * termination (newline) token.
ds@1952
  2106
     */
ds@1952
  2107
    lexpush();
ds@1952
  2108
    return 2;
ds@1952
  2109
  } else {
ds@1952
  2110
    lexpush();
ds@1952
  2111
    return 0;
ds@1952
  2112
  } /* if */
ds@1952
  2113
}
ds@1952
  2114
ds@1952
  2115
/*  tokeninfo
ds@1952
  2116
 *
ds@1952
  2117
 *  Returns additional information of a token after using "matchtoken()"
ds@1952
  2118
 *  or needtoken(). It does no harm using this routine after a call to
ds@1952
  2119
 *  "lex()", but lex() already returns the same information.
ds@1952
  2120
 *
ds@1952
  2121
 *  The token itself is the return value. Normally, this one is already known.
ds@1952
  2122
 */
ds@1952
  2123
SC_FUNC int tokeninfo(cell *val,char **str)
ds@1952
  2124
{
ds@1952
  2125
  /* if the token was pushed back, tokeninfo() returns the token and
ds@1952
  2126
   * parameters of the *next* token, not of the *current* token.
ds@1952
  2127
   */
ds@1952
  2128
  assert(!_pushed);
ds@1952
  2129
  *val=_lexval;
ds@1952
  2130
  *str=_lexstr;
ds@1952
  2131
  return _lextok;
ds@1952
  2132
}
ds@1952
  2133
ds@1952
  2134
/*  needtoken
ds@1952
  2135
 *
ds@1952
  2136
 *  This routine checks for a required token and gives an error message if
ds@1952
  2137
 *  it isn't there (and returns 0/FALSE in that case). Like function matchtoken(),
ds@1952
  2138
 *  this function returns 1 for "token found" and 2 for "statement termination
ds@1952
  2139
 *  token" found; see function matchtoken() for details.
ds@1952
  2140
 *
ds@1952
  2141
 *  Global references: _lextok;
ds@1952
  2142
 */
ds@1952
  2143
SC_FUNC int needtoken(int token)
ds@1952
  2144
{
ds@1952
  2145
  char s1[20],s2[20];
ds@1952
  2146
  int t;
ds@1952
  2147
ds@1952
  2148
  if ((t=matchtoken(token))!=0) {
ds@1952
  2149
    return t;
ds@1952
  2150
  } else {
ds@1952
  2151
    /* token already pushed back */
ds@1952
  2152
    assert(_pushed);
ds@1952
  2153
    if (token<256)
ds@1952
  2154
      sprintf(s1,"%c",(char)token);        /* single character token */
ds@1952
  2155
    else
ds@1952
  2156
      strcpy(s1,sc_tokens[token-tFIRST]);  /* multi-character symbol */
ds@1952
  2157
    if (!freading)
ds@1952
  2158
      strcpy(s2,"-end of file-");
ds@1952
  2159
    else if (_lextok<256)
ds@1952
  2160
      sprintf(s2,"%c",(char)_lextok);
ds@1952
  2161
    else
ds@1952
  2162
      strcpy(s2,sc_tokens[_lextok-tFIRST]);
ds@1952
  2163
    error(1,s1,s2);     /* expected ..., but found ... */
ds@1952
  2164
    return FALSE;
ds@1952
  2165
  } /* if */
ds@1952
  2166
}
ds@1952
  2167
ds@1952
  2168
/*  match
ds@1952
  2169
 *
ds@1952
  2170
 *  Compares a series of characters from the input file with the characters
ds@1952
  2171
 *  in "st" (that contains a token). If the token on the input file matches
ds@1952
  2172
 *  "st", the input file pointer "lptr" is adjusted to point to the next
ds@1952
  2173
 *  token, otherwise "lptr" remains unaltered.
ds@1952
  2174
 *
ds@1952
  2175
 *  If the parameter "end: is true, match() requires that the first character
ds@1952
  2176
 *  behind the recognized token is non-alphanumeric.
ds@1952
  2177
 *
ds@1952
  2178
 *  Global references: lptr   (altered)
ds@1952
  2179
 */
ds@1952
  2180
static int match(char *st,int end)
ds@1952
  2181
{
ds@1952
  2182
  int k;
ds@1952
  2183
  const unsigned char *ptr;
ds@1952
  2184
ds@1952
  2185
  k=0;
ds@1952
  2186
  ptr=lptr;
ds@1952
  2187
  while (st[k]) {
ds@1952
  2188
    if ((unsigned char)st[k]!=*ptr)
ds@1952
  2189
      return 0;
ds@1952
  2190
    k+=1;
ds@1952
  2191
    ptr+=1;
ds@1952
  2192
  } /* while */
ds@1952
  2193
  if (end) {            /* symbol must terminate with non-alphanumeric char */
ds@1952
  2194
    if (alphanum(*ptr))
ds@1952
  2195
      return 0;
ds@1952
  2196
  } /* if */
ds@1952
  2197
  lptr=ptr;     /* match found, skip symbol */
ds@1952
  2198
  return 1;
ds@1952
  2199
}
ds@1952
  2200
ds@1952
  2201
static void chk_grow_litq(void)
ds@1952
  2202
{
ds@1952
  2203
  if (litidx>=litmax) {
ds@1952
  2204
    cell *p;
ds@1952
  2205
ds@1952
  2206
    litmax+=sDEF_LITMAX;
ds@1952
  2207
    p=(cell *)realloc(litq,litmax*sizeof(cell));
ds@1952
  2208
    if (p==NULL)
ds@1952
  2209
      error(122,"literal table");   /* literal table overflow (fatal error) */
ds@1952
  2210
    litq=p;
ds@1952
  2211
  } /* if */
ds@1952
  2212
}
ds@1952
  2213
ds@1952
  2214
/*  litadd
ds@1952
  2215
 *
ds@1952
  2216
 *  Adds a value at the end of the literal queue. The literal queue is used
ds@1952
  2217
 *  for literal strings used in functions and for initializing array variables.
ds@1952
  2218
 *
ds@1952
  2219
 *  Global references: litidx  (altered)
ds@1952
  2220
 *                     litq    (altered)
ds@1952
  2221
 */
ds@1952
  2222
SC_FUNC void litadd(cell value)
ds@1952
  2223
{
ds@1952
  2224
  chk_grow_litq();
ds@1952
  2225
  assert(litidx<litmax);
ds@1952
  2226
  litq[litidx++]=value;
ds@1952
  2227
}
ds@1952
  2228
ds@1952
  2229
/*  litinsert
ds@1952
  2230
 *
ds@1952
  2231
 *  Inserts a value into the literal queue. This is sometimes necessary for
ds@1952
  2232
 *  initializing multi-dimensional arrays.
ds@1952
  2233
 *
ds@1952
  2234
 *  Global references: litidx  (altered)
ds@1952
  2235
 *                     litq    (altered)
ds@1952
  2236
 */
ds@1952
  2237
SC_FUNC void litinsert(cell value,int pos)
ds@1952
  2238
{
ds@1952
  2239
  chk_grow_litq();
ds@1952
  2240
  assert(litidx<litmax);
ds@1952
  2241
  assert(pos>=0 && pos<=litidx);
ds@1952
  2242
  memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell));
ds@1952
  2243
  litidx++;
ds@1952
  2244
  litq[pos]=value;
ds@1952
  2245
}
ds@1952
  2246
ds@1952
  2247
/*  litchar
ds@1952
  2248
 *
ds@1952
  2249
 *  Return current literal character and increase the pointer to point
ds@1952
  2250
 *  just behind this literal character.
ds@1952
  2251
 *
ds@1952
  2252
 *  Note: standard "escape sequences" are suported, but the backslash may be
ds@1952
  2253
 *        replaced by another character; the syntax '\ddd' is supported,
ds@1952
  2254
 *        but ddd must be decimal!
ds@1952
  2255
 */
ds@1952
  2256
static cell litchar(const unsigned char **lptr,int flags)
ds@1952
  2257
{
ds@1952
  2258
  cell c=0;
ds@1952
  2259
  const unsigned char *cptr;
ds@1952
  2260
ds@1952
  2261
  cptr=*lptr;
ds@1952
  2262
  if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) {  /* no escape character */
ds@1952
  2263
    #if !defined NO_UTF8
ds@1952
  2264
      if (sc_is_utf8 && (flags & UTF8MODE)!=0) {
ds@1952
  2265
        c=get_utf8_char(cptr,&cptr);
ds@1952
  2266
        assert(c>=0);   /* file was already scanned for conformance to UTF-8 */
ds@1952
  2267
      } else {
ds@1952
  2268
    #endif
ds@1952
  2269
      #if !defined NO_CODEPAGE
ds@1952
  2270
        c=cp_translate(cptr,&cptr);
ds@1952
  2271
      #else
ds@1952
  2272
        c=*cptr;
ds@1952
  2273
        cptr+=1;
ds@1952
  2274
      #endif
ds@1952
  2275
    #if !defined NO_UTF8
ds@1952
  2276
      } /* if */
ds@1952
  2277
    #endif
ds@1952
  2278
  } else {
ds@1952
  2279
    cptr+=1;
ds@1952
  2280
    if (*cptr==sc_ctrlchar) {
ds@1952
  2281
      c=*cptr;          /* \\ == \ (the escape character itself) */
ds@1952
  2282
      cptr+=1;
ds@1952
  2283
    } else {
ds@1952
  2284
      switch (*cptr) {
ds@1952
  2285
      case 'a':         /* \a == audible alarm */
ds@1952
  2286
        c=7;
ds@1952
  2287
        cptr+=1;
ds@1952
  2288
        break;
ds@1952
  2289
      case 'b':         /* \b == backspace */
ds@1952
  2290
        c=8;
ds@1952
  2291
        cptr+=1;
ds@1952
  2292
        break;
ds@1952
  2293
      case 'e':         /* \e == escape */
ds@1952
  2294
        c=27;
ds@1952
  2295
        cptr+=1;
ds@1952
  2296
        break;
ds@1952
  2297
      case 'f':         /* \f == form feed */
ds@1952
  2298
        c=12;
ds@1952
  2299
        cptr+=1;
ds@1952
  2300
        break;
ds@1952
  2301
      case 'n':         /* \n == NewLine character */
ds@1952
  2302
        c=10;
ds@1952
  2303
        cptr+=1;
ds@1952
  2304
        break;
ds@1952
  2305
      case 'r':         /* \r == carriage return */
ds@1952
  2306
        c=13;
ds@1952
  2307
        cptr+=1;
ds@1952
  2308
        break;
ds@1952
  2309
      case 't':         /* \t == horizontal TAB */
ds@1952
  2310
        c=9;
ds@1952
  2311
        cptr+=1;
ds@1952
  2312
        break;
ds@1952
  2313
      case 'v':         /* \v == vertical TAB */
ds@1952
  2314
        c=11;
ds@1952
  2315
        cptr+=1;
ds@1952
  2316
        break;
ds@1952
  2317
      case 'x':
ds@1952
  2318
      {
ds@1952
  2319
        int digits = 0;
ds@1952
  2320
        cptr+=1;
ds@1952
  2321
        c=0;
ds@1952
  2322
        while (ishex(*cptr) && digits < 2) {
ds@1952
  2323
          if (isdigit(*cptr))
ds@1952
  2324
            c=(c<<4)+(*cptr-'0');
ds@1952
  2325
          else
ds@1952
  2326
            c=(c<<4)+(tolower(*cptr)-'a'+10);
ds@1952
  2327
          cptr++;
ds@1952
  2328
          digits++;
ds@1952
  2329
        } /* while */
ds@1952
  2330
        if (*cptr==';')
ds@1952
  2331
          cptr++;       /* swallow a trailing ';' */
ds@1952
  2332
        break;
ds@1952
  2333
      }
ds@1952
  2334
      case '\'':        /* \' == ' (single quote) */
ds@1952
  2335
      case '"':         /* \" == " (single quote) */
ds@1952
  2336
      case '%':         /* \% == % (percent) */
ds@1952
  2337
        c=*cptr;
ds@1952
  2338
        cptr+=1;
ds@1952
  2339
        break;
ds@1952
  2340
      default:
ds@1952
  2341
        if (isdigit(*cptr)) {   /* \ddd */
ds@1952
  2342
          c=0;
ds@1952
  2343
          while (*cptr>='0' && *cptr<='9')  /* decimal! */
ds@1952
  2344
            c=c*10 + *cptr++ - '0';
ds@1952
  2345
          if (*cptr==';')
ds@1952
  2346
            cptr++;     /* swallow a trailing ';' */
ds@1952
  2347
        } else {
ds@1952
  2348
          error(27);    /* invalid character constant */
ds@1952
  2349
        } /* if */
ds@1952
  2350
      } /* switch */
ds@1952
  2351
    } /* if */
ds@1952
  2352
  } /* if */
ds@1952
  2353
  *lptr=cptr;
ds@1952
  2354
  assert(c>=0);
ds@1952
  2355
  return c;
ds@1952
  2356
}
ds@1952
  2357
ds@1952
  2358
/*  alpha
ds@1952
  2359
 *
ds@1952
  2360
 *  Test if character "c" is alphabetic ("a".."z"), an underscore ("_")
ds@1952
  2361
 *  or an "at" sign ("@"). The "@" is an extension to standard C.
ds@1952
  2362
 */
ds@1952
  2363
static int alpha(char c)
ds@1952
  2364
{
ds@1952
  2365
  return (isalpha(c) || c=='_' || c==PUBLIC_CHAR);
ds@1952
  2366
}
ds@1952
  2367
ds@1952
  2368
/*  alphanum
ds@1952
  2369
 *
ds@1952
  2370
 *  Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@")
ds@1952
  2371
 */
ds@1952
  2372
SC_FUNC int alphanum(char c)
ds@1952
  2373
{
ds@1952
  2374
  return (alpha(c) || isdigit(c));
ds@1952
  2375
}
ds@1952
  2376
ds@1952
  2377
/*  ishex
ds@1952
  2378
 *
ds@1952
  2379
 *  Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f").
ds@1952
  2380
 */
ds@1952
  2381
SC_FUNC int ishex(char c)
ds@1952
  2382
{
ds@1952
  2383
  return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
ds@1952
  2384
}
ds@1952
  2385
ds@1952
  2386
/* The local variable table must be searched backwards, so that the deepest
ds@1952
  2387
 * nesting of local variables is searched first. The simplest way to do
ds@1952
  2388
 * this is to insert all new items at the head of the list.
ds@1952
  2389
 * In the global list, the symbols are kept in sorted order, so that the
ds@1952
  2390
 * public functions are written in sorted order.
ds@1952
  2391
 */
ds@1952
  2392
static symbol *add_symbol(symbol *root,symbol *entry,int sort)
ds@1952
  2393
{
ds@1952
  2394
  symbol *newsym;
ds@1952
  2395
ds@1952
  2396
  if (sort)
ds@1952
  2397
    while (root->next!=NULL && strcmp(entry->name,root->next->name)>0)
ds@1952
  2398
      root=root->next;
ds@1952
  2399
ds@1952
  2400
  if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) {
ds@1952
  2401
    error(123);
ds@1952
  2402
    return NULL;
ds@1952
  2403
  } /* if */
ds@1952
  2404
  memcpy(newsym,entry,sizeof(symbol));
ds@1952
  2405
  newsym->next=root->next;
ds@1952
  2406
  root->next=newsym;
ds@1952
  2407
  return newsym;
ds@1952
  2408
}
ds@1952
  2409
ds@1952
  2410
static void free_symbol(symbol *sym)
ds@1952
  2411
{
ds@1952
  2412
  arginfo *arg;
ds@1952
  2413
ds@1952
  2414
  /* free all sub-symbol allocated memory blocks, depending on the
ds@1952
  2415
   * kind of the symbol
ds@1952
  2416
   */
ds@1952
  2417
  assert(sym!=NULL);
ds@1952
  2418
  if (sym->ident==iFUNCTN) {
ds@1952
  2419
    /* run through the argument list; "default array" arguments
ds@1952
  2420
     * must be freed explicitly; the tag list must also be freed */
ds@1952
  2421
    assert(sym->dim.arglist!=NULL);
ds@1952
  2422
    for (arg=sym->dim.arglist; arg->ident!=0; arg++) {
ds@1952
  2423
      if (arg->ident==iREFARRAY && arg->hasdefault)
ds@1952
  2424
        free(arg->defvalue.array.data);
ds@1952
  2425
      else if (arg->ident==iVARIABLE
ds@1952
  2426
               && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0))
ds@1952
  2427
        free(arg->defvalue.size.symname);
ds@1952
  2428
      assert(arg->tags!=NULL);
ds@1952
  2429
      free(arg->tags);
ds@1952
  2430
    } /* for */
ds@1952
  2431
    free(sym->dim.arglist);
ds@1952
  2432
    if (sym->states!=NULL) {
ds@1952
  2433
      delete_consttable(sym->states);
ds@1952
  2434
      free(sym->states);
ds@1952
  2435
    } /* if */
ds@1952
  2436
  } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
ds@1952
  2437
    if (sym->states!=NULL) {
ds@1952
  2438
      delete_consttable(sym->states);
ds@1952
  2439
      free(sym->states);
ds@1952
  2440
    } /* if */
ds@1952
  2441
  } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) {
ds@1952
  2442
    /* free the constant list of an enum root */
ds@1952
  2443
    assert(sym->dim.enumlist!=NULL);
ds@1952
  2444
    delete_consttable(sym->dim.enumlist);
ds@1952
  2445
    free(sym->dim.enumlist);
ds@1952
  2446
  } /* if */
ds@1952
  2447
  assert(sym->refer!=NULL);
ds@1952
  2448
  free(sym->refer);
ds@1952
  2449
  if (sym->documentation!=NULL)
ds@1952
  2450
    free(sym->documentation);
ds@1952
  2451
  free(sym);
ds@1952
  2452
}
ds@1952
  2453
ds@1952
  2454
SC_FUNC void delete_symbol(symbol *root,symbol *sym)
ds@1952
  2455
{
ds@1952
  2456
  /* find the symbol and its predecessor
ds@1952
  2457
   * (this function assumes that you will never delete a symbol that is not
ds@1952
  2458
   * in the table pointed at by "root")
ds@1952
  2459
   */
ds@1952
  2460
  assert(root!=sym);
ds@1952
  2461
  while (root->next!=sym) {
ds@1952
  2462
    root=root->next;
ds@1952
  2463
    assert(root!=NULL);
ds@1952
  2464
  } /* while */
ds@1952
  2465
ds@1952
  2466
  /* unlink it, then free it */
ds@1952
  2467
  root->next=sym->next;
ds@1952
  2468
  free_symbol(sym);
ds@1952
  2469
}
ds@1952
  2470
ds@1952
  2471
SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions)
ds@1952
  2472
{
ds@1952
  2473
  symbol *sym,*parent_sym;
ds@1952
  2474
  constvalue *stateptr;
ds@1952
  2475
  int mustdelete;
ds@1952
  2476
ds@1952
  2477
  /* erase only the symbols with a deeper nesting level than the
ds@1952
  2478
   * specified nesting level */
ds@1952
  2479
  while (root->next!=NULL) {
ds@1952
  2480
    sym=root->next;
ds@1952
  2481
    if (sym->compound<level)
ds@1952
  2482
      break;
ds@1952
  2483
    switch (sym->ident) {
ds@1952
  2484
    case iLABEL:
ds@1952
  2485
      mustdelete=delete_labels;
ds@1952
  2486
      break;
ds@1952
  2487
    case iVARIABLE:
ds@1952
  2488
    case iARRAY:
ds@1952
  2489
      /* do not delete global variables if functions are preserved */
ds@1952
  2490
      mustdelete=delete_functions;
ds@1952
  2491
      break;
ds@1952
  2492
    case iREFERENCE:
ds@1952
  2493
      /* always delete references (only exist as function parameters) */
ds@1952
  2494
      mustdelete=TRUE;
ds@1952
  2495
      break;
ds@1952
  2496
    case iREFARRAY:
ds@1952
  2497
      /* a global iREFARRAY symbol is the return value of a function: delete
ds@1952
  2498
       * this only if "globals" must be deleted; other iREFARRAY instances
ds@1952
  2499
       * (locals) are also deleted
ds@1952
  2500
       */
ds@1952
  2501
      mustdelete=delete_functions;
ds@1952
  2502
      for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent)
ds@1952
  2503
        assert(parent_sym->ident==iREFARRAY);
ds@1952
  2504
      assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL));
ds@1952
  2505
      if (parent_sym==NULL || parent_sym->ident!=iFUNCTN)
ds@1952
  2506
        mustdelete=TRUE;
ds@1952
  2507
      break;
ds@1952
  2508
    case iCONSTEXPR:
ds@1952
  2509
      /* delete constants, except predefined constants */
ds@1952
  2510
      mustdelete=delete_functions || (sym->usage & uPREDEF)==0;
ds@1952
  2511
      break;
ds@1952
  2512
    case iFUNCTN:
ds@1952
  2513
      /* optionally preserve globals (variables & functions), but
ds@1952
  2514
       * NOT native functions
ds@1952
  2515
       */
ds@1952
  2516
      mustdelete=delete_functions || (sym->usage & uNATIVE)!=0;
ds@1952
  2517
      assert(sym->parent==NULL);
ds@1952
  2518
      break;
ds@1952
  2519
    case iARRAYCELL:
ds@1952
  2520
    case iARRAYCHAR:
ds@1952
  2521
    case iEXPRESSION:
ds@1952
  2522
    case iVARARGS:
ds@1952
  2523
    default:
ds@1952
  2524
      assert(0);
ds@1952
  2525
      break;
ds@1952
  2526
    } /* switch */
ds@1952
  2527
    if (mustdelete) {
ds@1952
  2528
      root->next=sym->next;
ds@1952
  2529
      free_symbol(sym);
ds@1952
  2530
    } else {
ds@1952
  2531
      /* if the function was prototyped, but not implemented in this source,
ds@1952
  2532
       * mark it as such, so that its use can be flagged
ds@1952
  2533
       */
ds@1952
  2534
      if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0)
ds@1952
  2535
        sym->usage |= uMISSING;
ds@1952
  2536
      if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY)
ds@1952
  2537
        sym->usage &= ~uDEFINE; /* clear "defined" flag */
ds@1952
  2538
      /* set all states as "undefined" too */
ds@1952
  2539
      if (sym->states!=NULL)
ds@1952
  2540
        for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next)
ds@1952
  2541
          stateptr->value=0;
ds@1952
  2542
      /* for user defined operators, also remove the "prototyped" flag, as
ds@1952
  2543
       * user-defined operators *must* be declared before use
ds@1952
  2544
       */
ds@1952
  2545
      if (sym->ident==iFUNCTN && !alpha(*sym->name))
ds@1952
  2546
        sym->usage &= ~uPROTOTYPED;
ds@1952
  2547
      root=sym;                 /* skip the symbol */
ds@1952
  2548
    } /* if */
ds@1952
  2549
  } /* if */
ds@1952
  2550
}
ds@1952
  2551
ds@1952
  2552
/* The purpose of the hash is to reduce the frequency of a "name"
ds@1952
  2553
 * comparison (which is costly). There is little interest in avoiding
ds@1952
  2554
 * clusters in similar names, which is why this function is plain simple.
ds@1952
  2555
 */
ds@1952
  2556
SC_FUNC uint32_t namehash(const char *name)
ds@1952
  2557
{
ds@1952
  2558
  const unsigned char *ptr=(const unsigned char *)name;
ds@1952
  2559
  int len=strlen(name);
ds@1952
  2560
  if (len==0)
ds@1952
  2561
    return 0L;
ds@1952
  2562
  assert(len<256);
ds@1952
  2563
  return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]);
ds@1952
  2564
}
ds@1952
  2565
ds@1952
  2566
static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag)
ds@1952
  2567
{
ds@1952
  2568
  symbol *firstmatch=NULL;
ds@1952
  2569
  symbol *sym=root->next;
ds@1952
  2570
  int count=0;
ds@1952
  2571
  unsigned long hash=namehash(name);
ds@1952
  2572
  while (sym!=NULL) {
ds@1952
  2573
    if (hash==sym->hash && strcmp(name,sym->name)==0        /* check name */
ds@1952
  2574
        && (sym->parent==NULL || sym->ident==iCONSTEXPR)    /* sub-types (hierarchical types) are skipped, except for enum fields */
ds@1952
  2575
        && (sym->fnumber<0 || sym->fnumber==fnumber))       /* check file number for scope */
ds@1952
  2576
    {
ds@1952
  2577
      assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */
ds@1952
  2578
      if (sym->ident==iFUNCTN
ds@1952
  2579
          || automaton<0 && sym->states==NULL
ds@1952
  2580
          || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton)
ds@1952
  2581
      {
ds@1952
  2582
        if (cmptag==NULL)
ds@1952
  2583
          return sym;   /* return first match */
ds@1952
  2584
        /* return closest match or first match; count number of matches */
ds@1952
  2585
        if (firstmatch==NULL)
ds@1952
  2586
          firstmatch=sym;
ds@1952
  2587
        assert(cmptag!=NULL);
ds@1952
  2588
        if (*cmptag==0)
ds@1952
  2589
          count++;
ds@1952
  2590
        if (*cmptag==sym->tag) {
ds@1952
  2591
          *cmptag=1;    /* good match found, set number of matches to 1 */
ds@1952
  2592
          return sym;
ds@1952
  2593
        } /* if */
ds@1952
  2594
      } /* if */
ds@1952
  2595
    } /*  */
ds@1952
  2596
    sym=sym->next;
ds@1952
  2597
  } /* while */
ds@1952
  2598
  if (cmptag!=NULL && firstmatch!=NULL)
ds@1952
  2599
    *cmptag=count;
ds@1952
  2600
  return firstmatch;
ds@1952
  2601
}
ds@1952
  2602
ds@1952
  2603
static symbol *find_symbol_child(const symbol *root,const symbol *sym)
ds@1952
  2604
{
ds@1952
  2605
  symbol *ptr=root->next;
ds@1952
  2606
  while (ptr!=NULL) {
ds@1952
  2607
    if (ptr->parent==sym)
ds@1952
  2608
      return ptr;
ds@1952
  2609
    ptr=ptr->next;
ds@1952
  2610
  } /* while */
ds@1952
  2611
  return NULL;
ds@1952
  2612
}
ds@1952
  2613
ds@1952
  2614
/* Adds "bywhom" to the list of referrers of "entry". Typically,
ds@1952
  2615
 * bywhom will be the function that uses a variable or that calls
ds@1952
  2616
 * the function.
ds@1952
  2617
 */
ds@1952
  2618
SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom)
ds@1952
  2619
{
ds@1952
  2620
  int count;
ds@1952
  2621
ds@1952
  2622
  assert(bywhom!=NULL);         /* it makes no sense to add a "void" referrer */
ds@1952
  2623
  assert(entry!=NULL);
ds@1952
  2624
  assert(entry->refer!=NULL);
ds@1952
  2625
ds@1952
  2626
  /* see if it is already there */
ds@1952
  2627
  for (count=0; count<entry->numrefers && entry->refer[count]!=bywhom; count++)
ds@1952
  2628
    /* nothing */;
ds@1952
  2629
  if (count<entry->numrefers) {
ds@1952
  2630
    assert(entry->refer[count]==bywhom);
ds@1952
  2631
    return TRUE;
ds@1952
  2632
  } /* if */
ds@1952
  2633
ds@1952
  2634
  /* see if there is an empty spot in the referrer list */
ds@1952
  2635
  for (count=0; count<entry->numrefers && entry->refer[count]!=NULL; count++)
ds@1952
  2636
    /* nothing */;
ds@1952
  2637
  assert(count <= entry->numrefers);
ds@1952
  2638
  if (count==entry->numrefers) {
ds@1952
  2639
    symbol **refer;
ds@1952
  2640
    int newsize=2*entry->numrefers;
ds@1952
  2641
    assert(newsize>0);
ds@1952
  2642
    /* grow the referrer list */
ds@1952
  2643
    refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*));
ds@1952
  2644
    if (refer==NULL)
ds@1952
  2645
      return FALSE;             /* insufficient memory */
ds@1952
  2646
    /* initialize the new entries */
ds@1952
  2647
    entry->refer=refer;
ds@1952
  2648
    for (count=entry->numrefers; count<newsize; count++)
ds@1952
  2649
      entry->refer[count]=NULL;
ds@1952
  2650
    count=entry->numrefers;     /* first empty spot */
ds@1952
  2651
    entry->numrefers=newsize;
ds@1952
  2652
  } /* if */
ds@1952
  2653
ds@1952
  2654
  /* add the referrer */
ds@1952
  2655
  assert(entry->refer[count]==NULL);
ds@1952
  2656
  entry->refer[count]=bywhom;
ds@1952
  2657
  return TRUE;
ds@1952
  2658
}
ds@1952
  2659
ds@1952
  2660
SC_FUNC void markusage(symbol *sym,int usage)
ds@1952
  2661
{
ds@1952
  2662
  assert(sym!=NULL);
ds@1952
  2663
  sym->usage |= (char)usage;
ds@1952
  2664
  if ((usage & uWRITTEN)!=0)
ds@1952
  2665
    sym->lnumber=fline;
ds@1952
  2666
  /* check if (global) reference must be added to the symbol */
ds@1952
  2667
  if ((usage & (uREAD | uWRITTEN))!=0) {
ds@1952
  2668
    /* only do this for global symbols */
ds@1952
  2669
    if (sym->vclass==sGLOBAL) {
ds@1952
  2670
      /* "curfunc" should always be valid, since statements may not occurs
ds@1952
  2671
       * outside functions; in the case of syntax errors, however, the
ds@1952
  2672
       * compiler may arrive through this function
ds@1952
  2673
       */
ds@1952
  2674
      if (curfunc!=NULL)
ds@1952
  2675
        refer_symbol(sym,curfunc);
ds@1952
  2676
    } /* if */
ds@1952
  2677
  } /* if */
ds@1952
  2678
}
ds@1952
  2679
ds@1952
  2680
ds@1952
  2681
/*  findglb
ds@1952
  2682
 *
ds@1952
  2683
 *  Returns a pointer to the global symbol (if found) or NULL (if not found)
ds@1952
  2684
 */
ds@1952
  2685
SC_FUNC symbol *findglb(const char *name,int filter)
ds@1952
  2686
{
ds@1952
  2687
  /* find a symbol with a matching automaton first */
ds@1952
  2688
  symbol *sym=NULL;
ds@1952
  2689
ds@1952
  2690
  if (filter>sGLOBAL && sc_curstates>0) {
ds@1952
  2691
    /* find a symbol whose state list matches the current fsa */
ds@1952
  2692
    sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL);
ds@1952
  2693
    if (sym!=NULL && sym->ident!=iFUNCTN) {
ds@1952
  2694
      /* if sym!=NULL, we found a variable in the automaton; now we should
ds@1952
  2695
       * also verify whether there is an intersection between the symbol's
ds@1952
  2696
       * state list and the current state list
ds@1952
  2697
       */
ds@1952
  2698
      assert(sym->states!=NULL && sym->states->next!=NULL);
ds@1952
  2699
      if (!state_conflict_id(sc_curstates,sym->states->next->index))
ds@1952
  2700
        sym=NULL;
ds@1952
  2701
    } /* if */
ds@1952
  2702
  } /* if */
ds@1952
  2703
ds@1952
  2704
  /* if no symbol with a matching automaton exists, find a variable/function
ds@1952
  2705
   * that has no state(s) attached to it
ds@1952
  2706
   */
ds@1952
  2707
  if (sym==NULL)
ds@1952
  2708
    sym=find_symbol(&glbtab,name,fcurrent,-1,NULL);
ds@1952
  2709
  return sym;
ds@1952
  2710
}
ds@1952
  2711
ds@1952
  2712
/*  findloc
ds@1952
  2713
 *
ds@1952
  2714
 *  Returns a pointer to the local symbol (if found) or NULL (if not found).
ds@1952
  2715
 *  See add_symbol() how the deepest nesting level is searched first.
ds@1952
  2716
 */
ds@1952
  2717
SC_FUNC symbol *findloc(const char *name)
ds@1952
  2718
{
ds@1952
  2719
  return find_symbol(&loctab,name,-1,-1,NULL);
ds@1952
  2720
}
ds@1952
  2721
ds@1952
  2722
SC_FUNC symbol *findconst(const char *name,int *cmptag)
ds@1952
  2723
{
ds@1952
  2724
  symbol *sym;
ds@1952
  2725
ds@1952
  2726
  sym=find_symbol(&loctab,name,-1,-1,cmptag);  /* try local symbols first */
ds@1952
  2727
  if (sym==NULL || sym->ident!=iCONSTEXPR)     /* not found, or not a constant */
ds@1952
  2728
    sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag);
ds@1952
  2729
  if (sym==NULL || sym->ident!=iCONSTEXPR)
ds@1952
  2730
    return NULL;
ds@1952
  2731
  assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0);
ds@1952
  2732
  /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */
ds@1952
  2733
  return sym;
ds@1952
  2734
}
ds@1952
  2735
ds@1952
  2736
SC_FUNC symbol *finddepend(const symbol *parent)
ds@1952
  2737
{
ds@1952
  2738
  symbol *sym;
ds@1952
  2739
ds@1952
  2740
  sym=find_symbol_child(&loctab,parent);    /* try local symbols first */
ds@1952
  2741
  if (sym==NULL)                            /* not found */
ds@1952
  2742
    sym=find_symbol_child(&glbtab,parent);
ds@1952
  2743
  return sym;
ds@1952
  2744
}
ds@1952
  2745
ds@1952
  2746
/*  addsym
ds@1952
  2747
 *
ds@1952
  2748
 *  Adds a symbol to the symbol table (either global or local variables,
ds@1952
  2749
 *  or global and local constants).
ds@1952
  2750
 */
ds@1952
  2751
SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage)
ds@1952
  2752
{
ds@1952
  2753
  symbol entry, **refer;
ds@1952
  2754
ds@1952
  2755
  /* labels may only be defined once */
ds@1952
  2756
  assert(ident!=iLABEL || findloc(name)==NULL);
ds@1952
  2757
ds@1952
  2758
  /* create an empty referrer list */
ds@1952
  2759
  if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) {
ds@1952
  2760
    error(123);         /* insufficient memory */
ds@1952
  2761
    return NULL;
ds@1952
  2762
  } /* if */
ds@1952
  2763
  *refer=NULL;
ds@1952
  2764
ds@1952
  2765
  /* first fill in the entry */
ds@1952
  2766
  memset(&entry,0,sizeof entry);
ds@1952
  2767
  strcpy(entry.name,name);
ds@1952
  2768
  entry.hash=namehash(name);
ds@1952
  2769
  entry.addr=addr;
ds@1952
  2770
  entry.codeaddr=code_idx;
ds@1952
  2771
  entry.vclass=(char)vclass;
ds@1952
  2772
  entry.ident=(char)ident;
ds@1952
  2773
  entry.tag=tag;
ds@1952
  2774
  entry.usage=(char)usage;
ds@1952
  2775
  entry.fnumber=-1;     /* assume global visibility (ignored for local symbols) */
ds@1952
  2776
  entry.lnumber=fline;
ds@1952
  2777
  entry.numrefers=1;
ds@1952
  2778
  entry.refer=refer;
ds@1952
  2779
ds@1952
  2780
  /* then insert it in the list */
ds@1952
  2781
  if (vclass==sGLOBAL)
ds@1952
  2782
    return add_symbol(&glbtab,&entry,TRUE);
ds@1952
  2783
  else
ds@1952
  2784
    return add_symbol(&loctab,&entry,FALSE);
ds@1952
  2785
}
ds@1952
  2786
ds@1952
  2787
SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag,
ds@1952
  2788
                            int dim[],int numdim,int idxtag[])
ds@1952
  2789
{
ds@1952
  2790
  return addvariable2(name,addr,ident,vclass,tag,dim,numdim,idxtag,0);
ds@1952
  2791
}
ds@1952
  2792
ds@1952
  2793
SC_FUNC symbol *addvariable2(const char *name,cell addr,int ident,int vclass,int tag,
ds@1952
  2794
                            int dim[],int numdim,int idxtag[],int slength)
ds@1952
  2795
{
ds@1952
  2796
  symbol *sym;
ds@1952
  2797
ds@1952
  2798
  /* global variables may only be defined once
ds@1952
  2799
   * One complication is that functions returning arrays declare an array
ds@1952
  2800
   * with the same name as the function, so the assertion must allow for
ds@1952
  2801
   * this special case. Another complication is that variables may be
ds@1952
  2802
   * "redeclared" if they are local to an automaton (and findglb() will find
ds@1952
  2803
   * the symbol without states if no symbol with states exists).
ds@1952
  2804
   */
ds@1952
  2805
  assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0
ds@1952
  2806
         || sym->ident==iFUNCTN && sym==curfunc
ds@1952
  2807
         || sym->states==NULL && sc_curstates>0);
ds@1952
  2808
ds@1952
  2809
  if (ident==iARRAY || ident==iREFARRAY) {
ds@1952
  2810
    symbol *parent=NULL,*top;
ds@1952
  2811
    int level;
ds@1952
  2812
    sym=NULL;                   /* to avoid a compiler warning */
ds@1952
  2813
    for (level=0; level<numdim; level++) {
ds@1952
  2814
      top=addsym(name,addr,ident,vclass,tag,uDEFINE);
ds@1952
  2815
      top->dim.array.length=dim[level];
ds@1952
  2816
      if (tag == pc_tag_string && level == numdim - 1) {
ds@1952
  2817
        if (slength == 0)
ds@1952
  2818
          top->dim.array.length=dim[level] * sizeof(cell);
ds@1952
  2819
        else
ds@1952
  2820
          top->dim.array.slength=slength;
ds@1952
  2821
      } else {
ds@1952
  2822
        top->dim.array.slength=0;
ds@1952
  2823
      }
ds@1952
  2824
      top->dim.array.level=(short)(numdim-level-1);
ds@1952
  2825
      top->x.tags.index=idxtag[level];
ds@1952
  2826
      top->parent=parent;
ds@1952
  2827
      parent=top;
ds@1952
  2828
      if (level==0)
ds@1952
  2829
        sym=top;
ds@1952
  2830
    } /* for */
ds@1952
  2831
  } else {
ds@1952
  2832
    sym=addsym(name,addr,ident,vclass,tag,uDEFINE);
ds@1952
  2833
  } /* if */
ds@1952
  2834
  return sym;
ds@1952
  2835
}
ds@1952
  2836
ds@1952
  2837
/*  getlabel
ds@1952
  2838
 *
ds@1952
  2839
 *  Returns te next internal label number. The global variable sc_labnum is
ds@1952
  2840
 *  initialized to zero.
ds@1952
  2841
 */
ds@1952
  2842
SC_FUNC int getlabel(void)
ds@1952
  2843
{
ds@1952
  2844
  return sc_labnum++;
ds@1952
  2845
}
ds@1952
  2846
ds@1952
  2847
/*  itoh
ds@1952
  2848
 *
ds@1952
  2849
 *  Converts a number to a hexadecimal string and returns a pointer to that
ds@1952
  2850
 *  string. This function is NOT re-entrant.
ds@1952
  2851
 */
ds@1952
  2852
SC_FUNC char *itoh(ucell val)
ds@1952
  2853
{
ds@1952
  2854
static char itohstr[30];
ds@1952
  2855
  char *ptr;
ds@1952
  2856
  int i,nibble[16];             /* a 64-bit hexadecimal cell has 16 nibbles */
ds@1952
  2857
  int max;
ds@1952
  2858
ds@1952
  2859
  #if PAWN_CELL_SIZE==16
ds@1952
  2860
    max=4;
ds@1952
  2861
  #elif PAWN_CELL_SIZE==32
ds@1952
  2862
    max=8;
ds@1952
  2863
  #elif PAWN_CELL_SIZE==64
ds@1952
  2864
    max=16;
ds@1952
  2865
  #else
ds@1952
  2866
    #error Unsupported cell size
ds@1952
  2867
  #endif
ds@1952
  2868
  ptr=itohstr;
ds@1952
  2869
  for (i=0; i<max; i+=1){
ds@1952
  2870
    nibble[i]=(int)(val & 0x0f);        /* nibble 0 is lowest nibble */
ds@1952
  2871
    val>>=4;
ds@1952
  2872
  } /* endfor */
ds@1952
  2873
  i=max-1;
ds@1952
  2874
  while (nibble[i]==0 && i>0)   /* search for highest non-zero nibble */
ds@1952
  2875
    i-=1;
ds@1952
  2876
  while (i>=0){
ds@1952
  2877
    if (nibble[i]>=10)
ds@1952
  2878
      *ptr++=(char)('a'+(nibble[i]-10));
ds@1952
  2879
    else
ds@1952
  2880
      *ptr++=(char)('0'+nibble[i]);
ds@1952
  2881
    i-=1;
ds@1952
  2882
  } /* while */
ds@1952
  2883
  *ptr='\0';            /* and a zero-terminator */
ds@1952
  2884
  return itohstr;
ds@1952
  2885
}
ds@1952
  2886