1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/sourcepawn/compiler/sc1.c Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,6665 @@
1.4 +/* Pawn compiler
1.5 + *
1.6 + * Function and variable definition and declaration, statement parser.
1.7 + *
1.8 + * Copyright (c) ITB CompuPhase, 1997-2006
1.9 + *
1.10 + * This software is provided "as-is", without any express or implied warranty.
1.11 + * In no event will the authors be held liable for any damages arising from
1.12 + * the use of this software.
1.13 + *
1.14 + * Permission is granted to anyone to use this software for any purpose,
1.15 + * including commercial applications, and to alter it and redistribute it
1.16 + * freely, subject to the following restrictions:
1.17 + *
1.18 + * 1. The origin of this software must not be misrepresented; you must not
1.19 + * claim that you wrote the original software. If you use this software in
1.20 + * a product, an acknowledgment in the product documentation would be
1.21 + * appreciated but is not required.
1.22 + * 2. Altered source versions must be plainly marked as such, and must not be
1.23 + * misrepresented as being the original software.
1.24 + * 3. This notice may not be removed or altered from any source distribution.
1.25 + *
1.26 + * Version: $Id$
1.27 + */
1.28 +#include <assert.h>
1.29 +#include <ctype.h>
1.30 +#include <limits.h>
1.31 +#include <stdarg.h>
1.32 +#include <stdio.h>
1.33 +#include <stdlib.h>
1.34 +#include <string.h>
1.35 +
1.36 +#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__
1.37 + #include <conio.h>
1.38 + #include <io.h>
1.39 +#endif
1.40 +
1.41 +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
1.42 + #include <sclinux.h>
1.43 + #include <binreloc.h> /* from BinReloc, see www.autopackage.org */
1.44 +#endif
1.45 +
1.46 +#if defined FORTIFY
1.47 + #include <alloc/fortify.h>
1.48 +#endif
1.49 +
1.50 +#if defined __BORLANDC__ || defined __WATCOMC__
1.51 + #include <dos.h>
1.52 + static unsigned total_drives; /* dummy variable */
1.53 + #define dos_setdrive(i) _dos_setdrive(i,&total_drives)
1.54 +#elif defined _MSC_VER && defined _WIN32
1.55 + #include <direct.h> /* for _chdrive() */
1.56 + #define dos_setdrive(i) _chdrive(i)
1.57 +#endif
1.58 +#if defined __BORLANDC__
1.59 + #include <dir.h> /* for chdir() */
1.60 +#elif defined __WATCOMC__
1.61 + #include <direct.h> /* for chdir() */
1.62 +#endif
1.63 +#if defined __WIN32__ || defined _WIN32 || defined _Windows
1.64 + #include <windows.h>
1.65 +#endif
1.66 +
1.67 +#include <time.h>
1.68 +
1.69 +#include "lstring.h"
1.70 +#include "sc.h"
1.71 +#include "svn_version.h"
1.72 +#include "sctracker.h"
1.73 +#define VERSION_STR "3.2.3636"
1.74 +#define VERSION_INT 0x0302
1.75 +
1.76 +int pc_anytag = 0;
1.77 +int pc_functag = 0;
1.78 +int pc_tag_string = 0;
1.79 +
1.80 +static void resetglobals(void);
1.81 +static void initglobals(void);
1.82 +static char *get_extension(char *filename);
1.83 +static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
1.84 + char *rname,char *codepage);
1.85 +static void setconfig(char *root);
1.86 +static void setcaption(void);
1.87 +static void about(void);
1.88 +static void setconstants(void);
1.89 +static void parse(void);
1.90 +static void dumplits(void);
1.91 +static void dumpzero(int count);
1.92 +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst);
1.93 +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
1.94 + int stock,int fconst);
1.95 +static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
1.96 +static int declloc(int fstatic);
1.97 +static void decl_const(int table);
1.98 +static void declstruct();
1.99 +static void decl_enum(int table);
1.100 +static cell needsub(int *tag,constvalue **enumroot);
1.101 +static void initials(int ident,int tag,cell *size,int dim[],int numdim,
1.102 + constvalue *enumroot);
1.103 +static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
1.104 + int startlit,int counteddim[],constvalue *lastdim,
1.105 + constvalue *enumroot,int *errorfound);
1.106 +static cell initvector(int ident,int tag,cell size,int fillzero,
1.107 + constvalue *enumroot,int *errorfound);
1.108 +static cell init(int ident,int *tag,int *errorfound);
1.109 +static int getstates(const char *funcname);
1.110 +static void attachstatelist(symbol *sym, int state_id);
1.111 +static void funcstub(int fnative);
1.112 +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
1.113 +static int declargs(symbol *sym,int chkshadow);
1.114 +static void doarg(char *name,int ident,int offset,int tags[],int numtags,
1.115 + int fpublic,int fconst,int chkshadow,arginfo *arg);
1.116 +static void make_report(symbol *root,FILE *log,char *sourcefile);
1.117 +static void reduce_referrers(symbol *root);
1.118 +static long max_stacksize(symbol *root,int *recursion);
1.119 +static int testsymbols(symbol *root,int level,int testlabs,int testconst);
1.120 +static void destructsymbols(symbol *root,int level);
1.121 +static constvalue *find_constval_byval(constvalue *table,cell val);
1.122 +static symbol *fetchlab(char *name);
1.123 +static void statement(int *lastindent,int allow_decl);
1.124 +static void compound(int stmt_sameline,int starttok);
1.125 +static int test(int label,int parens,int invert);
1.126 +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
1.127 + int *tag,symbol **symptr,int chkfuncresult);
1.128 +static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
1.129 + int *tag,symbol **symptr,int chkfuncresult,value *lval);
1.130 +static void doassert(void);
1.131 +static void doexit(void);
1.132 +static int doif(void);
1.133 +static int dowhile(void);
1.134 +static int dodo(void);
1.135 +static int dofor(void);
1.136 +static void doswitch(void);
1.137 +static void dogoto(void);
1.138 +static void dolabel(void);
1.139 +static void doreturn(void);
1.140 +static void dofuncenum(int listmode);
1.141 +static void dobreak(void);
1.142 +static void docont(void);
1.143 +static void dosleep(void);
1.144 +static void dostate(void);
1.145 +static void addwhile(int *ptr);
1.146 +static void delwhile(void);
1.147 +static int *readwhile(void);
1.148 +static void inst_datetime_defines(void);
1.149 +static void inst_binary_name(char *binfname);
1.150 +
1.151 +enum {
1.152 + TEST_PLAIN, /* no parentheses */
1.153 + TEST_THEN, /* '(' <expr> ')' or <expr> 'then' */
1.154 + TEST_DO, /* '(' <expr> ')' or <expr> 'do' */
1.155 + TEST_OPT, /* '(' <expr> ')' or <expr> */
1.156 +};
1.157 +static int norun = 0; /* the compiler never ran */
1.158 +static int autozero = 1; /* if 1 will zero out the variable, if 0 omit the zeroing */
1.159 +static int lastst = 0; /* last executed statement type */
1.160 +static int nestlevel = 0; /* number of active (open) compound statements */
1.161 +static int endlessloop= 0; /* nesting level of endless loop */
1.162 +static int rettype = 0; /* the type that a "return" expression should have */
1.163 +static int skipinput = 0; /* number of lines to skip from the first input file */
1.164 +static int optproccall = TRUE; /* support "procedure call" */
1.165 +static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */
1.166 +static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */
1.167 +static int sc_parsenum = 0; /* number of the extra parses */
1.168 +static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */
1.169 +static int *wqptr; /* pointer to next entry */
1.170 +#if !defined SC_LIGHT
1.171 + static char sc_rootpath[_MAX_PATH];
1.172 + static char *sc_documentation=NULL;/* main documentation */
1.173 +#endif
1.174 +#if defined __WIN32__ || defined _WIN32 || defined _Windows
1.175 + static HWND hwndFinish = 0;
1.176 +#endif
1.177 +
1.178 +SC_VDECL int glbstringread = 0;
1.179 +char g_tmpfile[_MAX_PATH] = {0};
1.180 +
1.181 +/* "main" of the compiler
1.182 + */
1.183 +#if defined __cplusplus
1.184 + extern "C"
1.185 +#endif
1.186 +int pc_compile(int argc, char *argv[])
1.187 +{
1.188 + int entry,i,jmpcode;
1.189 + int retcode;
1.190 + char incfname[_MAX_PATH];
1.191 + char reportname[_MAX_PATH];
1.192 + char codepage[MAXCODEPAGE+1];
1.193 + FILE *binf;
1.194 + void *inpfmark;
1.195 + int lcl_packstr,lcl_needsemicolon,lcl_tabsize;
1.196 + #if !defined SC_LIGHT
1.197 + int hdrsize=0;
1.198 + #endif
1.199 + char *ptr;
1.200 +
1.201 + /* set global variables to their initial value */
1.202 + binf=NULL;
1.203 + initglobals();
1.204 + errorset(sRESET,0);
1.205 + errorset(sEXPRRELEASE,0);
1.206 + lexinit();
1.207 +
1.208 + /* make sure that we clean up on a fatal error; do this before the first
1.209 + * call to error(). */
1.210 + if ((jmpcode=setjmp(errbuf))!=0)
1.211 + goto cleanup;
1.212 +
1.213 + /* allocate memory for fixed tables */
1.214 + inpfname=(char*)malloc(_MAX_PATH);
1.215 + if (inpfname==NULL)
1.216 + error(123); /* insufficient memory */
1.217 + litq=(cell*)malloc(litmax*sizeof(cell));
1.218 + if (litq==NULL)
1.219 + error(123); /* insufficient memory */
1.220 + if (!phopt_init())
1.221 + error(123); /* insufficient memory */
1.222 +
1.223 + setopt(argc,argv,outfname,errfname,incfname,reportname,codepage);
1.224 + strcpy(binfname,outfname);
1.225 + ptr=get_extension(binfname);
1.226 + if (ptr!=NULL && stricmp(ptr,".asm")==0)
1.227 + set_extension(binfname,".smx",TRUE);
1.228 + else
1.229 + set_extension(binfname,".smx",FALSE);
1.230 + /* set output names that depend on the input name */
1.231 + if (sc_listing)
1.232 + set_extension(outfname,".lst",TRUE);
1.233 + else
1.234 + set_extension(outfname,".asm",TRUE);
1.235 + if (strlen(errfname)!=0)
1.236 + remove(errfname); /* delete file on startup */
1.237 + else if (verbosity>0)
1.238 + setcaption();
1.239 + setconfig(argv[0]); /* the path to the include and codepage files */
1.240 + sc_ctrlchar_org=sc_ctrlchar;
1.241 + lcl_packstr=sc_packstr;
1.242 + lcl_needsemicolon=sc_needsemicolon;
1.243 + lcl_tabsize=sc_tabsize;
1.244 + #if !defined NO_CODEPAGE
1.245 + if (!cp_set(codepage)) /* set codepage */
1.246 + error(128); /* codepage mapping file not found */
1.247 + #endif
1.248 + /* optionally create a temporary input file that is a collection of all
1.249 + * input files
1.250 + */
1.251 + assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */
1.252 + if (get_sourcefile(1)!=NULL) {
1.253 + /* there are at least two or more source files */
1.254 + char *tname,*sname;
1.255 + FILE *ftmp,*fsrc;
1.256 + int fidx;
1.257 + #if defined __WIN32__ || defined _WIN32
1.258 + tname=_tempnam(NULL,"pawn");
1.259 + #elif defined __MSDOS__ || defined _Windows
1.260 + tname=tempnam(NULL,"pawn");
1.261 + #elif defined(MACOS) && !defined(__MACH__)
1.262 + /* tempnam is not supported for the Macintosh CFM build. */
1.263 + error(124,get_sourcefile(1));
1.264 + tname=NULL;
1.265 + sname=NULL;
1.266 + #else
1.267 + tname=tempnam(NULL,"pawn");
1.268 + #endif
1.269 + ftmp=(FILE*)pc_createsrc(tname);
1.270 + for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) {
1.271 + unsigned char tstring[128];
1.272 + fsrc=(FILE*)pc_opensrc(sname);
1.273 + if (fsrc==NULL) {
1.274 + pc_closesrc(ftmp);
1.275 + remove(tname);
1.276 + strcpy(inpfname,sname); /* avoid invalid filename */
1.277 + error(120,sname);
1.278 + } /* if */
1.279 + pc_writesrc(ftmp,(unsigned char*)"#file \"");
1.280 + pc_writesrc(ftmp,(unsigned char*)sname);
1.281 + pc_writesrc(ftmp,(unsigned char*)"\"\n");
1.282 + while (!pc_eofsrc(fsrc) && pc_readsrc(fsrc,tstring,sizeof tstring)) {
1.283 + pc_writesrc(ftmp,tstring);
1.284 + } /* while */
1.285 + pc_closesrc(fsrc);
1.286 + } /* for */
1.287 + pc_closesrc(ftmp);
1.288 + strcpy(inpfname,tname);
1.289 + strcpy(g_tmpfile,tname);
1.290 + free(tname);
1.291 + } else {
1.292 + strcpy(inpfname,get_sourcefile(0));
1.293 + } /* if */
1.294 + inpf_org=(FILE*)pc_opensrc(inpfname);
1.295 + if (inpf_org==NULL)
1.296 + error(120,inpfname);
1.297 + freading=TRUE;
1.298 + outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */
1.299 + if (outf==NULL)
1.300 + error(121,outfname);
1.301 + /* immediately open the binary file, for other programs to check */
1.302 + if (sc_asmfile || sc_listing) {
1.303 + binf=NULL;
1.304 + } else {
1.305 + binf=(FILE*)pc_openbin(binfname);
1.306 + if (binf==NULL)
1.307 + error(121,binfname);
1.308 + } /* if */
1.309 + setconstants(); /* set predefined constants and tagnames */
1.310 + for (i=0; i<skipinput; i++) /* skip lines in the input file */
1.311 + if (pc_readsrc(inpf_org,pline,sLINEMAX)!=NULL)
1.312 + fline++; /* keep line number up to date */
1.313 + skipinput=fline;
1.314 + sc_status=statFIRST;
1.315 + /* write starting options (from the command line or the configuration file) */
1.316 + if (sc_listing) {
1.317 + char string[150];
1.318 + sprintf(string,"#pragma ctrlchar 0x%02x\n"
1.319 + "#pragma pack %s\n"
1.320 + "#pragma semicolon %s\n"
1.321 + "#pragma tabsize %d\n",
1.322 + sc_ctrlchar,
1.323 + sc_packstr ? "true" : "false",
1.324 + sc_needsemicolon ? "true" : "false",
1.325 + sc_tabsize);
1.326 + pc_writeasm(outf,string);
1.327 + setfiledirect(inpfname);
1.328 + } /* if */
1.329 + /* do the first pass through the file (or possibly two or more "first passes") */
1.330 + sc_parsenum=0;
1.331 + inpfmark=pc_getpossrc(inpf_org);
1.332 + do {
1.333 + /* reset "defined" flag of all functions and global variables */
1.334 + reduce_referrers(&glbtab);
1.335 + delete_symbols(&glbtab,0,TRUE,FALSE);
1.336 + #if !defined NO_DEFINE
1.337 + delete_substtable();
1.338 + inst_datetime_defines();
1.339 + inst_binary_name(binfname);
1.340 + #endif
1.341 + resetglobals();
1.342 + pstructs_free();
1.343 + funcenums_free();
1.344 + sc_ctrlchar=sc_ctrlchar_org;
1.345 + sc_packstr=lcl_packstr;
1.346 + sc_needsemicolon=lcl_needsemicolon;
1.347 + sc_tabsize=lcl_tabsize;
1.348 + errorset(sRESET,0);
1.349 + /* reset the source file */
1.350 + inpf=inpf_org;
1.351 + freading=TRUE;
1.352 + pc_resetsrc(inpf,inpfmark); /* reset file position */
1.353 + fline=skipinput; /* reset line number */
1.354 + sc_reparse=FALSE; /* assume no extra passes */
1.355 + sc_status=statFIRST; /* resetglobals() resets it to IDLE */
1.356 +
1.357 + if (strlen(incfname)>0) {
1.358 + if (strcmp(incfname,sDEF_PREFIX)==0) {
1.359 + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */
1.360 + } else {
1.361 + if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */
1.362 + error(120,incfname); /* cannot read from ... (fatal error) */
1.363 + } /* if */
1.364 + } /* if */
1.365 + preprocess(); /* fetch first line */
1.366 + parse(); /* process all input */
1.367 + sc_parsenum++;
1.368 + } while (sc_reparse);
1.369 +
1.370 + /* second (or third) pass */
1.371 + sc_status=statWRITE; /* set, to enable warnings */
1.372 + state_conflict(&glbtab);
1.373 +
1.374 + /* write a report, if requested */
1.375 + #if !defined SC_LIGHT
1.376 + if (sc_makereport) {
1.377 + FILE *frep=stdout;
1.378 + if (strlen(reportname)>0)
1.379 + frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */
1.380 + if (frep!=NULL) {
1.381 + make_report(&glbtab,frep,get_sourcefile(0));
1.382 + if (strlen(reportname)>0)
1.383 + fclose(frep);
1.384 + } /* if */
1.385 + if (sc_documentation!=NULL) {
1.386 + free(sc_documentation);
1.387 + sc_documentation=NULL;
1.388 + } /* if */
1.389 + } /* if */
1.390 + #endif
1.391 + if (sc_listing)
1.392 + goto cleanup;
1.393 +
1.394 + /* ??? for re-parsing the listing file instead of the original source
1.395 + * file (and doing preprocessing twice):
1.396 + * - close input file, close listing file
1.397 + * - re-open listing file for reading (inpf)
1.398 + * - open assembler file (outf)
1.399 + */
1.400 +
1.401 + /* reset "defined" flag of all functions and global variables */
1.402 + reduce_referrers(&glbtab);
1.403 + delete_symbols(&glbtab,0,TRUE,FALSE);
1.404 + funcenums_free();
1.405 + pstructs_free();
1.406 + #if !defined NO_DEFINE
1.407 + delete_substtable();
1.408 + inst_datetime_defines();
1.409 + inst_binary_name(binfname);
1.410 + #endif
1.411 + resetglobals();
1.412 + sc_ctrlchar=sc_ctrlchar_org;
1.413 + sc_packstr=lcl_packstr;
1.414 + sc_needsemicolon=lcl_needsemicolon;
1.415 + sc_tabsize=lcl_tabsize;
1.416 + errorset(sRESET,0);
1.417 + /* reset the source file */
1.418 + inpf=inpf_org;
1.419 + freading=TRUE;
1.420 + pc_resetsrc(inpf,inpfmark); /* reset file position */
1.421 + fline=skipinput; /* reset line number */
1.422 + lexinit(); /* clear internal flags of lex() */
1.423 + sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */
1.424 + writeleader(&glbtab);
1.425 + insert_dbgfile(inpfname);
1.426 + if (strlen(incfname)>0) {
1.427 + if (strcmp(incfname,sDEF_PREFIX)==0)
1.428 + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */
1.429 + else
1.430 + plungequalifiedfile(incfname); /* parse implicit include file (again) */
1.431 + } /* if */
1.432 + preprocess(); /* fetch first line */
1.433 + parse(); /* process all input */
1.434 + /* inpf is already closed when readline() attempts to pop of a file */
1.435 + writetrailer(); /* write remaining stuff */
1.436 +
1.437 + entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined
1.438 + * functions and variables */
1.439 + if (!entry)
1.440 + error(13); /* no entry point (no public functions) */
1.441 +
1.442 +cleanup:
1.443 + if (inpf!=NULL) /* main source file is not closed, do it now */
1.444 + pc_closesrc(inpf);
1.445 + /* write the binary file (the file is already open) */
1.446 + if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) {
1.447 + assert(binf!=NULL);
1.448 + pc_resetasm(outf); /* flush and loop back, for reading */
1.449 + #if !defined SC_LIGHT
1.450 + hdrsize=
1.451 + #endif
1.452 + assemble(binf,outf); /* assembler file is now input */
1.453 + } /* if */
1.454 + if (outf!=NULL) {
1.455 + pc_closeasm(outf,!(sc_asmfile || sc_listing));
1.456 + outf=NULL;
1.457 + } /* if */
1.458 + if (binf!=NULL) {
1.459 + pc_closebin(binf,errnum!=0);
1.460 + binf=NULL;
1.461 + } /* if */
1.462 +
1.463 + #if !defined SC_LIGHT
1.464 + if (errnum==0 && strlen(errfname)==0) {
1.465 +#if 0 //bug in compiler -- someone's script caused this function to infrecurs
1.466 + int recursion;
1.467 + long stacksize=max_stacksize(&glbtab,&recursion);
1.468 +#endif
1.469 + int flag_exceed=0;
1.470 + if (pc_amxlimit>0) {
1.471 + long totalsize=hdrsize+code_idx;
1.472 + if (pc_amxram==0)
1.473 + totalsize+=(glb_declared+pc_stksize)*sizeof(cell);
1.474 + if (totalsize>=pc_amxlimit)
1.475 + flag_exceed=1;
1.476 + } /* if */
1.477 + if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram)
1.478 + flag_exceed=1;
1.479 + if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || flag_exceed) {
1.480 + pc_printf("Header size: %8ld bytes\n", (long)hdrsize);
1.481 + pc_printf("Code size: %8ld bytes\n", (long)code_idx);
1.482 + pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell));
1.483 + pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell));
1.484 +#if 0
1.485 + pc_printf("estimated max. usage");
1.486 + if (recursion)
1.487 + pc_printf(": unknown, due to recursion\n");
1.488 + else if ((pc_memflags & suSLEEP_INSTR)!=0)
1.489 + pc_printf(": unknown, due to the \"sleep\" instruction\n");
1.490 + else
1.491 + pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell));
1.492 +#endif
1.493 + pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell));
1.494 + } /* if */
1.495 + if (flag_exceed)
1.496 + error(126,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */
1.497 + } /* if */
1.498 + #endif
1.499 +
1.500 + if (g_tmpfile[0] != '\0') {
1.501 + remove(g_tmpfile);
1.502 + }
1.503 + if (inpfname!=NULL) {
1.504 + free(inpfname);
1.505 + } /* if */
1.506 + if (litq!=NULL)
1.507 + free(litq);
1.508 + phopt_cleanup();
1.509 + stgbuffer_cleanup();
1.510 + clearstk();
1.511 + assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols
1.512 + * should already have been deleted */
1.513 + delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet
1.514 + * done (i.e. on a fatal error) */
1.515 + delete_symbols(&glbtab,0,TRUE,TRUE);
1.516 + delete_consttable(&tagname_tab);
1.517 + delete_consttable(&libname_tab);
1.518 + delete_consttable(&sc_automaton_tab);
1.519 + delete_consttable(&sc_state_tab);
1.520 + state_deletetable();
1.521 + delete_aliastable();
1.522 + delete_pathtable();
1.523 + delete_sourcefiletable();
1.524 + delete_dbgstringtable();
1.525 + funcenums_free();
1.526 + pstructs_free();
1.527 + #if !defined NO_DEFINE
1.528 + delete_substtable();
1.529 + #endif
1.530 + #if !defined SC_LIGHT
1.531 + delete_docstringtable();
1.532 + if (sc_documentation!=NULL)
1.533 + free(sc_documentation);
1.534 + #endif
1.535 + delete_autolisttable();
1.536 + if (errnum!=0) {
1.537 + if (strlen(errfname)==0)
1.538 + pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : "");
1.539 + retcode=1;
1.540 + } else if (warnnum!=0){
1.541 + if (strlen(errfname)==0)
1.542 + pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : "");
1.543 + retcode=0; /* use "0", so that MAKE and similar tools continue */
1.544 + } else {
1.545 + retcode=jmpcode;
1.546 + if (retcode==0 && verbosity>=2)
1.547 + pc_printf("\nDone.\n");
1.548 + } /* if */
1.549 + #if defined __WIN32__ || defined _WIN32 || defined _Windows
1.550 + if (IsWindow(hwndFinish))
1.551 + PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L);
1.552 + #endif
1.553 + #if defined FORTIFY
1.554 + Fortify_ListAllMemory();
1.555 + #endif
1.556 + return retcode;
1.557 +}
1.558 +
1.559 +#if defined __cplusplus
1.560 + extern "C"
1.561 +#endif
1.562 +int pc_addconstant(char *name,cell value,int tag)
1.563 +{
1.564 + errorset(sFORCESET,0); /* make sure error engine is silenced */
1.565 + sc_status=statIDLE;
1.566 + add_constant(name,value,sGLOBAL,tag);
1.567 + return 1;
1.568 +}
1.569 +
1.570 +static void inst_binary_name(char *binfname)
1.571 +{
1.572 + size_t i, len;
1.573 + char *binptr;
1.574 + char newpath[512], newname[512];
1.575 +
1.576 + binptr = NULL;
1.577 + len = strlen(binfname);
1.578 + for (i = len - 1; i >= 0 && i < len; i--)
1.579 + {
1.580 + if (binfname[i] == '/'
1.581 +#if defined WIN32 || defined _WIN32
1.582 + || binfname[i] == '\\'
1.583 +#endif
1.584 + )
1.585 + {
1.586 + binptr = &binfname[i + 1];
1.587 + break;
1.588 + }
1.589 + }
1.590 +
1.591 + if (binptr == NULL)
1.592 + {
1.593 + binptr = binfname;
1.594 + }
1.595 +
1.596 + snprintf(newpath, sizeof(newpath), "\"%s\"", binfname);
1.597 + snprintf(newname, sizeof(newname), "\"%s\"", binptr);
1.598 +
1.599 + insert_subst("__BINARY_PATH__", newpath, 15);
1.600 + insert_subst("__BINARY_NAME__", newname, 15);
1.601 +}
1.602 +
1.603 +static void inst_datetime_defines(void)
1.604 +{
1.605 + char date[64];
1.606 + char ltime[64];
1.607 + time_t td;
1.608 + struct tm *curtime;
1.609 +
1.610 + time(&td);
1.611 + curtime = localtime(&td);
1.612 + strftime(date, 31, "\"%m/%d/%Y\"", curtime);
1.613 + strftime(ltime, 31, "\"%H:%M:%S\"", curtime);
1.614 +
1.615 + insert_subst("__DATE__", date, 8);
1.616 + insert_subst("__TIME__", ltime, 8);
1.617 +}
1.618 +
1.619 +#if defined __cplusplus
1.620 + extern "C"
1.621 +#endif
1.622 +int pc_addtag(char *name)
1.623 +{
1.624 + cell val;
1.625 + constvalue *ptr;
1.626 + int last,tag;
1.627 +
1.628 + if (name==NULL) {
1.629 + /* no tagname was given, check for one */
1.630 + if (lex(&val,&name)!=tLABEL) {
1.631 + lexpush();
1.632 + return 0; /* untagged */
1.633 + } /* if */
1.634 + } /* if */
1.635 +
1.636 + assert(strchr(name,':')==NULL); /* colon should already have been stripped */
1.637 + last=0;
1.638 + ptr=tagname_tab.next;
1.639 + while (ptr!=NULL) {
1.640 + tag=(int)(ptr->value & TAGMASK);
1.641 + if (strcmp(name,ptr->name)==0)
1.642 + return tag; /* tagname is known, return its sequence number */
1.643 + tag &= (int)~FIXEDTAG;
1.644 + tag &= (int)~FUNCTAG;
1.645 + if (tag>last)
1.646 + last=tag;
1.647 + ptr=ptr->next;
1.648 + } /* while */
1.649 +
1.650 + /* tagname currently unknown, add it */
1.651 + tag=last+1; /* guaranteed not to exist already */
1.652 + if (isupper(*name))
1.653 + tag |= (int)FIXEDTAG;
1.654 + append_constval(&tagname_tab,name,(cell)tag,0);
1.655 + return tag;
1.656 +}
1.657 +
1.658 +int pc_addfunctag(char *name)
1.659 +{
1.660 + cell val;
1.661 + constvalue *ptr;
1.662 + int last,tag;
1.663 +
1.664 + if (name==NULL) {
1.665 + /* no tagname was given, check for one */
1.666 + if (lex(&val,&name)!=tLABEL) {
1.667 + lexpush();
1.668 + return 0; /* untagged */
1.669 + } /* if */
1.670 + } /* if */
1.671 +
1.672 + assert(strchr(name,':')==NULL); /* colon should already have been stripped */
1.673 + last=0;
1.674 + ptr=tagname_tab.next;
1.675 + while (ptr!=NULL) {
1.676 + tag=(int)(ptr->value & TAGMASK);
1.677 + if (strcmp(name,ptr->name)==0)
1.678 + return tag; /* tagname is known, return its sequence number */
1.679 + tag &= (int)~FIXEDTAG;
1.680 + tag &= (int)~FUNCTAG;
1.681 + if (tag>last)
1.682 + last=tag;
1.683 + ptr=ptr->next;
1.684 + } /* while */
1.685 +
1.686 + /* tagname currently unknown, add it */
1.687 + tag=last+1; /* guaranteed not to exist already */
1.688 + if (isupper(*name))
1.689 + tag |= (int)FIXEDTAG;
1.690 + tag |= (int)FUNCTAG;
1.691 + append_constval(&tagname_tab,name,(cell)tag,0);
1.692 + return tag;
1.693 +}
1.694 +
1.695 +static void resetglobals(void)
1.696 +{
1.697 + /* reset the subset of global variables that is modified by the first pass */
1.698 + curfunc=NULL; /* pointer to current function */
1.699 + lastst=0; /* last executed statement type */
1.700 + nestlevel=0; /* number of active (open) compound statements */
1.701 + rettype=0; /* the type that a "return" expression should have */
1.702 + litidx=0; /* index to literal table */
1.703 + stgidx=0; /* index to the staging buffer */
1.704 + sc_labnum=0; /* top value of (internal) labels */
1.705 + staging=0; /* true if staging output */
1.706 + declared=0; /* number of local cells declared */
1.707 + glb_declared=0; /* number of global cells declared */
1.708 + code_idx=0; /* number of bytes with generated code */
1.709 + ntv_funcid=0; /* incremental number of native function */
1.710 + curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */
1.711 + freading=FALSE; /* no input file ready yet */
1.712 + fline=0; /* the line number in the current file */
1.713 + fnumber=0; /* the file number in the file table (debugging) */
1.714 + fcurrent=0; /* current file being processed (debugging) */
1.715 + sc_intest=FALSE; /* true if inside a test */
1.716 + sideeffect=0; /* true if an expression causes a side-effect */
1.717 + stmtindent=0; /* current indent of the statement */
1.718 + indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */
1.719 + sc_allowtags=TRUE; /* allow/detect tagnames */
1.720 + sc_status=statIDLE;
1.721 + sc_allowproccall=FALSE;
1.722 + pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */
1.723 + sc_alignnext=FALSE;
1.724 + pc_docexpr=FALSE;
1.725 + pc_deprecate=NULL;
1.726 + sc_curstates=0;
1.727 + pc_memflags=0;
1.728 +}
1.729 +
1.730 +static void initglobals(void)
1.731 +{
1.732 + resetglobals();
1.733 +
1.734 + sc_asmfile=FALSE; /* do not create .ASM file */
1.735 + sc_listing=FALSE; /* do not create .LST file */
1.736 + skipinput=0; /* number of lines to skip from the first input file */
1.737 + sc_ctrlchar=CTRL_CHAR;/* the escape character */
1.738 + litmax=sDEF_LITMAX; /* current size of the literal table */
1.739 + errnum=0; /* number of errors */
1.740 + warnnum=0; /* number of warnings */
1.741 + optproccall=FALSE; /* sourcemod: do not support "procedure call" */
1.742 + verbosity=1; /* verbosity level, no copyright banner */
1.743 + sc_debug=sCHKBOUNDS|sSYMBOLIC; /* sourcemod: full debug stuff */
1.744 + pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */
1.745 + sc_packstr=TRUE; /* strings are packed by default */
1.746 + sc_compress=FALSE; /* always disable compact encoding! */
1.747 + sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */
1.748 + sc_dataalign=sizeof(cell);
1.749 + pc_stksize=sDEF_AMXSTACK;/* default stack size */
1.750 + pc_amxlimit=0; /* no limit on size of the abstract machine */
1.751 + pc_amxram=0; /* no limit on data size of the abstract machine */
1.752 + sc_tabsize=8; /* assume a TAB is 8 spaces */
1.753 + sc_rationaltag=0; /* assume no support for rational numbers */
1.754 + rational_digits=0; /* number of fractional digits */
1.755 +
1.756 + outfname[0]='\0'; /* output file name */
1.757 + errfname[0]='\0'; /* error file name */
1.758 + inpf=NULL; /* file read from */
1.759 + inpfname=NULL; /* pointer to name of the file currently read from */
1.760 + outf=NULL; /* file written to */
1.761 + litq=NULL; /* the literal queue */
1.762 + glbtab.next=NULL; /* clear global variables/constants table */
1.763 + loctab.next=NULL; /* " local " / " " */
1.764 + tagname_tab.next=NULL;/* tagname table */
1.765 + libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */
1.766 +
1.767 + pline[0]='\0'; /* the line read from the input file */
1.768 + lptr=NULL; /* points to the current position in "pline" */
1.769 + curlibrary=NULL; /* current library */
1.770 + inpf_org=NULL; /* main source file */
1.771 +
1.772 + wqptr=wq; /* initialize while queue pointer */
1.773 +
1.774 +#if !defined SC_LIGHT
1.775 + sc_documentation=NULL;
1.776 + sc_makereport=FALSE; /* do not generate a cross-reference report */
1.777 +#endif
1.778 +}
1.779 +
1.780 +static char *get_extension(char *filename)
1.781 +{
1.782 + char *ptr;
1.783 +
1.784 + assert(filename!=NULL);
1.785 + ptr=strrchr(filename,'.');
1.786 + if (ptr!=NULL) {
1.787 + /* ignore extension on a directory or at the start of the filename */
1.788 + if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR)
1.789 + ptr=NULL;
1.790 + } /* if */
1.791 + return ptr;
1.792 +}
1.793 +
1.794 +/* set_extension
1.795 + * Set the default extension, or force an extension. To erase the
1.796 + * extension of a filename, set "extension" to an empty string.
1.797 + */
1.798 +SC_FUNC void set_extension(char *filename,char *extension,int force)
1.799 +{
1.800 + char *ptr;
1.801 +
1.802 + assert(extension!=NULL && (*extension=='\0' || *extension=='.'));
1.803 + assert(filename!=NULL);
1.804 + ptr=get_extension(filename);
1.805 + if (force && ptr!=NULL)
1.806 + *ptr='\0'; /* set zero terminator at the position of the period */
1.807 + if (force || ptr==NULL)
1.808 + strcat(filename,extension);
1.809 +}
1.810 +
1.811 +static const char *option_value(const char *optptr)
1.812 +{
1.813 + return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1;
1.814 +}
1.815 +
1.816 +static int toggle_option(const char *optptr, int option)
1.817 +{
1.818 + switch (*option_value(optptr)) {
1.819 + case '\0':
1.820 + option=!option;
1.821 + break;
1.822 + case '-':
1.823 + option=FALSE;
1.824 + break;
1.825 + case '+':
1.826 + option=TRUE;
1.827 + break;
1.828 + default:
1.829 + about();
1.830 + } /* switch */
1.831 + return option;
1.832 +}
1.833 +
1.834 +/* Parsing command line options is indirectly recursive: parseoptions()
1.835 + * calls parserespf() to handle options in a a response file and
1.836 + * parserespf() calls parseoptions() at its turn after having created
1.837 + * an "option list" from the contents of the file.
1.838 + */
1.839 +static void parserespf(char *filename,char *oname,char *ename,char *pname,
1.840 + char *rname, char *codepage);
1.841 +
1.842 +static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname,
1.843 + char *rname, char *codepage)
1.844 +{
1.845 + char str[_MAX_PATH],*name;
1.846 + const char *ptr;
1.847 + int arg,i,isoption;
1.848 +
1.849 + for (arg=1; arg<argc; arg++) {
1.850 + #if DIRSEP_CHAR=='/'
1.851 + isoption= argv[arg][0]=='-';
1.852 + #else
1.853 + isoption= argv[arg][0]=='/' || argv[arg][0]=='-';
1.854 + #endif
1.855 + if (isoption) {
1.856 + ptr=&argv[arg][1];
1.857 + switch (*ptr) {
1.858 + case 'A':
1.859 + i=atoi(option_value(ptr));
1.860 + if ((i % sizeof(cell))==0)
1.861 + sc_dataalign=i;
1.862 + else
1.863 + about();
1.864 + break;
1.865 + case 'a':
1.866 + if (*(ptr+1)!='\0')
1.867 + about();
1.868 + sc_asmfile=TRUE; /* skip last pass of making binary file */
1.869 + if (verbosity>1)
1.870 + verbosity=1;
1.871 + break;
1.872 +#if 0 /* not allowed in SourceMod */
1.873 + case 'C':
1.874 + #if AMX_COMPACTMARGIN > 2
1.875 + sc_compress=toggle_option(ptr,sc_compress);
1.876 + #else
1.877 + about();
1.878 + #endif
1.879 + break;
1.880 +#endif
1.881 + case 'c':
1.882 + strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */
1.883 + break;
1.884 +#if defined dos_setdrive
1.885 + case 'D': /* set active directory */
1.886 + ptr=option_value(ptr);
1.887 + if (ptr[1]==':')
1.888 + dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */
1.889 + chdir(ptr);
1.890 + break;
1.891 +#endif
1.892 +#if 0 /* not allowed to change for SourceMod */
1.893 + case 'd':
1.894 + switch (*option_value(ptr)) {
1.895 + case '0':
1.896 + sc_debug=0;
1.897 + break;
1.898 + case '1':
1.899 + sc_debug=sCHKBOUNDS; /* assertions and bounds checking */
1.900 + break;
1.901 + case '2':
1.902 + sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */
1.903 + break;
1.904 + case '3':
1.905 + sc_debug=sCHKBOUNDS | sSYMBOLIC;
1.906 + pc_optimize=sOPTIMIZE_NONE;
1.907 + /* also avoid peephole optimization */
1.908 + break;
1.909 + default:
1.910 + about();
1.911 + } /* switch */
1.912 + break;
1.913 +#endif
1.914 + case 'e':
1.915 + strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */
1.916 + break;
1.917 +#if defined __WIN32__ || defined _WIN32 || defined _Windows
1.918 + case 'H':
1.919 + hwndFinish=(HWND)atoi(option_value(ptr));
1.920 + if (!IsWindow(hwndFinish))
1.921 + hwndFinish=(HWND)0;
1.922 + break;
1.923 +#endif
1.924 + case 'i':
1.925 + strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */
1.926 + i=strlen(str);
1.927 + if (i>0) {
1.928 + if (str[i-1]!=DIRSEP_CHAR) {
1.929 + str[i]=DIRSEP_CHAR;
1.930 + str[i+1]='\0';
1.931 + } /* if */
1.932 + insert_path(str);
1.933 + } /* if */
1.934 + break;
1.935 + case 'l':
1.936 + if (*(ptr+1)!='\0')
1.937 + about();
1.938 + sc_listing=TRUE; /* skip second pass & code generation */
1.939 + break;
1.940 + case 'o':
1.941 + strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */
1.942 + break;
1.943 + case 'O':
1.944 + pc_optimize=*option_value(ptr) - '0';
1.945 + if (pc_optimize<sOPTIMIZE_NONE || pc_optimize>=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO)
1.946 + about();
1.947 + break;
1.948 + case 'p':
1.949 + strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */
1.950 + break;
1.951 +#if !defined SC_LIGHT
1.952 + case 'r':
1.953 + strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */
1.954 + sc_makereport=TRUE;
1.955 + if (strlen(rname)>0) {
1.956 + set_extension(rname,".xml",FALSE);
1.957 + } else if ((name=get_sourcefile(0))!=NULL) {
1.958 + assert(strlen(rname)==0);
1.959 + assert(strlen(name)<_MAX_PATH);
1.960 + if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
1.961 + ptr++; /* strip path */
1.962 + else
1.963 + ptr=name;
1.964 + assert(strlen(ptr)<_MAX_PATH);
1.965 + strcpy(rname,ptr);
1.966 + set_extension(rname,".xml",TRUE);
1.967 + } /* if */
1.968 + break;
1.969 +#endif
1.970 + case 'S':
1.971 + i=atoi(option_value(ptr));
1.972 + if (i>32)
1.973 + pc_stksize=(cell)i; /* stack size has minimum size */
1.974 + else
1.975 + about();
1.976 + break;
1.977 + case 's':
1.978 + skipinput=atoi(option_value(ptr));
1.979 + break;
1.980 + case 't':
1.981 + sc_tabsize=atoi(option_value(ptr));
1.982 + break;
1.983 + case 'v':
1.984 + verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2;
1.985 + if (sc_asmfile && verbosity>1)
1.986 + verbosity=1;
1.987 + break;
1.988 + case 'w':
1.989 + i=(int)strtol(option_value(ptr),(char **)&ptr,10);
1.990 + if (*ptr=='-')
1.991 + pc_enablewarning(i,0);
1.992 + else if (*ptr=='+')
1.993 + pc_enablewarning(i,1);
1.994 + else if (*ptr=='\0')
1.995 + pc_enablewarning(i,2);
1.996 + break;
1.997 + case 'X':
1.998 + if (*(ptr+1)=='D') {
1.999 + i=atoi(option_value(ptr+1));
1.1000 + if (i>64)
1.1001 + pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */
1.1002 + else
1.1003 + about();
1.1004 + } else {
1.1005 + i=atoi(option_value(ptr));
1.1006 + if (i>64)
1.1007 + pc_amxlimit=(cell)i;/* abstract machine has minimum size */
1.1008 + else
1.1009 + about();
1.1010 + } /* if */
1.1011 + break;
1.1012 + case '\\': /* use \ instead for escape characters */
1.1013 + sc_ctrlchar='\\';
1.1014 + break;
1.1015 + case '^': /* use ^ instead for escape characters */
1.1016 + sc_ctrlchar='^';
1.1017 + break;
1.1018 + case ';':
1.1019 + sc_needsemicolon=toggle_option(ptr,sc_needsemicolon);
1.1020 + break;
1.1021 +#if 0 /* not allowed to change in SourceMod */
1.1022 + case '(':
1.1023 + optproccall=!toggle_option(ptr,!optproccall);
1.1024 + break;
1.1025 +#endif
1.1026 + default: /* wrong option */
1.1027 + about();
1.1028 + } /* switch */
1.1029 + } else if (argv[arg][0]=='@') {
1.1030 + #if !defined SC_LIGHT
1.1031 + parserespf(&argv[arg][1],oname,ename,pname,rname,codepage);
1.1032 + #endif
1.1033 + } else if ((ptr=strchr(argv[arg],'='))!=NULL) {
1.1034 + i=(int)(ptr-argv[arg]);
1.1035 + if (i>sNAMEMAX) {
1.1036 + i=sNAMEMAX;
1.1037 + error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */
1.1038 + } /* if */
1.1039 + strlcpy(str,argv[arg],i+1); /* str holds symbol name */
1.1040 + i=atoi(ptr+1);
1.1041 + add_constant(str,i,sGLOBAL,0);
1.1042 + } else {
1.1043 + strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".sp" */
1.1044 + set_extension(str,".sp",FALSE);
1.1045 + insert_sourcefile(str);
1.1046 + /* The output name is the first input name with a different extension,
1.1047 + * but it is stored in a different directory
1.1048 + */
1.1049 + if (strlen(oname)==0) {
1.1050 + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1.1051 + ptr++; /* strip path */
1.1052 + else
1.1053 + ptr=str;
1.1054 + assert(strlen(ptr)<_MAX_PATH);
1.1055 + strcpy(oname,ptr);
1.1056 + } /* if */
1.1057 + set_extension(oname,".asm",TRUE);
1.1058 +#if !defined SC_LIGHT
1.1059 + if (sc_makereport && strlen(rname)==0) {
1.1060 + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1.1061 + ptr++; /* strip path */
1.1062 + else
1.1063 + ptr=str;
1.1064 + assert(strlen(ptr)<_MAX_PATH);
1.1065 + strcpy(rname,ptr);
1.1066 + set_extension(rname,".xml",TRUE);
1.1067 + } /* if */
1.1068 +#endif
1.1069 + } /* if */
1.1070 + } /* for */
1.1071 +}
1.1072 +
1.1073 +#if !defined SC_LIGHT
1.1074 +static void parserespf(char *filename,char *oname,char *ename,char *pname,
1.1075 + char *rname,char *codepage)
1.1076 +{
1.1077 +#define MAX_OPTIONS 100
1.1078 + FILE *fp;
1.1079 + char *string, *ptr, **argv;
1.1080 + int argc;
1.1081 + long size;
1.1082 +
1.1083 + if ((fp=fopen(filename,"r"))==NULL)
1.1084 + error(120,filename); /* error reading input file */
1.1085 + /* load the complete file into memory */
1.1086 + fseek(fp,0L,SEEK_END);
1.1087 + size=ftell(fp);
1.1088 + fseek(fp,0L,SEEK_SET);
1.1089 + assert(size<INT_MAX);
1.1090 + if ((string=(char *)malloc((int)size+1))==NULL)
1.1091 + error(123); /* insufficient memory */
1.1092 + /* fill with zeros; in MS-DOS, fread() may collapse CR/LF pairs to
1.1093 + * a single '\n', so the string size may be smaller than the file
1.1094 + * size. */
1.1095 + memset(string,0,(int)size+1);
1.1096 + fread(string,1,(int)size,fp);
1.1097 + fclose(fp);
1.1098 + /* allocate table for option pointers */
1.1099 + if ((argv=(char **)malloc(MAX_OPTIONS*sizeof(char*)))==NULL)
1.1100 + error(123); /* insufficient memory */
1.1101 + /* fill the options table */
1.1102 + ptr=strtok(string," \t\r\n");
1.1103 + for (argc=1; argc<MAX_OPTIONS && ptr!=NULL; argc++) {
1.1104 + /* note: the routine skips argv[0], for compatibility with main() */
1.1105 + argv[argc]=ptr;
1.1106 + ptr=strtok(NULL," \t\r\n");
1.1107 + } /* for */
1.1108 + if (ptr!=NULL)
1.1109 + error(122,"option table"); /* table overflow */
1.1110 + /* parse the option table */
1.1111 + parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1.1112 + /* free allocated memory */
1.1113 + free(argv);
1.1114 + free(string);
1.1115 +}
1.1116 +#endif
1.1117 +
1.1118 +static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
1.1119 + char *rname,char *codepage)
1.1120 +{
1.1121 + delete_sourcefiletable(); /* make sure it is empty */
1.1122 + *oname='\0';
1.1123 + *ename='\0';
1.1124 + *pname='\0';
1.1125 + *rname='\0';
1.1126 + *codepage='\0';
1.1127 + strcpy(pname,sDEF_PREFIX);
1.1128 +
1.1129 + #if 0 /* needed to test with BoundsChecker for DOS (it does not pass
1.1130 + * through arguments) */
1.1131 + insert_sourcefile("test.p");
1.1132 + strcpy(oname,"test.asm");
1.1133 + #endif
1.1134 +
1.1135 + #if !defined SC_LIGHT
1.1136 + /* first parse a "config" file with default options */
1.1137 + if (argv[0]!=NULL) {
1.1138 + char cfgfile[_MAX_PATH];
1.1139 + char *ext;
1.1140 + strcpy(cfgfile,argv[0]);
1.1141 + if ((ext=strrchr(cfgfile,DIRSEP_CHAR))!=NULL) {
1.1142 + *(ext+1)='\0'; /* strip the program filename */
1.1143 + strcat(cfgfile,"pawn.cfg");
1.1144 + } else {
1.1145 + strcpy(cfgfile,"pawn.cfg");
1.1146 + } /* if */
1.1147 + if (access(cfgfile,4)==0)
1.1148 + parserespf(cfgfile,oname,ename,pname,rname,codepage);
1.1149 + } /* if */
1.1150 + #endif
1.1151 + parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1.1152 + if (get_sourcefile(0)==NULL)
1.1153 + about();
1.1154 +}
1.1155 +
1.1156 +#if defined __BORLANDC__ || defined __WATCOMC__
1.1157 + #pragma argsused
1.1158 +#endif
1.1159 +static void setconfig(char *root)
1.1160 +{
1.1161 + #if defined macintosh
1.1162 + insert_path(":include:");
1.1163 + #else
1.1164 + char path[_MAX_PATH];
1.1165 + char *ptr,*base;
1.1166 + int len;
1.1167 +
1.1168 + /* add the default "include" directory */
1.1169 + #if defined __WIN32__ || defined _WIN32
1.1170 + GetModuleFileNameA(NULL,path,_MAX_PATH);
1.1171 + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
1.1172 + /* see www.autopackage.org for the BinReloc module */
1.1173 + br_init_lib(NULL);
1.1174 + ptr=br_find_exe("spcomp");
1.1175 + strlcpy(path,ptr,sizeof path);
1.1176 + free(ptr);
1.1177 + #else
1.1178 + if (root!=NULL)
1.1179 + strlcpy(path,root,sizeof path); /* path + filename (hopefully) */
1.1180 + #endif
1.1181 + #if defined __MSDOS__
1.1182 + /* strip the options (appended to the path + filename) */
1.1183 + if ((ptr=strpbrk(path," \t/"))!=NULL)
1.1184 + *ptr='\0';
1.1185 + #endif
1.1186 + /* terminate just behind last \ or : */
1.1187 + if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL || (ptr=strchr(path,':'))!=NULL) {
1.1188 + /* If there is no "\" or ":", the string probably does not contain the
1.1189 + * path; so we just don't add it to the list in that case
1.1190 + */
1.1191 + *(ptr+1)='\0';
1.1192 + base=ptr;
1.1193 + strcat(path,"include");
1.1194 + len=strlen(path);
1.1195 + path[len]=DIRSEP_CHAR;
1.1196 + path[len+1]='\0';
1.1197 + /* see if it exists */
1.1198 + if (access(path,0)!=0 && *base==DIRSEP_CHAR) {
1.1199 + /* There is no "include" directory below the directory where the compiler
1.1200 + * is found. This typically means that the compiler is in a "bin" sub-directory
1.1201 + * and the "include" is below the *parent*. So find the parent...
1.1202 + */
1.1203 + *base='\0';
1.1204 + if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL) {
1.1205 + *(ptr+1)='\0';
1.1206 + strcat(path,"include");
1.1207 + len=strlen(path);
1.1208 + path[len]=DIRSEP_CHAR;
1.1209 + path[len+1]='\0';
1.1210 + } else {
1.1211 + *base=DIRSEP_CHAR;
1.1212 + } /* if */
1.1213 + } /* if */
1.1214 + insert_path(path);
1.1215 + /* same for the codepage root */
1.1216 + #if !defined NO_CODEPAGE
1.1217 + *ptr='\0';
1.1218 + if (!cp_path(path,"codepage"))
1.1219 + error(129,path); /* codepage path */
1.1220 + #endif
1.1221 + /* also copy the root path (for the XML documentation) */
1.1222 + #if !defined SC_LIGHT
1.1223 + *ptr='\0';
1.1224 + strcpy(sc_rootpath,path);
1.1225 + #endif
1.1226 + } /* if */
1.1227 + #endif /* macintosh */
1.1228 +}
1.1229 +
1.1230 +static void setcaption(void)
1.1231 +{
1.1232 + pc_printf("SourcePawn Compiler " SVN_FULL_VERSION "\n");
1.1233 + pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2008 AlliedModders, LLC\n\n");
1.1234 +}
1.1235 +
1.1236 +static void about(void)
1.1237 +{
1.1238 + if (strlen(errfname)==0) {
1.1239 + setcaption();
1.1240 + pc_printf("Usage: spcomp <filename> [filename...] [options]\n\n");
1.1241 + pc_printf("Options:\n");
1.1242 + pc_printf(" -A<num> alignment in bytes of the data segment and the stack\n");
1.1243 + pc_printf(" -a output assembler code\n");
1.1244 +#if 0 /* not toggleable in SourceMod */
1.1245 + pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-');
1.1246 +#endif
1.1247 + pc_printf(" -c<name> codepage name or number; e.g. 1252 for Windows Latin-1\n");
1.1248 +#if defined dos_setdrive
1.1249 + pc_printf(" -Dpath active directory path\n");
1.1250 +#endif
1.1251 +#if 0 /* not used for SourceMod */
1.1252 + pc_printf(" -d<num> debugging level (default=-d%d)\n",sc_debug);
1.1253 + pc_printf(" 0 no symbolic information, no run-time checks\n");
1.1254 + pc_printf(" 1 run-time checks, no symbolic information\n");
1.1255 + pc_printf(" 2 full debug information and dynamic checking\n");
1.1256 + pc_printf(" 3 same as -d2, but implies -O0\n");
1.1257 +#endif
1.1258 + pc_printf(" -e<name> set name of error file (quiet compile)\n");
1.1259 +#if defined __WIN32__ || defined _WIN32 || defined _Windows
1.1260 + pc_printf(" -H<hwnd> window handle to send a notification message on finish\n");
1.1261 +#endif
1.1262 + pc_printf(" -i<name> path for include files\n");
1.1263 + pc_printf(" -l create list file (preprocess only)\n");
1.1264 + pc_printf(" -o<name> set base name of (P-code) output file\n");
1.1265 + pc_printf(" -O<num> optimization level (default=-O%d)\n",pc_optimize);
1.1266 + pc_printf(" 0 no optimization\n");
1.1267 +#if 0 /* not used for SourceMod */
1.1268 + pc_printf(" 1 JIT-compatible optimizations only\n");
1.1269 +#endif
1.1270 + pc_printf(" 2 full optimizations\n");
1.1271 + pc_printf(" -p<name> set name of \"prefix\" file\n");
1.1272 +#if !defined SC_LIGHT
1.1273 + pc_printf(" -r[name] write cross reference report to console or to specified file\n");
1.1274 +#endif
1.1275 + pc_printf(" -S<num> stack/heap size in cells (default=%d)\n",(int)pc_stksize);
1.1276 + pc_printf(" -s<num> skip lines from the input file\n");
1.1277 + pc_printf(" -t<num> TAB indent size (in character positions, default=%d)\n",sc_tabsize);
1.1278 + pc_printf(" -v<num> verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity);
1.1279 + pc_printf(" -w<num> disable a specific warning by its number\n");
1.1280 + pc_printf(" -X<num> abstract machine size limit in bytes\n");
1.1281 + pc_printf(" -XD<num> abstract machine data/stack size limit in bytes\n");
1.1282 + pc_printf(" -\\ use '\\' for escape characters\n");
1.1283 + pc_printf(" -^ use '^' for escape characters\n");
1.1284 + pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-');
1.1285 +#if 0 /* not allowed in SourceMod */
1.1286 + pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+');
1.1287 +#endif
1.1288 + pc_printf(" sym=val define constant \"sym\" with value \"val\"\n");
1.1289 + pc_printf(" sym= define constant \"sym\" with value 0\n");
1.1290 +#if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__
1.1291 + pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n");
1.1292 + pc_printf("equivalent.\n");
1.1293 +#endif
1.1294 + pc_printf("\nOptions with a value may optionally separate the value from the option letter\n");
1.1295 + pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n");
1.1296 + pc_printf("and \"-d:0\" are all equivalent.\n");
1.1297 + } /* if */
1.1298 + norun = 1;
1.1299 + longjmp(errbuf,3); /* user abort */
1.1300 +}
1.1301 +
1.1302 +static void setconstants(void)
1.1303 +{
1.1304 + int debug;
1.1305 +
1.1306 + assert(sc_status==statIDLE);
1.1307 + append_constval(&tagname_tab,"_",0,0);/* "untagged" */
1.1308 + append_constval(&tagname_tab,"bool",1,0);
1.1309 +
1.1310 + pc_anytag = pc_addtag("any");
1.1311 + pc_functag = pc_addfunctag("Function");
1.1312 + pc_tag_string = pc_addtag("String");
1.1313 +
1.1314 + add_constant("true",1,sGLOBAL,1); /* boolean flags */
1.1315 + add_constant("false",0,sGLOBAL,1);
1.1316 + add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */
1.1317 + #if PAWN_CELL_SIZE==16
1.1318 + add_constant("cellbits",16,sGLOBAL,0);
1.1319 + #if defined _I16_MAX
1.1320 + add_constant("cellmax",_I16_MAX,sGLOBAL,0);
1.1321 + add_constant("cellmin",_I16_MIN,sGLOBAL,0);
1.1322 + #else
1.1323 + add_constant("cellmax",SHRT_MAX,sGLOBAL,0);
1.1324 + add_constant("cellmin",SHRT_MIN,sGLOBAL,0);
1.1325 + #endif
1.1326 + #elif PAWN_CELL_SIZE==32
1.1327 + add_constant("cellbits",32,sGLOBAL,0);
1.1328 + #if defined _I32_MAX
1.1329 + add_constant("cellmax",_I32_MAX,sGLOBAL,0);
1.1330 + add_constant("cellmin",_I32_MIN,sGLOBAL,0);
1.1331 + #else
1.1332 + add_constant("cellmax",LONG_MAX,sGLOBAL,0);
1.1333 + add_constant("cellmin",LONG_MIN,sGLOBAL,0);
1.1334 + #endif
1.1335 + #elif PAWN_CELL_SIZE==64
1.1336 + #if !defined _I64_MIN
1.1337 + #define _I64_MIN (-9223372036854775807ULL - 1)
1.1338 + #define _I64_MAX 9223372036854775807ULL
1.1339 + #endif
1.1340 + add_constant("cellbits",64,sGLOBAL,0);
1.1341 + add_constant("cellmax",_I64_MAX,sGLOBAL,0);
1.1342 + add_constant("cellmin",_I64_MIN,sGLOBAL,0);
1.1343 + #else
1.1344 + #error Unsupported cell size
1.1345 + #endif
1.1346 + add_constant("charbits",sCHARBITS,sGLOBAL,0);
1.1347 + add_constant("charmin",0,sGLOBAL,0);
1.1348 + add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0);
1.1349 + add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0);
1.1350 +
1.1351 + add_constant("__Pawn",VERSION_INT,sGLOBAL,0);
1.1352 +
1.1353 + debug=0;
1.1354 + if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC))
1.1355 + debug=2;
1.1356 + else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS)
1.1357 + debug=1;
1.1358 + add_constant("debug",debug,sGLOBAL,0);
1.1359 +
1.1360 + append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */
1.1361 +}
1.1362 +
1.1363 +static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst)
1.1364 +{
1.1365 + int tok,err;
1.1366 + cell val;
1.1367 + char *str;
1.1368 +
1.1369 + assert(fconst!=NULL);
1.1370 + assert(fstock!=NULL);
1.1371 + assert(fstatic!=NULL);
1.1372 + assert(fpublic!=NULL);
1.1373 + *fconst=FALSE;
1.1374 + *fstock=FALSE;
1.1375 + *fstatic=FALSE;
1.1376 + *fpublic=FALSE;
1.1377 + switch (initialtok) {
1.1378 + case tCONST:
1.1379 + *fconst=TRUE;
1.1380 + break;
1.1381 + case tSTOCK:
1.1382 + *fstock=TRUE;
1.1383 + break;
1.1384 + case tSTATIC:
1.1385 + *fstatic=TRUE;
1.1386 + break;
1.1387 + case tPUBLIC:
1.1388 + *fpublic=TRUE;
1.1389 + break;
1.1390 + } /* switch */
1.1391 +
1.1392 + err=0;
1.1393 + do {
1.1394 + tok=lex(&val,&str); /* read in (new) token */
1.1395 + switch (tok) {
1.1396 + case tCONST:
1.1397 + if (*fconst)
1.1398 + err=42; /* invalid combination of class specifiers */
1.1399 + *fconst=TRUE;
1.1400 + break;
1.1401 + case tSTOCK:
1.1402 + if (*fstock)
1.1403 + err=42; /* invalid combination of class specifiers */
1.1404 + *fstock=TRUE;
1.1405 + break;
1.1406 + case tSTATIC:
1.1407 + if (*fstatic)
1.1408 + err=42; /* invalid combination of class specifiers */
1.1409 + *fstatic=TRUE;
1.1410 + break;
1.1411 + case tPUBLIC:
1.1412 + if (*fpublic)
1.1413 + err=42; /* invalid combination of class specifiers */
1.1414 + *fpublic=TRUE;
1.1415 + break;
1.1416 + default:
1.1417 + lexpush();
1.1418 + tok=0; /* force break out of loop */
1.1419 + } /* switch */
1.1420 + } while (tok && err==0);
1.1421 +
1.1422 + /* extra checks */
1.1423 + if (*fstatic && *fpublic) {
1.1424 + err=42; /* invalid combination of class specifiers */
1.1425 + *fstatic=*fpublic=FALSE;
1.1426 + } /* if */
1.1427 +
1.1428 + if (err)
1.1429 + error(err);
1.1430 + return err==0;
1.1431 +}
1.1432 +
1.1433 +/* parse - process all input text
1.1434 + *
1.1435 + * At this level, only static declarations and function definitions are legal.
1.1436 + */
1.1437 +static void parse(void)
1.1438 +{
1.1439 + int tok,fconst,fstock,fstatic,fpublic;
1.1440 + cell val;
1.1441 + char *str;
1.1442 +
1.1443 + while (freading){
1.1444 + /* first try whether a declaration possibly is native or public */
1.1445 + tok=lex(&val,&str); /* read in (new) token */
1.1446 + switch (tok) {
1.1447 + case 0:
1.1448 + /* ignore zero's */
1.1449 + break;
1.1450 + case tNEW:
1.1451 + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst))
1.1452 + declglb(NULL,0,fpublic,fstatic,fstock,fconst);
1.1453 + break;
1.1454 + case tSTATIC:
1.1455 + /* This can be a static function or a static global variable; we know
1.1456 + * which of the two as soon as we have parsed up to the point where an
1.1457 + * opening paranthesis of a function would be expected. To back out after
1.1458 + * deciding it was a declaration of a static variable after all, we have
1.1459 + * to store the symbol name and tag.
1.1460 + */
1.1461 + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1.1462 + assert(!fpublic);
1.1463 + declfuncvar(fpublic,fstatic,fstock,fconst);
1.1464 + } /* if */
1.1465 + break;
1.1466 + case tCONST:
1.1467 + decl_const(sGLOBAL);
1.1468 + break;
1.1469 + case tENUM:
1.1470 + decl_enum(sGLOBAL);
1.1471 + break;
1.1472 + case tFUNCENUM:
1.1473 + dofuncenum(TRUE);
1.1474 + break;
1.1475 + case tFUNCTAG:
1.1476 + dofuncenum(FALSE);
1.1477 + break;
1.1478 + case tSTRUCT:
1.1479 + declstruct();
1.1480 + break;
1.1481 + case tPUBLIC:
1.1482 + /* This can be a public function or a public variable; see the comment
1.1483 + * above (for static functions/variables) for details.
1.1484 + */
1.1485 + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1.1486 + assert(!fstatic);
1.1487 + declfuncvar(fpublic,fstatic,fstock,fconst);
1.1488 + } /* if */
1.1489 + break;
1.1490 + case tSTOCK:
1.1491 + /* This can be a stock function or a stock *global*) variable; see the
1.1492 + * comment above (for static functions/variables) for details.
1.1493 + */
1.1494 + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1.1495 + assert(fstock);
1.1496 + declfuncvar(fpublic,fstatic,fstock,fconst);
1.1497 + } /* if */
1.1498 + break;
1.1499 + case tLABEL:
1.1500 + case tSYMBOL:
1.1501 + case tOPERATOR:
1.1502 + lexpush();
1.1503 + if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) {
1.1504 + error(10); /* illegal function or declaration */
1.1505 + lexclr(TRUE); /* drop the rest of the line */
1.1506 + litidx=0; /* drop the literal queue too */
1.1507 + } /* if */
1.1508 + break;
1.1509 + case tNATIVE:
1.1510 + funcstub(TRUE); /* create a dummy function */
1.1511 + break;
1.1512 + case tFORWARD:
1.1513 + funcstub(FALSE);
1.1514 + break;
1.1515 + case '}':
1.1516 + error(54); /* unmatched closing brace */
1.1517 + break;
1.1518 + case '{':
1.1519 + error(55); /* start of function body without function header */
1.1520 + break;
1.1521 + default:
1.1522 + if (freading) {
1.1523 + error(10); /* illegal function or declaration */
1.1524 + lexclr(TRUE); /* drop the rest of the line */
1.1525 + litidx=0; /* drop any literal arrays (strings) */
1.1526 + } /* if */
1.1527 + } /* switch */
1.1528 + } /* while */
1.1529 +}
1.1530 +
1.1531 +/* dumplits
1.1532 + *
1.1533 + * Dump the literal pool (strings etc.)
1.1534 + *
1.1535 + * Global references: litidx (referred to only)
1.1536 + */
1.1537 +static void dumplits(void)
1.1538 +{
1.1539 + int j,k;
1.1540 +
1.1541 + if (sc_status==statSKIP)
1.1542 + return;
1.1543 +
1.1544 + k=0;
1.1545 + while (k<litidx){
1.1546 + /* should be in the data segment */
1.1547 + assert(curseg==2);
1.1548 + defstorage();
1.1549 + j=16; /* 16 values per line */
1.1550 + while (j && k<litidx){
1.1551 + outval(litq[k], FALSE);
1.1552 + stgwrite(" ");
1.1553 + k++;
1.1554 + j--;
1.1555 + if (j==0 || k>=litidx)
1.1556 + stgwrite("\n"); /* force a newline after 10 dumps */
1.1557 + /* Note: stgwrite() buffers a line until it is complete. It recognizes
1.1558 + * the end of line as a sequence of "\n\0", so something like "\n\t"
1.1559 + * so should not be passed to stgwrite().
1.1560 + */
1.1561 + } /* while */
1.1562 + } /* while */
1.1563 +}
1.1564 +
1.1565 +/* dumpzero
1.1566 + *
1.1567 + * Dump zero's for default initial values
1.1568 + */
1.1569 +static void dumpzero(int count)
1.1570 +{
1.1571 + int i;
1.1572 +
1.1573 + if (sc_status==statSKIP || count<=0)
1.1574 + return;
1.1575 + assert(curseg==2);
1.1576 + defstorage();
1.1577 + i=0;
1.1578 + while (count-- > 0) {
1.1579 + outval(0, FALSE);
1.1580 + i=(i+1) % 16;
1.1581 + stgwrite((i==0 || count==0) ? "\n" : " ");
1.1582 + if (i==0 && count>0)
1.1583 + defstorage();
1.1584 + } /* while */
1.1585 +}
1.1586 +
1.1587 +static void aligndata(int numbytes)
1.1588 +{
1.1589 + assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of
1.1590 + * the cell size */
1.1591 + assert(numbytes!=0);
1.1592 +
1.1593 + if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) {
1.1594 + while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0)
1.1595 + litadd(0);
1.1596 + } /* if */
1.1597 +
1.1598 +}
1.1599 +
1.1600 +#if !defined SC_LIGHT
1.1601 +/* sc_attachdocumentation()
1.1602 + * appends documentation comments to the passed-in symbol, or to a global
1.1603 + * string if "sym" is NULL.
1.1604 + */
1.1605 +void sc_attachdocumentation(symbol *sym)
1.1606 +{
1.1607 + int line;
1.1608 + size_t length;
1.1609 + char *str,*doc;
1.1610 +
1.1611 + if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) {
1.1612 + /* just clear the entire table */
1.1613 + delete_docstringtable();
1.1614 + return;
1.1615 + } /* if */
1.1616 + /* in the case of state functions, multiple documentation sections may
1.1617 + * appear; we should concatenate these
1.1618 + * (with forward declarations, this is also already the case, so the assertion
1.1619 + * below is invalid)
1.1620 + */
1.1621 + // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL);
1.1622 +
1.1623 + /* first check the size */
1.1624 + length=0;
1.1625 + for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
1.1626 + if (length>0)
1.1627 + length++; /* count 1 extra for a separating space */
1.1628 + length+=strlen(str);
1.1629 + } /* for */
1.1630 + if (sym==NULL && sc_documentation!=NULL) {
1.1631 + length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
1.1632 + assert(length>strlen(sc_documentation));
1.1633 + } /* if */
1.1634 +
1.1635 + if (length>0) {
1.1636 + /* allocate memory for the documentation */
1.1637 + if (sym!=NULL && sym->documentation!=NULL)
1.1638 + length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
1.1639 + doc=(char*)malloc((length+1)*sizeof(char));
1.1640 + if (doc!=NULL) {
1.1641 + /* initialize string or concatenate */
1.1642 + if (sym==NULL && sc_documentation!=NULL) {
1.1643 + strcpy(doc,sc_documentation);
1.1644 + strcat(doc,"<p/>");
1.1645 + } else if (sym!=NULL && sym->documentation!=NULL) {
1.1646 + strcpy(doc,sym->documentation);
1.1647 + strcat(doc,"<p/>");
1.1648 + free(sym->documentation);
1.1649 + sym->documentation=NULL;
1.1650 + } else {
1.1651 + doc[0]='\0';
1.1652 + } /* if */
1.1653 + /* collect all documentation */
1.1654 + while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
1.1655 + if (doc[0]!='\0')
1.1656 + strcat(doc," ");
1.1657 + strcat(doc,str);
1.1658 + delete_docstring(0);
1.1659 + } /* while */
1.1660 + if (str!=NULL) {
1.1661 + /* also delete the separator */
1.1662 + assert(*str==sDOCSEP);
1.1663 + delete_docstring(0);
1.1664 + } /* if */
1.1665 + if (sym!=NULL) {
1.1666 + assert(sym->documentation==NULL);
1.1667 + sym->documentation=doc;
1.1668 + } else {
1.1669 + if (sc_documentation!=NULL)
1.1670 + free(sc_documentation);
1.1671 + sc_documentation=doc;
1.1672 + } /* if */
1.1673 + } /* if */
1.1674 + } else {
1.1675 + /* delete an empty separator, if present */
1.1676 + if ((str=get_docstring(0))!=NULL && *str==sDOCSEP)
1.1677 + delete_docstring(0);
1.1678 + } /* if */
1.1679 +}
1.1680 +
1.1681 +static void insert_docstring_separator(void)
1.1682 +{
1.1683 + char sep[2]={sDOCSEP,'\0'};
1.1684 + insert_docstring(sep);
1.1685 +}
1.1686 +#else
1.1687 + #define sc_attachdocumentation(s) (void)(s)
1.1688 + #define insert_docstring_separator()
1.1689 +#endif
1.1690 +
1.1691 +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst)
1.1692 +{
1.1693 + char name[sNAMEMAX+11];
1.1694 + int tok,tag=0;
1.1695 + char *str;
1.1696 + cell val;
1.1697 + int invalidfunc;
1.1698 + pstruct_t *pstruct = NULL;
1.1699 +
1.1700 + tok=lex(&val,&str);
1.1701 + if (tok == tLABEL) {
1.1702 + pstruct=pstructs_find(str);
1.1703 + tag=pc_addtag(str);
1.1704 + } else {
1.1705 + lexpush();
1.1706 + }
1.1707 + tok=lex(&val,&str);
1.1708 + /* if we arrived here, this may not be a declaration of a native function
1.1709 + * or variable
1.1710 + */
1.1711 + if (tok==tNATIVE) {
1.1712 + error(42); /* invalid combination of class specifiers */
1.1713 + return;
1.1714 + } /* if */
1.1715 +
1.1716 + if (tok!=tSYMBOL && tok!=tOPERATOR) {
1.1717 + lexpush();
1.1718 + needtoken(tSYMBOL);
1.1719 + lexclr(TRUE); /* drop the rest of the line */
1.1720 + litidx=0; /* drop the literal queue too */
1.1721 + return;
1.1722 + } /* if */
1.1723 + if (tok==tOPERATOR) {
1.1724 + lexpush(); /* push "operator" keyword back (for later analysis) */
1.1725 + if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) {
1.1726 + error(10); /* illegal function or declaration */
1.1727 + lexclr(TRUE); /* drop the rest of the line */
1.1728 + litidx=0; /* drop the literal queue too */
1.1729 + } /* if */
1.1730 + } else {
1.1731 + /* so tok is tSYMBOL */
1.1732 + assert(strlen(str)<=sNAMEMAX);
1.1733 + strcpy(name,str);
1.1734 + /* only variables can be "const" or both "public" and "stock" */
1.1735 + invalidfunc= fconst || (fpublic && fstock);
1.1736 + if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) {
1.1737 + /* if not a function, try a global variable */
1.1738 + if (pstruct) {
1.1739 + declstructvar(name,fpublic,pstruct);
1.1740 + } else {
1.1741 + declglb(name,tag,fpublic,fstatic,fstock,fconst);
1.1742 + }
1.1743 + } /* if */
1.1744 + } /* if */
1.1745 +}
1.1746 +
1.1747 +/* declstruct - declare global struct symbols
1.1748 + *
1.1749 + * global references: glb_declared (altered)
1.1750 + */
1.1751 +static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
1.1752 +{
1.1753 + char name[sNAMEMAX+1];
1.1754 + int tok,i;
1.1755 + cell val;
1.1756 + char *str;
1.1757 + int cur_litidx = 0;
1.1758 + cell *values, *found;
1.1759 + int usage;
1.1760 + symbol *mysym,*sym;
1.1761 +
1.1762 + strcpy(name, firstname);
1.1763 +
1.1764 + values = (cell *)malloc(pstruct->argcount * sizeof(cell));
1.1765 + found = (cell *)malloc(pstruct->argcount * sizeof(cell));
1.1766 +
1.1767 + memset(found, 0, sizeof(cell) * pstruct->argcount);
1.1768 +
1.1769 + //:TODO: Make this work with stock
1.1770 +
1.1771 + /**
1.1772 + * Lastly, very lastly, we will insert a copy of this variable.
1.1773 + * This is soley to expose the pubvar.
1.1774 + */
1.1775 + usage = uREAD|uCONST|uSTRUCT;
1.1776 + if (fpublic)
1.1777 + {
1.1778 + usage |= uPUBLIC;
1.1779 + }
1.1780 + mysym = NULL;
1.1781 + for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1.1782 + {
1.1783 + if (strcmp(name, sym->name) == 0)
1.1784 + {
1.1785 + if ((sym->usage & uSTRUCT) && sym->vclass == sGLOBAL)
1.1786 + {
1.1787 + if (sym->usage & uDEFINE)
1.1788 + {
1.1789 + error(21, name);
1.1790 + } else {
1.1791 + if (sym->usage & uPUBLIC && !fpublic)
1.1792 + {
1.1793 + error(42);
1.1794 + }
1.1795 + }
1.1796 + } else {
1.1797 + error(21, name);
1.1798 + }
1.1799 + mysym = sym;
1.1800 + break;
1.1801 + }
1.1802 + }
1.1803 + if (!mysym)
1.1804 + {
1.1805 + mysym=addsym(name, 0, iVARIABLE, sGLOBAL, pc_addtag(pstruct->name), usage);
1.1806 + }
1.1807 +
1.1808 + if (!matchtoken('='))
1.1809 + {
1.1810 + matchtoken(';');
1.1811 + /* Mark it as undefined instead */
1.1812 + mysym->usage = uSTOCK|uSTRUCT;
1.1813 + return;
1.1814 + } else {
1.1815 + mysym->usage = usage;
1.1816 + }
1.1817 + needtoken('{');
1.1818 + do
1.1819 + {
1.1820 + structarg_t *arg;
1.1821 + /* Detect early exit */
1.1822 + if (matchtoken('}'))
1.1823 + {
1.1824 + lexpush();
1.1825 + break;
1.1826 + }
1.1827 + tok=lex(&val,&str);
1.1828 + if (tok != tSYMBOL)
1.1829 + {
1.1830 + error(1, "-identifier-", str);
1.1831 + continue;
1.1832 + }
1.1833 + arg=pstructs_getarg(pstruct,str);
1.1834 + if (arg == NULL)
1.1835 + {
1.1836 + error(96, str, sym->name);
1.1837 + }
1.1838 + needtoken('=');
1.1839 + cur_litidx = litidx;
1.1840 + tok=lex(&val,&str);
1.1841 + if (!arg)
1.1842 + {
1.1843 + continue;
1.1844 + }
1.1845 + if (tok == tSTRING)
1.1846 + {
1.1847 + assert(litidx != 0);
1.1848 + if (arg->dimcount != 1)
1.1849 + {
1.1850 + error(48);
1.1851 + } else if (arg->tag != pc_tag_string) {
1.1852 + error(213);
1.1853 + }
1.1854 + values[arg->index] = glb_declared * sizeof(cell);
1.1855 + glb_declared += (litidx-cur_litidx);
1.1856 + found[arg->index] = 1;
1.1857 + } else if (tok == tNUMBER || tok == tRATIONAL) {
1.1858 + /* eat optional 'f' */
1.1859 + matchtoken('f');
1.1860 + if (arg->ident != iVARIABLE && arg->ident != iREFERENCE)
1.1861 + {
1.1862 + error(23);
1.1863 + } else {
1.1864 + if (arg->tag == pc_addtag("Float") && tok == tNUMBER ||
1.1865 + arg->tag == 0 && tok == tRATIONAL)
1.1866 + {
1.1867 + error(213);
1.1868 + }
1.1869 + if (arg->ident == iVARIABLE)
1.1870 + {
1.1871 + values[arg->index] = val;
1.1872 + } else if (arg->ident == iREFERENCE) {
1.1873 + values[arg->index] = glb_declared * sizeof(cell);
1.1874 + glb_declared += 1;
1.1875 + litadd(val);
1.1876 + cur_litidx = litidx;
1.1877 + }
1.1878 + found[arg->index] = 1;
1.1879 + }
1.1880 + } else if (tok == tSYMBOL) {
1.1881 + for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1.1882 + {
1.1883 + if (sym->vclass != sGLOBAL)
1.1884 + {
1.1885 + continue;
1.1886 + }
1.1887 + if (strcmp(sym->name, str) == 0)
1.1888 + {
1.1889 + if (arg->ident == iREFERENCE && sym->ident != iVARIABLE)
1.1890 + {
1.1891 + error(97, str);
1.1892 + } else if (arg->ident == iARRAY) {
1.1893 + if (sym->ident != iARRAY)
1.1894 + {
1.1895 + error(97, str);
1.1896 + } else {
1.1897 + /* :TODO: We should check dimension sizes here... */
1.1898 + }
1.1899 + } else if (arg->ident == iREFARRAY) {
1.1900 + if (sym->ident != iARRAY)
1.1901 + {
1.1902 + error(97, str);
1.1903 + }
1.1904 + /* :TODO: Check dimension sizes! */
1.1905 + } else {
1.1906 + error(97, str);
1.1907 + }
1.1908 + if (sym->tag != arg->tag)
1.1909 + {
1.1910 + error(213);
1.1911 + }
1.1912 + sym->usage |= uREAD;
1.1913 + values[arg->index] = sym->addr;
1.1914 + found[arg->index] = 1;
1.1915 + refer_symbol(sym, mysym);
1.1916 + break;
1.1917 + }
1.1918 + }
1.1919 + if (!sym)
1.1920 + {
1.1921 + error(17, str);
1.1922 + }
1.1923 + } else {
1.1924 + error(1, "-identifier-", str);
1.1925 + }
1.1926 + } while (matchtoken(','));
1.1927 + needtoken('}');
1.1928 + matchtoken(';'); /* eat up optional semicolon */
1.1929 +
1.1930 + for (i=0; i<pstruct->argcount; i++)
1.1931 + {
1.1932 + if (!found[i])
1.1933 + {
1.1934 + structarg_t *arg = pstruct->args[i];
1.1935 + if (arg->ident == iREFARRAY)
1.1936 + {
1.1937 + values[arg->index] = glb_declared * sizeof(cell);
1.1938 + glb_declared += 1;
1.1939 + litadd(0);
1.1940 + cur_litidx = litidx;
1.1941 + } else if (arg->ident == iVARIABLE) {
1.1942 + values[arg->index] = 0;
1.1943 + } else {
1.1944 + /* :TODO: broken for iARRAY! (unused tho) */
1.1945 + }
1.1946 + }
1.1947 + }
1.1948 +
1.1949 + mysym->addr = glb_declared * sizeof(cell);
1.1950 + glb_declared += pstruct->argcount;
1.1951 +
1.1952 + for (i=0; i<pstruct->argcount; i++)
1.1953 + {
1.1954 + litadd(values[i]);
1.1955 + }
1.1956 +
1.1957 + begdseg();
1.1958 + dumplits();
1.1959 + litidx=0;
1.1960 +
1.1961 + free(found);
1.1962 + free(values);
1.1963 +}
1.1964 +
1.1965 +/* declglb - declare global symbols
1.1966 + *
1.1967 + * Declare a static (global) variable. Global variables are stored in
1.1968 + * the DATA segment.
1.1969 + *
1.1970 + * global references: glb_declared (altered)
1.1971 + */
1.1972 +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst)
1.1973 +{
1.1974 + int ident,tag,ispublic;
1.1975 + int idxtag[sDIMEN_MAX];
1.1976 + char name[sNAMEMAX+1];
1.1977 + cell val,size,cidx;
1.1978 + ucell address;
1.1979 + int glb_incr;
1.1980 + char *str;
1.1981 + int dim[sDIMEN_MAX];
1.1982 + int numdim;
1.1983 + int slength=0;
1.1984 + short filenum;
1.1985 + symbol *sym;
1.1986 + constvalue *enumroot;
1.1987 + #if !defined NDEBUG
1.1988 + cell glbdecl=0;
1.1989 + #endif
1.1990 +
1.1991 + assert(!fpublic || !fstatic); /* may not both be set */
1.1992 + insert_docstring_separator(); /* see comment in newfunc() */
1.1993 + filenum=fcurrent; /* save file number at the start of the declaration */
1.1994 + do {
1.1995 + size=1; /* single size (no array) */
1.1996 + numdim=0; /* no dimensions */
1.1997 + ident=iVARIABLE;
1.1998 + if (firstname!=NULL) {
1.1999 + assert(strlen(firstname)<=sNAMEMAX);
1.2000 + strcpy(name,firstname); /* save symbol name */
1.2001 + tag=firsttag;
1.2002 + firstname=NULL;
1.2003 + } else {
1.2004 + tag=pc_addtag(NULL);
1.2005 + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
1.2006 + error(20,str); /* invalid symbol name */
1.2007 + assert(strlen(str)<=sNAMEMAX);
1.2008 + strcpy(name,str); /* save symbol name */
1.2009 + } /* if */
1.2010 + ispublic=fpublic;
1.2011 + if (name[0]==PUBLIC_CHAR) {
1.2012 + ispublic=TRUE; /* implicitly public variable */
1.2013 + assert(!fstatic);
1.2014 + } /* if */
1.2015 + while (matchtoken('[')) {
1.2016 + ident=iARRAY;
1.2017 + if (numdim == sDIMEN_MAX) {
1.2018 + error(53); /* exceeding maximum number of dimensions */
1.2019 + return;
1.2020 + } /* if */
1.2021 + size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */
1.2022 + #if INT_MAX < LONG_MAX
1.2023 + if (size > INT_MAX)
1.2024 + error(125); /* overflow, exceeding capacity */
1.2025 + #endif
1.2026 +#if 0 /* We don't actually care */
1.2027 + if (ispublic)
1.2028 + error(56,name); /* arrays cannot be public */
1.2029 +#endif
1.2030 + dim[numdim++]=(int)size;
1.2031 + } /* while */
1.2032 + if (ident == iARRAY && tag == pc_tag_string && dim[numdim-1]) {
1.2033 + slength=dim[numdim-1];
1.2034 + dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
1.2035 + }
1.2036 + assert(sc_curstates==0);
1.2037 + sc_curstates=getstates(name);
1.2038 + if (sc_curstates<0) {
1.2039 + error(85,name); /* empty state list on declaration */
1.2040 + sc_curstates=0;
1.2041 + } else if (sc_curstates>0 && ispublic) {
1.2042 + error(88,name); /* public variables may not have states */
1.2043 + sc_curstates=0;
1.2044 + } /* if */
1.2045 + sym=findconst(name,NULL);
1.2046 + if (sym==NULL) {
1.2047 + sym=findglb(name,sSTATEVAR);
1.2048 + /* if a global variable without states is found and this declaration has
1.2049 + * states, the declaration is okay
1.2050 + */
1.2051 + if (sym!=NULL && sym->states==NULL && sc_curstates>0)
1.2052 + sym=NULL; /* set to NULL, we found the global variable */
1.2053 + if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL)
1.2054 + error(233,name); /* state variable shadows a global variable */
1.2055 + } /* if */
1.2056 + /* we have either:
1.2057 + * a) not found a matching variable (or rejected it, because it was a shadow)
1.2058 + * b) found a global variable and we were looking for that global variable
1.2059 + * c) found a state variable in the automaton that we were looking for
1.2060 + */
1.2061 + assert(sym==NULL
1.2062 + || sym->states==NULL && sc_curstates==0
1.2063 + || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates);
1.2064 + /* a state variable may only have a single id in its list (so either this
1.2065 + * variable has no states, or it has a single list)
1.2066 + */
1.2067 + assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL);
1.2068 + /* it is okay for the (global) variable to exist, as long as it belongs to
1.2069 + * a different automaton
1.2070 + */
1.2071 + if (sym!=NULL && (sym->usage & uDEFINE)!=0)
1.2072 + error(21,name); /* symbol already defined */
1.2073 + /* if this variable is never used (which can be detected only in the
1.2074 + * second stage), shut off code generation
1.2075 + */
1.2076 + cidx=0; /* only to avoid a compiler warning */
1.2077 + if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) {
1.2078 + sc_status=statSKIP;
1.2079 + cidx=code_idx;
1.2080 + #if !defined NDEBUG
1.2081 + glbdecl=glb_declared;
1.2082 + #endif
1.2083 + } /* if */
1.2084 + begdseg(); /* real (initialized) data in data segment */
1.2085 + assert(litidx==0); /* literal queue should be empty */
1.2086 + if (sc_alignnext) {
1.2087 + litidx=0;
1.2088 + aligndata(sc_dataalign);
1.2089 + dumplits(); /* dump the literal queue */
1.2090 + sc_alignnext=FALSE;
1.2091 + litidx=0; /* global initial data is dumped, so restart at zero */
1.2092 + } /* if */
1.2093 + assert(litidx==0); /* literal queue should be empty (again) */
1.2094 + initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */
1.2095 + if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
1.2096 + slength = glbstringread;
1.2097 + }
1.2098 + assert(size>=litidx);
1.2099 + if (numdim==1)
1.2100 + dim[0]=(int)size;
1.2101 + /* before dumping the initial values (or zeros) check whether this variable
1.2102 + * overlaps another
1.2103 + */
1.2104 + if (sc_curstates>0) {
1.2105 + unsigned char *map;
1.2106 +
1.2107 + if (litidx!=0)
1.2108 + error(89,name); /* state variables may not be initialized */
1.2109 + /* find an appropriate address for the state variable */
1.2110 + /* assume that it cannot be found */
1.2111 + address=sizeof(cell)*glb_declared;
1.2112 + glb_incr=(int)size;
1.2113 + /* use a memory map in which every cell occupies one bit */
1.2114 + if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) {
1.2115 + int fsa=state_getfsa(sc_curstates);
1.2116 + symbol *sweep;
1.2117 + cell sweepsize,addr;
1.2118 + memset(map,0,(glb_declared+7)/8);
1.2119 + assert(fsa>=0);
1.2120 + /* fill in all variables belonging to this automaton */
1.2121 + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
1.2122 + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
1.2123 + continue; /* hierarchical type, or no states, or same as this variable */
1.2124 + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
1.2125 + continue; /* a function or a constant */
1.2126 + if ((sweep->usage & uDEFINE)==0)
1.2127 + continue; /* undefined variable, ignore */
1.2128 + if (fsa!=state_getfsa(sweep->states->next->index))
1.2129 + continue; /* wrong automaton */
1.2130 + /* when arrived here, this is a global variable, with states and
1.2131 + * belonging to the same automaton as the variable we are declaring
1.2132 + */
1.2133 + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
1.2134 + assert(sweep->addr % sizeof(cell) == 0);
1.2135 + addr=sweep->addr/sizeof(cell);
1.2136 + /* mark this address range */
1.2137 + while (sweepsize-->0) {
1.2138 + map[addr/8] |= (unsigned char)(1 << (addr % 8));
1.2139 + addr++;
1.2140 + } /* while */
1.2141 + } /* for */
1.2142 + /* go over it again, clearing any ranges that have conflicts */
1.2143 + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
1.2144 + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
1.2145 + continue; /* hierarchical type, or no states, or same as this variable */
1.2146 + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
1.2147 + continue; /* a function or a constant */
1.2148 + if ((sweep->usage & uDEFINE)==0)
1.2149 + continue; /* undefined variable, ignore */
1.2150 + if (fsa!=state_getfsa(sweep->states->next->index))
1.2151 + continue; /* wrong automaton */
1.2152 + /* when arrived here, this is a global variable, with states and
1.2153 + * belonging to the same automaton as the variable we are declaring
1.2154 + */
1.2155 + /* if the lists of states of the existing variable and the new
1.2156 + * variable have a non-empty intersection, this is not a suitable
1.2157 + * overlap point -> wipe the address range
1.2158 + */
1.2159 + if (state_conflict_id(sc_curstates,sweep->states->next->index)) {
1.2160 + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
1.2161 + assert(sweep->addr % sizeof(cell) == 0);
1.2162 + addr=sweep->addr/sizeof(cell);
1.2163 + /* mark this address range */
1.2164 + while (sweepsize-->0) {
1.2165 + map[addr/8] &= (unsigned char)(~(1 << (addr % 8)));
1.2166 + addr++;
1.2167 + } /* while */
1.2168 + } /* if */
1.2169 + } /* for */
1.2170 + /* now walk through the map and find a starting point that is big enough */
1.2171 + sweepsize=0;
1.2172 + for (addr=0; addr<glb_declared; addr++) {
1.2173 + if ((map[addr/8] & (1 << (addr % 8)))==0)
1.2174 + continue;
1.2175 + for (sweepsize=addr+1; sweepsize<glb_declared; sweepsize++) {
1.2176 + if ((map[sweepsize/8] & (1 << (sweepsize % 8)))==0)
1.2177 + break; /* zero bit found, skip this range */
1.2178 + if (sweepsize-addr>=size)
1.2179 + break; /* fitting range found, no need to search further */
1.2180 + } /* for */
1.2181 + if (sweepsize-addr>=size)
1.2182 + break; /* fitting range found, no need to search further */
1.2183 + addr=sweepsize;
1.2184 + } /* for */
1.2185 + free(map);
1.2186 + if (sweepsize-addr>=size) {
1.2187 + address=sizeof(cell)*addr; /* fitting range found, set it */
1.2188 + glb_incr=0;
1.2189 + } /* if */
1.2190 + } /* if */
1.2191 + } else {
1.2192 + address=sizeof(cell)*glb_declared;
1.2193 + glb_incr=(int)size;
1.2194 + } /* if */
1.2195 + if (size != CELL_MAX && address==sizeof(cell)*glb_declared) {
1.2196 + dumplits(); /* dump the literal queue */
1.2197 + dumpzero((int)size-litidx);
1.2198 + } /* if */
1.2199 + litidx=0;
1.2200 + if (sym==NULL) { /* define only if not yet defined */
1.2201 + sym=addvariable2(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag,slength);
1.2202 + if (sc_curstates>0)
1.2203 + attachstatelist(sym,sc_curstates);
1.2204 + } else { /* if declared but not yet defined, adjust the variable's address */
1.2205 + assert(sym->states==NULL && sc_curstates==0
1.2206 + || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL);
1.2207 + sym->addr=address;
1.2208 + sym->codeaddr=code_idx;
1.2209 + sym->usage|=uDEFINE;
1.2210 + } /* if */
1.2211 + assert(sym!=NULL);
1.2212 + sc_curstates=0;
1.2213 + if (ispublic)
1.2214 + sym->usage|=uPUBLIC|uREAD;
1.2215 + if (fconst)
1.2216 + sym->usage|=uCONST;
1.2217 + if (fstock)
1.2218 + sym->usage|=uSTOCK;
1.2219 + if (fstatic)
1.2220 + sym->fnumber=filenum;
1.2221 + sc_attachdocumentation(sym);/* attach any documenation to the variable */
1.2222 + if (sc_status==statSKIP) {
1.2223 + sc_status=statWRITE;
1.2224 + code_idx=cidx;
1.2225 + assert(glb_declared==glbdecl);
1.2226 + } else {
1.2227 + glb_declared+=glb_incr; /* add total number of cells (if added to the end) */
1.2228 + } /* if */
1.2229 + } while (matchtoken(',')); /* enddo */ /* more? */
1.2230 + needtoken(tTERM); /* if not comma, must be semicolumn */
1.2231 +}
1.2232 +
1.2233 +/* declloc - declare local symbols
1.2234 + *
1.2235 + * Declare local (automatic) variables. Since these variables are relative
1.2236 + * to the STACK, there is no switch to the DATA segment. These variables
1.2237 + * cannot be initialized either.
1.2238 + *
1.2239 + * global references: declared (altered)
1.2240 + * funcstatus (referred to only)
1.2241 + */
1.2242 +static int declloc(int fstatic)
1.2243 +{
1.2244 + int ident,tag;
1.2245 + int idxtag[sDIMEN_MAX];
1.2246 + char name[sNAMEMAX+1];
1.2247 + symbol *sym;
1.2248 + constvalue *enumroot=NULL;
1.2249 + cell val,size;
1.2250 + char *str;
1.2251 + value lval = {0};
1.2252 + int cur_lit=0;
1.2253 + int dim[sDIMEN_MAX];
1.2254 + int numdim;
1.2255 + int fconst;
1.2256 + int staging_start;
1.2257 + int slength = 0;
1.2258 +
1.2259 + fconst=matchtoken(tCONST);
1.2260 + do {
1.2261 + ident=iVARIABLE;
1.2262 + size=1;
1.2263 + slength=0;
1.2264 + numdim=0; /* no dimensions */
1.2265 + tag=pc_addtag(NULL);
1.2266 + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
1.2267 + error(20,str); /* invalid symbol name */
1.2268 + assert(strlen(str)<=sNAMEMAX);
1.2269 + strcpy(name,str); /* save symbol name */
1.2270 + if (name[0]==PUBLIC_CHAR)
1.2271 + error(56,name); /* local variables cannot be public */
1.2272 + /* Note: block locals may be named identical to locals at higher
1.2273 + * compound blocks (as with standard C); so we must check (and add)
1.2274 + * the "nesting level" of local variables to verify the
1.2275 + * multi-definition of symbols.
1.2276 + */
1.2277 + if ((sym=findloc(name))!=NULL && sym->compound==nestlevel)
1.2278 + error(21,name); /* symbol already defined */
1.2279 + /* Although valid, a local variable whose name is equal to that
1.2280 + * of a global variable or to that of a local variable at a lower
1.2281 + * level might indicate a bug.
1.2282 + */
1.2283 + if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL)
1.2284 + error(219,name); /* variable shadows another symbol */
1.2285 + if (matchtoken('[')) {
1.2286 + int _index;
1.2287 + cell _code;
1.2288 + int dim_ident;
1.2289 + symbol *dim_sym;
1.2290 + value dim_val;
1.2291 + int all_constant = 1;
1.2292 + int _staging = staging;
1.2293 +
1.2294 + if (!_staging)
1.2295 + stgset(TRUE);
1.2296 + stgget(&_index, &_code);
1.2297 +
1.2298 + do {
1.2299 + if (numdim == sDIMEN_MAX) {
1.2300 + error(53); /* exceeding maximum number of dimensions */
1.2301 + return (all_constant ? iARRAY : iREFARRAY);
1.2302 + } else if (numdim) { /* if */
1.2303 + /* If we have a dimension on the stack, push it */
1.2304 + pushreg(sPRI);
1.2305 + }
1.2306 + if (matchtoken(']')) {
1.2307 + idxtag[numdim] = 0;
1.2308 + dim[numdim] = 0;
1.2309 + numdim++;
1.2310 + continue;
1.2311 + }
1.2312 + dim_ident = doexpr2(TRUE,FALSE,FALSE,FALSE,&idxtag[numdim],&dim_sym,0,&dim_val);
1.2313 + if (dim_ident == iVARIABLE || dim_ident == iEXPRESSION || dim_ident == iARRAYCELL) {
1.2314 + all_constant = 0;
1.2315 + } else if (dim_ident == iCONSTEXPR) {
1.2316 + dim[numdim] = dim_val.constval;
1.2317 + /* :TODO: :URGENT: Make sure this still works */
1.2318 + if (dim_sym && dim_sym->usage & uENUMROOT)
1.2319 + enumroot = dim_sym->dim.enumlist;
1.2320 + idxtag[numdim] = dim_sym ? dim_sym->tag : 0;
1.2321 + #if INT_MAX < LONG_MAX
1.2322 + if (dim[numdim] > INT_MAX)
1.2323 + error(125); /* overflow, exceeding capacity */
1.2324 + #endif
1.2325 + } else {
1.2326 + error(29); /* invalid expression, assumed 0 */
1.2327 + }
1.2328 + numdim++;
1.2329 + needtoken(']');
1.2330 + } while (matchtoken('['));
1.2331 + if (all_constant) {
1.2332 + /* Change the last dimension to be based on chars instead if we have a string */
1.2333 + if (tag == pc_tag_string && numdim && dim[numdim-1]) {
1.2334 + slength = dim[numdim-1];
1.2335 + dim[numdim-1] = (dim[numdim-1] + sizeof(cell)-1) / sizeof(cell);
1.2336 + }
1.2337 + /* Scrap the code generated */
1.2338 + ident = iARRAY;
1.2339 + stgdel(_index, _code);
1.2340 + } else {
1.2341 + if (tag == pc_tag_string && numdim && dim[numdim-1]) {
1.2342 + stradjust(sPRI);
1.2343 + }
1.2344 + pushreg(sPRI);
1.2345 + memset(dim, 0, sizeof(int)*sDIMEN_MAX);
1.2346 + ident = iREFARRAY;
1.2347 + genarray(numdim, autozero);
1.2348 + }
1.2349 + stgout(_index);
1.2350 + if (!_staging)
1.2351 + stgset(FALSE);
1.2352 + }
1.2353 + if (getstates(name))
1.2354 + error(88,name); /* local variables may not have states */
1.2355 + if (ident==iARRAY || fstatic) {
1.2356 + if (sc_alignnext) {
1.2357 + aligndata(sc_dataalign);
1.2358 + sc_alignnext=FALSE;
1.2359 + } /* if */
1.2360 + cur_lit=litidx; /* save current index in the literal table */
1.2361 + if (numdim && !dim[numdim-1])
1.2362 + size = 0;
1.2363 + initials(ident,tag,&size,dim,numdim,enumroot);
1.2364 + if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
1.2365 + slength = glbstringread;
1.2366 + }
1.2367 + if (size==0)
1.2368 + return ident; /* error message already given */
1.2369 + if (numdim==1)
1.2370 + dim[0]=(int)size;
1.2371 + }
1.2372 + /* reserve memory (on the stack) for the variable */
1.2373 + if (fstatic) {
1.2374 + /* write zeros for uninitialized fields */
1.2375 + while (litidx<cur_lit+size)
1.2376 + litadd(0);
1.2377 + sym=addvariable2(name,(cur_lit+glb_declared)*sizeof(cell),ident,sSTATIC,
1.2378 + tag,dim,numdim,idxtag,slength);
1.2379 + } else if (ident!=iREFARRAY) {
1.2380 + declared+=(int)size; /* variables are put on stack, adjust "declared" */
1.2381 + sym=addvariable2(name,-declared*sizeof(cell),ident,sLOCAL,tag,
1.2382 + dim,numdim,idxtag,slength);
1.2383 + if (ident==iVARIABLE) {
1.2384 + assert(!staging);
1.2385 + stgset(TRUE); /* start stage-buffering */
1.2386 + assert(stgidx==0);
1.2387 + staging_start=stgidx;
1.2388 + } /* if */
1.2389 + markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
1.2390 + modstk(-(int)size*sizeof(cell));
1.2391 + markstack(MEMUSE_STATIC, size);
1.2392 + assert(curfunc!=NULL);
1.2393 + assert((curfunc->usage & uNATIVE)==0);
1.2394 + if (curfunc->x.stacksize<declared+1)
1.2395 + curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
1.2396 + } else if (ident==iREFARRAY) {
1.2397 + declared+=1; /* one cell for address */
1.2398 + sym=addvariable(name,-declared*sizeof(cell),ident,sLOCAL,tag,dim,numdim,idxtag);
1.2399 + //markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
1.2400 + /* genarray() pushes the address onto the stack, so we don't need to call modstk() here! */
1.2401 + markheap(MEMUSE_DYNAMIC, 0);
1.2402 + markstack(MEMUSE_STATIC, 1);
1.2403 + assert(curfunc != NULL && ((curfunc->usage & uNATIVE) == 0));
1.2404 + if (curfunc->x.stacksize<declared+1)
1.2405 + curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
1.2406 + } /* if */
1.2407 + /* now that we have reserved memory for the variable, we can proceed
1.2408 + * to initialize it */
1.2409 + assert(sym!=NULL); /* we declared it, it must be there */
1.2410 + sym->compound=nestlevel; /* for multiple declaration/shadowing check */
1.2411 + if (fconst)
1.2412 + sym->usage|=uCONST;
1.2413 + if (!fstatic) { /* static variables already initialized */
1.2414 + if (ident==iVARIABLE) {
1.2415 + /* simple variable, also supports initialization */
1.2416 + int ctag = tag; /* set to "tag" by default */
1.2417 + int explicit_init=FALSE;/* is the variable explicitly initialized? */
1.2418 + int cident=ident;
1.2419 + if (matchtoken('=')) {
1.2420 + if (!autozero)
1.2421 + error(10);
1.2422 + cident=doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE);
1.2423 + explicit_init=TRUE;
1.2424 + } else {
1.2425 + if (autozero)
1.2426 + ldconst(0,sPRI); /* uninitialized variable, set to zero */
1.2427 + } /* if */
1.2428 + if (autozero) {
1.2429 + /* now try to save the value (still in PRI) in the variable */
1.2430 + lval.sym=sym;
1.2431 + lval.ident=iVARIABLE;
1.2432 + lval.constval=0;
1.2433 + lval.tag=tag;
1.2434 + check_userop(NULL,ctag,lval.tag,2,NULL,&ctag);
1.2435 + store(&lval);
1.2436 + markexpr(sEXPR,NULL,0); /* full expression ends after the store */
1.2437 + }
1.2438 + assert(staging); /* end staging phase (optimize expression) */
1.2439 + stgout(staging_start);
1.2440 + stgset(FALSE);
1.2441 + if (!matchtag_string(cident, ctag) && !matchtag(tag,ctag,TRUE))
1.2442 + {
1.2443 + if (tag & FUNCTAG)
1.2444 + error(100); /* error - function prototypes do not match */
1.2445 + else
1.2446 + error(213); /* warning - tag mismatch */
1.2447 + }
1.2448 + /* if the variable was not explicitly initialized, reset the
1.2449 + * "uWRITTEN" flag that store() set */
1.2450 + if (!explicit_init)
1.2451 + sym->usage &= ~uWRITTEN;
1.2452 + } else if (ident!=iREFARRAY) {
1.2453 + /* an array */
1.2454 + assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax);
1.2455 + assert(size>0 && size>=sym->dim.array.length);
1.2456 + assert(numdim>1 || size==sym->dim.array.length);
1.2457 + if (autozero) {
1.2458 + /* final literal values that are zero make no sense to put in the literal
1.2459 + * pool, because values get zero-initialized anyway; we check for this,
1.2460 + * because users often explicitly initialize strings to ""
1.2461 + */
1.2462 + while (litidx>cur_lit && litq[litidx-1]==0)
1.2463 + litidx--;
1.2464 + /* if the array is not completely filled, set all values to zero first */
1.2465 + if (litidx-cur_lit<size && (ucell)size<CELL_MAX)
1.2466 + fillarray(sym,size*sizeof(cell),0);
1.2467 + }
1.2468 + if (cur_lit<litidx) {
1.2469 + /* check whether the complete array is set to a single value; if
1.2470 + * it is, more compact code can be generated */
1.2471 + cell first=litq[cur_lit];
1.2472 + int i;
1.2473 + for (i=cur_lit; i<litidx && litq[i]==first; i++)
1.2474 + /* nothing */;
1.2475 + if (i==litidx) {
1.2476 + /* all values are the same */
1.2477 + fillarray(sym,(litidx-cur_lit)*sizeof(cell),first);
1.2478 + litidx=cur_lit; /* reset literal table */
1.2479 + } else {
1.2480 + /* copy the literals to the array */
1.2481 + ldconst((cur_lit+glb_declared)*sizeof(cell),sPRI);
1.2482 + copyarray(sym,(litidx-cur_lit)*sizeof(cell));
1.2483 + } /* if */
1.2484 + } /* if */
1.2485 + } /* if */
1.2486 + } /* if */
1.2487 + } while (matchtoken(',')); /* enddo */ /* more? */
1.2488 + needtoken(tTERM); /* if not comma, must be semicolumn */
1.2489 + return ident;
1.2490 +}
1.2491 +
1.2492 +/* this function returns the maximum value for a cell in case of an error
1.2493 + * (invalid dimension).
1.2494 + */
1.2495 +static cell calc_arraysize(int dim[],int numdim,int cur)
1.2496 +{
1.2497 + cell subsize;
1.2498 + ucell newsize;
1.2499 +
1.2500 + /* the return value is in cells, not bytes */
1.2501 + assert(cur>=0 && cur<=numdim);
1.2502 + if (cur==numdim)
1.2503 + return 0;
1.2504 + subsize=calc_arraysize(dim,numdim,cur+1);
1.2505 + newsize=dim[cur]+dim[cur]*subsize;
1.2506 + if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize*sizeof(cell)>=CELL_MAX)
1.2507 + return CELL_MAX;
1.2508 + return newsize;
1.2509 +}
1.2510 +
1.2511 +static cell gen_indirection_vecs(array_info_t *ar, int dim, cell cur_offs)
1.2512 +{
1.2513 + int i;
1.2514 + cell write_offs = cur_offs;
1.2515 + cell *data_offs = ar->data_offs;
1.2516 +
1.2517 + cur_offs += ar->dim_list[dim];
1.2518 +
1.2519 + /**
1.2520 + * Dimension n-x where x > 2 will have sub-vectors.
1.2521 + * Otherwise, we just need to reference the data section.
1.2522 + */
1.2523 + if (ar->dim_count > 2 && dim < ar->dim_count - 2)
1.2524 + {
1.2525 + /**
1.2526 + * For each index at this dimension, write offstes to our sub-vectors.
1.2527 + * After we write one sub-vector, we generate its sub-vectors recursively.
1.2528 + * At the end, we're given the next offset we can use.
1.2529 + */
1.2530 + for (i = 0; i < ar->dim_list[dim]; i++)
1.2531 + {
1.2532 + ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell);
1.2533 + write_offs++;
1.2534 + ar->cur_dims[dim] = i;
1.2535 + cur_offs = gen_indirection_vecs(ar, dim + 1, cur_offs);
1.2536 + }
1.2537 + } else if (ar->dim_count > 1) {
1.2538 + /**
1.2539 + * In this section, there are no sub-vectors, we need to write offsets
1.2540 + * to the data. This is separate so the data stays in one big chunk.
1.2541 + * The data offset will increment by the size of the last dimension,
1.2542 + * because that is where the data is finally computed as. But the last
1.2543 + * dimension can be of variable size, so we have to detect that.
1.2544 + */
1.2545 + if (ar->dim_list[dim + 1] == 0)
1.2546 + {
1.2547 + int vec_start = 0;
1.2548 +
1.2549 + /**
1.2550 + * Using the precalculated offsets, compute an index into the last
1.2551 + * dimension array.
1.2552 + */
1.2553 + for (i = 0; i < dim; i++)
1.2554 + {
1.2555 + vec_start += ar->cur_dims[i] * ar->dim_offs_precalc[i];
1.2556 + }
1.2557 +
1.2558 + /**
1.2559 + * Now, vec_start points to a vector of last dimension offsets for
1.2560 + * the preceding dimension combination(s).
1.2561 + * I.e. (1,2,i,j) in [3][4][5][] will be:
1.2562 + * j = 1*(4*5) + 2*(5) + i, and the parenthetical expressions are
1.2563 + * precalculated for us so we can easily generalize here.
1.2564 + */
1.2565 + for (i = 0; i < ar->dim_list[dim]; i++)
1.2566 + {
1.2567 + ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell);
1.2568 + write_offs++;
1.2569 + *data_offs = *data_offs + ar->lastdim_list[vec_start + i];
1.2570 + }
1.2571 + } else {
1.2572 + /**
1.2573 + * The last dimension size is constant. There's no extra work to
1.2574 + * compute the last dimension size.
1.2575 + */
1.2576 + for (i = 0; i < ar->dim_list[dim]; i++)
1.2577 + {
1.2578 + ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell);
1.2579 + write_offs++;
1.2580 + *data_offs = *data_offs + ar->dim_list[dim + 1];
1.2581 + }
1.2582 + }
1.2583 + }
1.2584 +
1.2585 + return cur_offs;
1.2586 +}
1.2587 +
1.2588 +static cell calc_indirection(const int dim_list[], int dim_count, int dim)
1.2589 +{
1.2590 + cell size = dim_list[dim];
1.2591 +
1.2592 + if (dim < dim_count - 2)
1.2593 + {
1.2594 + size += dim_list[dim] * calc_indirection(dim_list, dim_count, dim + 1);
1.2595 + }
1.2596 +
1.2597 + return size;
1.2598 +}
1.2599 +
1.2600 +static void adjust_indirectiontables(int dim[],int numdim,int cur,cell increment,
1.2601 + int startlit,constvalue *lastdim,int *skipdim)
1.2602 +{
1.2603 + /* Find how many cells the indirection table will be */
1.2604 + cell tbl_size;
1.2605 + int *dyn_list = NULL;
1.2606 + int cur_dims[sDIMEN_MAX];
1.2607 + cell dim_offset_precalc[sDIMEN_MAX];
1.2608 + array_info_t ar;
1.2609 +
1.2610 + if (numdim == 1)
1.2611 + {
1.2612 + return;
1.2613 + }
1.2614 +
1.2615 + tbl_size = calc_indirection(dim, numdim, 0);
1.2616 + memset(cur_dims, 0, sizeof(cur_dims));
1.2617 +
1.2618 + /**
1.2619 + * Flatten the last dimension array list -- this makes
1.2620 + * things MUCH easier in the indirection calculator.
1.2621 + */
1.2622 + if (lastdim)
1.2623 + {
1.2624 + int i;
1.2625 + constvalue *ld = lastdim->next;
1.2626 +
1.2627 + /* Get the total number of last dimensions. */
1.2628 + for (i = 0; ld != NULL; i++, ld = ld->next)
1.2629 + {
1.2630 + /* Nothing */
1.2631 + }
1.2632 + /* Store them in an array instead of a linked list. */
1.2633 + dyn_list = (int *)malloc(sizeof(int) * i);
1.2634 + for (i = 0, ld = lastdim->next;
1.2635 + ld != NULL;
1.2636 + i++, ld = ld->next)
1.2637 + {
1.2638 + dyn_list[i] = ld->value;
1.2639 + }
1.2640 +
1.2641 + /**
1.2642 + * Pre-calculate all of the offsets. This speeds up and simplifies
1.2643 + * the indirection process. For example, if we have an array like:
1.2644 + * [a][b][c][d][], and given (A,B,C), we want to find the size of
1.2645 + * the last dimension [A][B][C][i], we must do:
1.2646 + *
1.2647 + * list[A*(b*c*d) + B*(c*d) + C*(d) + i]
1.2648 + *
1.2649 + * Generalizing this algorithm in the indirection process is expensive,
1.2650 + * so we lessen the need for nested loops by pre-computing the parts:
1.2651 + * (b*c*d), (c*d), and (d).
1.2652 + *
1.2653 + * In other words, finding the offset to dimension N at index I is
1.2654 + * I * (S[N+1] * S[N+2] ... S[N+n-1]) where S[] is the size of dimension
1.2655 + * function, and n is the index of the last dimension.
1.2656 + */
1.2657 + for (i = 0; i < numdim - 1; i++)
1.2658 + {
1.2659 + int j;
1.2660 +
1.2661 + dim_offset_precalc[i] = 1;
1.2662 + for (j = i + 1; j < numdim - 1; j++)
1.2663 + {
1.2664 + dim_offset_precalc[i] *= dim[j];
1.2665 + }
1.2666 + }
1.2667 +
1.2668 + ar.dim_offs_precalc = dim_offset_precalc;
1.2669 + ar.lastdim_list = dyn_list;
1.2670 + } else {
1.2671 + ar.dim_offs_precalc = NULL;
1.2672 + ar.lastdim_list = NULL;
1.2673 + }
1.2674 +
1.2675 + ar.base = &litq[startlit];
1.2676 + ar.data_offs = &tbl_size;
1.2677 + ar.dim_list = dim;
1.2678 + ar.dim_count = numdim;
1.2679 + ar.cur_dims = cur_dims;
1.2680 +
1.2681 + gen_indirection_vecs(&ar, 0, 0);
1.2682 +
1.2683 + free(dyn_list);
1.2684 +}
1.2685 +
1.2686 +/* initials
1.2687 + *
1.2688 + * Initialize global objects and local arrays.
1.2689 + * size==array cells (count), if 0 on input, the routine counts the number of elements
1.2690 + * tag==required tagname id (not the returned tag)
1.2691 + *
1.2692 + * Global references: litidx (altered)
1.2693 + */
1.2694 +static void initials2(int ident,int tag,cell *size,int dim[],int numdim,
1.2695 + constvalue *enumroot, int eq_match_override, int curlit_override)
1.2696 +{
1.2697 + int ctag;
1.2698 + cell tablesize;
1.2699 + int curlit=(curlit_override == -1) ? litidx : curlit_override;
1.2700 + int err=0;
1.2701 +
1.2702 + if (eq_match_override == -1) {
1.2703 + eq_match_override = matchtoken('=');
1.2704 + }
1.2705 +
1.2706 + if (!eq_match_override) {
1.2707 + assert(ident!=iARRAY || numdim>0);
1.2708 + if (ident==iARRAY && dim[numdim-1]==0) {
1.2709 + /* declared as "myvar[];" which is senseless (note: this *does* make
1.2710 + * sense in the case of a iREFARRAY, which is a function parameter)
1.2711 + */
1.2712 + error(9); /* array has zero length -> invalid size */
1.2713 + } /* if */
1.2714 + if (ident==iARRAY) {
1.2715 + assert(numdim>0 && numdim<=sDIMEN_MAX);
1.2716 + *size=calc_arraysize(dim,numdim,0);
1.2717 + if (*size==(cell)CELL_MAX) {
1.2718 + error(9); /* array is too big -> invalid size */
1.2719 + return;
1.2720 + } /* if */
1.2721 + /* first reserve space for the indirection vectors of the array, then
1.2722 + * adjust it to contain the proper values
1.2723 + * (do not use dumpzero(), as it bypasses the literal queue)
1.2724 + */
1.2725 + for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
1.2726 + litadd(0);
1.2727 + if (dim[numdim-1]!=0) /* error 9 has already been given */
1.2728 + adjust_indirectiontables(dim,numdim,0,0,curlit,NULL,NULL);
1.2729 + } /* if */
1.2730 + return;
1.2731 + } /* if */
1.2732 +
1.2733 + if (ident==iVARIABLE) {
1.2734 + assert(*size==1);
1.2735 + init(ident,&ctag,NULL);
1.2736 + if (!matchtag(tag,ctag,TRUE))
1.2737 + {
1.2738 + if (tag & FUNCTAG)
1.2739 + error(100); /* error - function prototypes do not match */
1.2740 + else
1.2741 + error(213); /* warning - tag mismatch */
1.2742 + }
1.2743 + } else {
1.2744 + assert(numdim>0);
1.2745 + if (numdim==1) {
1.2746 + *size=initvector(ident,tag,dim[0],FALSE,enumroot,NULL);
1.2747 + } else {
1.2748 + int errorfound=FALSE;
1.2749 + int counteddim[sDIMEN_MAX];
1.2750 + int idx;
1.2751 + constvalue lastdim={NULL,"",0,0}; /* sizes of the final dimension */
1.2752 + int skipdim=0;
1.2753 +
1.2754 + if (dim[numdim-1]!=0)
1.2755 + *size=calc_arraysize(dim,numdim,0); /* calc. full size, if known */
1.2756 + /* already reserve space for the indirection tables (for an array with
1.2757 + * known dimensions)
1.2758 + * (do not use dumpzero(), as it bypasses the literal queue)
1.2759 + */
1.2760 + for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
1.2761 + litadd(0);
1.2762 + /* now initialize the sub-arrays */
1.2763 + memset(counteddim,0,sizeof counteddim);
1.2764 + initarray(ident,tag,dim,numdim,0,curlit,counteddim,&lastdim,enumroot,&errorfound);
1.2765 + /* check the specified array dimensions with the initializer counts */
1.2766 + for (idx=0; idx<numdim-1; idx++) {
1.2767 + if (dim[idx]==0) {
1.2768 + dim[idx]=counteddim[idx];
1.2769 + } else if (counteddim[idx]<dim[idx]) {
1.2770 + error(52); /* array is not fully initialized */
1.2771 + err++;
1.2772 + } else if (counteddim[idx]>dim[idx]) {
1.2773 + error(18); /* initialization data exceeds declared size */
1.2774 + err++;
1.2775 + } /* if */
1.2776 + } /* for */
1.2777 + if (numdim>1 && dim[numdim-1]==0) {
1.2778 + /* also look whether, by any chance, all "counted" final dimensions are
1.2779 + * the same value; if so, we can store this
1.2780 + */
1.2781 + constvalue *ld=lastdim.next;
1.2782 + int d,match;
1.2783 + for (d=0; d<dim[numdim-2]; d++) {
1.2784 + assert(ld!=NULL);
1.2785 + assert(strtol(ld->name,NULL,16)==d);
1.2786 + if (d==0)
1.2787 + match=ld->value;
1.2788 + else if (match!=ld->value)
1.2789 + break;
1.2790 + ld=ld->next;
1.2791 + } /* for */
1.2792 + if (d==dim[numdim-2])
1.2793 + dim[numdim-1]=match;
1.2794 + } /* if */
1.2795 + /* after all arrays have been initalized, we know the (major) dimensions
1.2796 + * of the array and we can properly adjust the indirection vectors
1.2797 + */
1.2798 + if (err==0)
1.2799 + adjust_indirectiontables(dim,numdim,0,0,curlit,&lastdim,&skipdim);
1.2800 + delete_consttable(&lastdim); /* clear list of minor dimension sizes */
1.2801 + } /* if */
1.2802 + } /* if */
1.2803 +
1.2804 + if (*size==0)
1.2805 + *size=litidx-curlit; /* number of elements defined */
1.2806 +}
1.2807 +
1.2808 +static void initials(int ident,int tag,cell *size,int dim[],int numdim,
1.2809 + constvalue *enumroot)
1.2810 +{
1.2811 + initials2(ident, tag, size, dim, numdim, enumroot, -1, -1);
1.2812 +}
1.2813 +
1.2814 +static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
1.2815 + int startlit,int counteddim[],constvalue *lastdim,
1.2816 + constvalue *enumroot,int *errorfound)
1.2817 +{
1.2818 + cell dsize,totalsize;
1.2819 + int idx,abortparse;
1.2820 +
1.2821 + assert(cur>=0 && cur<numdim);
1.2822 + assert(startlit>=0);
1.2823 + assert(cur+2<=numdim);/* there must be 2 dimensions or more to do */
1.2824 + assert(errorfound!=NULL && *errorfound==FALSE);
1.2825 + totalsize=0;
1.2826 + needtoken('{');
1.2827 + for (idx=0,abortparse=FALSE; !abortparse; idx++) {
1.2828 +
1.2829 + /* In case the major dimension is zero, we need to store the offset
1.2830 + * to the newly detected sub-array into the indirection table; i.e.
1.2831 + * this table needs to be expanded and updated.
1.2832 + * In the current design, the indirection vectors for a multi-dimensional
1.2833 + * array are adjusted after parsing all initializers. Hence, it is only
1.2834 + * necessary at this point to reserve space for an extra cell in the
1.2835 + * indirection vector.
1.2836 + */
1.2837 + if (dim[cur]==0) {
1.2838 + litinsert(0,startlit);
1.2839 + } else if (idx>=dim[cur]) {
1.2840 + error(18); /* initialization data exceeds array size */
1.2841 + break;
1.2842 + } /* if */
1.2843 + if (cur+2<numdim) {
1.2844 + dsize=initarray(ident,tag,dim,numdim,cur+1,startlit,counteddim,
1.2845 + lastdim,enumroot,errorfound);
1.2846 + } else {
1.2847 + dsize=initvector(ident,tag,dim[cur+1],TRUE,enumroot,errorfound);
1.2848 + /* The final dimension may be variable length. We need to keep the
1.2849 + * lengths of the final dimensions in order to set the indirection
1.2850 + * vectors for the next-to-last dimension.
1.2851 + */
1.2852 + append_constval(lastdim,itoh(idx),dsize,0);
1.2853 + } /* if */
1.2854 + totalsize+=dsize;
1.2855 + if (*errorfound || !matchtoken(','))
1.2856 + abortparse=TRUE;
1.2857 + } /* for */
1.2858 + needtoken('}');
1.2859 + assert(counteddim!=NULL);
1.2860 + if (counteddim[cur]>0) {
1.2861 + if (idx<counteddim[cur])
1.2862 + error(52); /* array is not fully initialized */
1.2863 + else if (idx>counteddim[cur])
1.2864 + error(18); /* initialization data exceeds declared size */
1.2865 + } /* if */
1.2866 + counteddim[cur]=idx;
1.2867 +
1.2868 + return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */
1.2869 +}
1.2870 +
1.2871 +/* initvector
1.2872 + * Initialize a single dimensional array
1.2873 + */
1.2874 +static cell initvector(int ident,int tag,cell size,int fillzero,
1.2875 + constvalue *enumroot,int *errorfound)
1.2876 +{
1.2877 + cell prev1=0,prev2=0;
1.2878 + int ellips=FALSE;
1.2879 + int curlit=litidx;
1.2880 + int rtag,ctag;
1.2881 +
1.2882 + assert(ident==iARRAY || ident==iREFARRAY);
1.2883 + if (matchtoken('{')) {
1.2884 + constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL;
1.2885 + do {
1.2886 + int fieldlit=litidx;
1.2887 + int matchbrace,i;
1.2888 + if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */
1.2889 + lexpush();
1.2890 + break;
1.2891 + } /* if */
1.2892 + if ((ellips=matchtoken(tELLIPS))!=0)
1.2893 + break;
1.2894 + /* for enumeration fields, allow another level of braces ("{...}") */
1.2895 + matchbrace=0; /* preset */
1.2896 + ellips=0;
1.2897 + if (enumfield!=NULL)
1.2898 + matchbrace=matchtoken('{');
1.2899 + for ( ;; ) {
1.2900 + prev2=prev1;
1.2901 + prev1=init(ident,&ctag,errorfound);
1.2902 + if (!matchbrace)
1.2903 + break;
1.2904 + if ((ellips=matchtoken(tELLIPS))!=0)
1.2905 + break;
1.2906 + if (!matchtoken(',')) {
1.2907 + needtoken('}');
1.2908 + break;
1.2909 + } /* for */
1.2910 + } /* for */
1.2911 + /* if this array is based on an enumeration, fill the "field" up with
1.2912 + * zeros, and toggle the tag
1.2913 + */
1.2914 + if (enumroot!=NULL && enumfield==NULL)
1.2915 + error(227); /* more initializers than enum fields */
1.2916 + rtag=tag; /* preset, may be overridden by enum field tag */
1.2917 + if (enumfield!=NULL) {
1.2918 + cell step;
1.2919 + int cmptag=enumfield->index;
1.2920 + symbol *symfield=findconst(enumfield->name,&cmptag);
1.2921 + if (cmptag>1)
1.2922 + error(91,enumfield->name); /* ambiguous constant, needs tag override */
1.2923 + assert(symfield!=NULL);
1.2924 + assert(fieldlit<litidx);
1.2925 + if (litidx-fieldlit>symfield->dim.array.length)
1.2926 + error(228); /* length of initializer exceeds size of the enum field */
1.2927 + if (ellips) {
1.2928 + step=prev1-prev2;
1.2929 + } else {
1.2930 + step=0;
1.2931 + prev1=0;
1.2932 + } /* if */
1.2933 + for (i=litidx-fieldlit; i<symfield->dim.array.length; i++) {
1.2934 + prev1+=step;
1.2935 + litadd(prev1);
1.2936 + } /* for */
1.2937 + rtag=symfield->x.tags.index; /* set the expected tag to the index tag */
1.2938 + enumfield=enumfield->next;
1.2939 + } /* if */
1.2940 + if (!matchtag(rtag,ctag,TRUE))
1.2941 + {
1.2942 + if (rtag & FUNCTAG)
1.2943 + error(100); /* error - function prototypes do not match */
1.2944 + else
1.2945 + error(213); /* warning - tag mismatch */
1.2946 + }
1.2947 + } while (matchtoken(',')); /* do */
1.2948 + needtoken('}');
1.2949 + } else {
1.2950 + init(ident,&ctag,errorfound);
1.2951 + if (!matchtag(tag,ctag,TRUE))
1.2952 + error(213); /* tagname mismatch */
1.2953 + } /* if */
1.2954 + /* fill up the literal queue with a series */
1.2955 + if (ellips) {
1.2956 + cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2;
1.2957 + if (size==0 || (litidx-curlit)==0)
1.2958 + error(41); /* invalid ellipsis, array size unknown */
1.2959 + else if ((litidx-curlit)==(int)size)
1.2960 + error(18); /* initialization data exceeds declared size */
1.2961 + while ((litidx-curlit)<(int)size) {
1.2962 + prev1+=step;
1.2963 + litadd(prev1);
1.2964 + } /* while */
1.2965 + } /* if */
1.2966 + if (fillzero && size>0) {
1.2967 + while ((litidx-curlit)<(int)size)
1.2968 + litadd(0);
1.2969 + } /* if */
1.2970 + if (size==0) {
1.2971 + size=litidx-curlit; /* number of elements defined */
1.2972 + } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */
1.2973 + error(18); /* initialization data exceeds declared size */
1.2974 + litidx=(int)size+curlit; /* avoid overflow in memory moves */
1.2975 + } /* if */
1.2976 + return size;
1.2977 +}
1.2978 +
1.2979 +/* init
1.2980 + *
1.2981 + * Evaluate one initializer.
1.2982 + */
1.2983 +static cell init(int ident,int *tag,int *errorfound)
1.2984 +{
1.2985 + cell i = 0;
1.2986 +
1.2987 + if (matchtoken(tSTRING)){
1.2988 + /* lex() automatically stores strings in the literal table (and
1.2989 + * increases "litidx") */
1.2990 + if (ident==iVARIABLE) {
1.2991 + error(6); /* must be assigned to an array */
1.2992 + litidx=1; /* reset literal queue */
1.2993 + } /* if */
1.2994 + *tag=pc_tag_string;
1.2995 + } else if (constexpr(&i,tag,NULL)){
1.2996 + litadd(i); /* store expression result in literal table */
1.2997 + } else {
1.2998 + if (errorfound!=NULL)
1.2999 + *errorfound=TRUE;
1.3000 + } /* if */
1.3001 + return i;
1.3002 +}
1.3003 +
1.3004 +/* needsub
1.3005 + *
1.3006 + * Get required array size
1.3007 + */
1.3008 +static cell needsub(int *tag,constvalue **enumroot)
1.3009 +{
1.3010 + cell val;
1.3011 + symbol *sym;
1.3012 +
1.3013 + assert(tag!=NULL);
1.3014 + *tag=0;
1.3015 + if (enumroot!=NULL)
1.3016 + *enumroot=NULL; /* preset */
1.3017 + if (matchtoken(']')) /* we have already seen "[" */
1.3018 + return 0; /* zero size (like "char msg[]") */
1.3019 +
1.3020 + constexpr(&val,tag,&sym); /* get value (must be constant expression) */
1.3021 + if (val<0) {
1.3022 + error(9); /* negative array size is invalid; assumed zero */
1.3023 + val=0;
1.3024 + } /* if */
1.3025 + needtoken(']');
1.3026 +
1.3027 + if (enumroot!=NULL) {
1.3028 + /* get the field list for an enumeration */
1.3029 + assert(*enumroot==NULL);/* should have been preset */
1.3030 + assert(sym==NULL || sym->ident==iCONSTEXPR);
1.3031 + if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) {
1.3032 + assert(sym->dim.enumlist!=NULL);
1.3033 + *enumroot=sym->dim.enumlist;
1.3034 + } /* if */
1.3035 + } /* if */
1.3036 +
1.3037 + return val; /* return array size */
1.3038 +}
1.3039 +
1.3040 +/* decl_const - declare a single constant
1.3041 + *
1.3042 + */
1.3043 +static void decl_const(int vclass)
1.3044 +{
1.3045 + char constname[sNAMEMAX+1];
1.3046 + cell val;
1.3047 + char *str;
1.3048 + int tag,exprtag;
1.3049 + int symbolline;
1.3050 + symbol *sym;
1.3051 +
1.3052 + insert_docstring_separator(); /* see comment in newfunc() */
1.3053 + do {
1.3054 + tag=pc_addtag(NULL);
1.3055 + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
1.3056 + error(20,str); /* invalid symbol name */
1.3057 + symbolline=fline; /* save line where symbol was found */
1.3058 + strcpy(constname,str); /* save symbol name */
1.3059 + needtoken('=');
1.3060 + constexpr(&val,&exprtag,NULL); /* get value */
1.3061 + /* add_constant() checks for duplicate definitions */
1.3062 + if (!matchtag(tag,exprtag,FALSE)) {
1.3063 + /* temporarily reset the line number to where the symbol was defined */
1.3064 + int orgfline=fline;
1.3065 + fline=symbolline;
1.3066 + error(213); /* tagname mismatch */
1.3067 + fline=orgfline;
1.3068 + } /* if */
1.3069 + sym=add_constant(constname,val,vclass,tag);
1.3070 + if (sym!=NULL)
1.3071 + sc_attachdocumentation(sym);/* attach any documenation to the constant */
1.3072 + } while (matchtoken(',')); /* enddo */ /* more? */
1.3073 + needtoken(tTERM);
1.3074 +}
1.3075 +
1.3076 +/*
1.3077 + * declstruct - declare a struct type
1.3078 + */
1.3079 +static void declstruct(void)
1.3080 +{
1.3081 + cell val;
1.3082 + char *str;
1.3083 + int tok;
1.3084 + pstruct_t *pstruct;
1.3085 + int size;
1.3086 +
1.3087 + /* get the explicit tag (required!) */
1.3088 + tok = lex(&val,&str);
1.3089 + if (tok != tSYMBOL)
1.3090 + {
1.3091 + error(93);
1.3092 + }
1.3093 +
1.3094 + if (pstructs_find(str) != NULL)
1.3095 + {
1.3096 + error(98);
1.3097 + }
1.3098 +
1.3099 + pstruct = pstructs_add(str);
1.3100 +
1.3101 + needtoken('{');
1.3102 + do
1.3103 + {
1.3104 + structarg_t arg;
1.3105 + if (matchtoken('}'))
1.3106 + {
1.3107 + /* Quick exit */
1.3108 + lexpush();
1.3109 + break;
1.3110 + }
1.3111 + memset(&arg, 0, sizeof(structarg_t));
1.3112 + tok = lex(&val,&str);
1.3113 + if (tok == tCONST)
1.3114 + {
1.3115 + arg.fconst = 1;
1.3116 + tok = lex(&val,&str);
1.3117 + }
1.3118 + arg.ident = 0;
1.3119 + if (tok == '&')
1.3120 + {
1.3121 + arg.ident = iREFERENCE;
1.3122 + tok = lex(&val,&str);
1.3123 + }
1.3124 + if (tok == tLABEL)
1.3125 + {
1.3126 + arg.tag = pc_addtag(str);
1.3127 + tok = lex(&val,&str);
1.3128 + }
1.3129 + if (tok != tSYMBOL)
1.3130 + {
1.3131 + error(1, "-identifier-", str);
1.3132 + continue;
1.3133 + }
1.3134 + strcpy(arg.name, str);
1.3135 + if (matchtoken('['))
1.3136 + {
1.3137 + if (arg.ident == iREFERENCE)
1.3138 + {
1.3139 + error(67, arg.name);
1.3140 + }
1.3141 + arg.ident = iARRAY;
1.3142 + do
1.3143 + {
1.3144 + constvalue *enumroot;
1.3145 + int ignore_tag;
1.3146 + if (arg.dimcount == sDIMEN_MAX)
1.3147 + {
1.3148 + error(53);
1.3149 + break;
1.3150 + }
1.3151 + size = needsub(&ignore_tag, &enumroot);
1.3152 + arg.dims[arg.dimcount++] = size;
1.3153 + } while (matchtoken('['));
1.3154 + /* Handle strings */
1.3155 + if (arg.tag == pc_tag_string && arg.dims[arg.dimcount-1])
1.3156 + {
1.3157 + arg.dims[arg.dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
1.3158 + }
1.3159 + if (arg.dimcount == 1 && !arg.dims[arg.dimcount-1])
1.3160 + {
1.3161 + arg.ident = iREFARRAY;
1.3162 + }
1.3163 + } else if (!arg.ident) {
1.3164 + arg.ident = iVARIABLE;
1.3165 + }
1.3166 + if (pstructs_addarg(pstruct, &arg) == NULL)
1.3167 + {
1.3168 + error(99, arg.name, pstruct->name);
1.3169 + }
1.3170 + } while (matchtoken(','));
1.3171 + needtoken('}');
1.3172 + matchtoken(';'); /* eat up optional semicolon */
1.3173 +}
1.3174 +
1.3175 +
1.3176 +/**
1.3177 + * dofuncenum - declare function enumerations
1.3178 + */
1.3179 +static void dofuncenum(int listmode)
1.3180 +{
1.3181 + cell val;
1.3182 + char *str;
1.3183 + // char *ptr;
1.3184 + char tagname[sNAMEMAX+1];
1.3185 + constvalue *cur;
1.3186 + funcenum_t *fenum = NULL;
1.3187 + int i;
1.3188 +
1.3189 + /* get the explicit tag (required!) */
1.3190 + int l = lex(&val,&str);
1.3191 + if (l != tSYMBOL)
1.3192 + {
1.3193 + error(93);
1.3194 + }
1.3195 +
1.3196 + /* This tag can't already exist! */
1.3197 + cur=tagname_tab.next;
1.3198 + while (cur)
1.3199 + {
1.3200 + if (strcmp(cur->name, str) == 0)
1.3201 + {
1.3202 + /* Another bad one... */
1.3203 + if (!(cur->value & FUNCTAG))
1.3204 + {
1.3205 + error(94);
1.3206 + }
1.3207 + break;
1.3208 + }
1.3209 + cur = cur->next;
1.3210 + }
1.3211 + strcpy(tagname, str);
1.3212 +
1.3213 + fenum = funcenums_add(tagname);
1.3214 +
1.3215 + if (listmode)
1.3216 + {
1.3217 + needtoken('{');
1.3218 + }
1.3219 + do
1.3220 + {
1.3221 + functag_t func;
1.3222 + if (listmode && matchtoken('}'))
1.3223 + {
1.3224 + /* Quick exit */
1.3225 + lexpush();
1.3226 + break;
1.3227 + }
1.3228 + memset(&func, 0, sizeof(func));
1.3229 + func.ret_tag = pc_addtag(NULL); /* Get the return tag */
1.3230 + l = lex(&val, &str);
1.3231 + /* :TODO:
1.3232 + * Right now, there is a problem. We can't pass non-public function pointer addresses around,
1.3233 + * because the address isn't known until the final reparse. Unfortunately, you can write code
1.3234 + * before the address is known, because Pawn's compiler isn't truly multipass.
1.3235 + *
1.3236 + * When you call a function, there's no problem, because it does not write the address.
1.3237 + * The assembly looks like this:
1.3238 + * call MyFunction
1.3239 + * Then, only at assembly time (once all passes are done), does it know the address.
1.3240 + * I do not see any solution to this because there is no way I know to inject the function name
1.3241 + * rather than the constant value. And even if we could, we'd have to change the assembler recognize that.
1.3242 + */
1.3243 + /*if (l == tSTOCK)
1.3244 + {
1.3245 + func.type = uSTOCK;
1.3246 + } else */if (l == tPUBLIC) {
1.3247 + func.type = uPUBLIC;
1.3248 + } else {
1.3249 + error(1, "-public-", str);
1.3250 + }
1.3251 + needtoken('(');
1.3252 + do
1.3253 + {
1.3254 + funcarg_t *arg = &(func.args[func.argcount]);
1.3255 +
1.3256 + /* Quick exit */
1.3257 + if (matchtoken(')'))
1.3258 + {
1.3259 + lexpush();
1.3260 + break;
1.3261 + }
1.3262 + l = lex(&val, &str);
1.3263 + if (l == '&')
1.3264 + {
1.3265 + if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
1.3266 + {
1.3267 + error(1, "-identifier-", "&");
1.3268 + }
1.3269 + arg->ident = iREFERENCE;
1.3270 + } else if (l == tCONST) {
1.3271 + if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
1.3272 + {
1.3273 + error(1, "-identifier-", "const");
1.3274 + }
1.3275 + arg->fconst=TRUE;
1.3276 + } else if (l == tLABEL) {
1.3277 + if (arg->tagcount > 0)
1.3278 + {
1.3279 + error(1, "-identifier-", "-tagname-");
1.3280 + }
1.3281 + arg->tags[arg->tagcount++] = pc_addtag(str);
1.3282 +#if 0
1.3283 + while (arg->tagcount < sTAGS_MAX)
1.3284 + {
1.3285 + if (!matchtoken('_') && !needtoken(tSYMBOL))
1.3286 + {
1.3287 + break;
1.3288 + }
1.3289 + tokeninfo(&val, &ptr);
1.3290 + arg->tags[arg->tagcount++] = pc_addtag(ptr);
1.3291 + if (matchtoken('}'))
1.3292 + {
1.3293 + break;
1.3294 + }
1.3295 + needtoken(',');
1.3296 + }
1.3297 + needtoken(':');
1.3298 +#endif
1.3299 + l=tLABEL;
1.3300 + } else if (l == tSYMBOL) {
1.3301 + if (func.argcount >= sARGS_MAX)
1.3302 + {
1.3303 + error(45);
1.3304 + }
1.3305 + if (str[0] == PUBLIC_CHAR)
1.3306 + {
1.3307 + error(56, str);
1.3308 + }
1.3309 + if (matchtoken('['))
1.3310 + {
1.3311 + cell size;
1.3312 + if (arg->ident == iREFERENCE)
1.3313 + {
1.3314 + error(67, str);
1.3315 + }
1.3316 + do
1.3317 + {
1.3318 + constvalue *enumroot;
1.3319 + int ignore_tag;
1.3320 + if (arg->dimcount == sDIMEN_MAX)
1.3321 + {
1.3322 + error(53);
1.3323 + break;
1.3324 + }
1.3325 + size = needsub(&ignore_tag, &enumroot);
1.3326 + arg->dims[arg->dimcount] = size;
1.3327 + arg->dimcount += 1;
1.3328 + } while (matchtoken('['));
1.3329 + /* Handle strings */
1.3330 + if ((arg->tagcount == 1 && arg->tags[0] == pc_tag_string)
1.3331 + && arg->dims[arg->dimcount-1])
1.3332 + {
1.3333 + arg->dims[arg->dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
1.3334 + }
1.3335 + arg->ident=iREFARRAY;
1.3336 + } else if (arg->ident == 0) {
1.3337 + arg->ident = iVARIABLE;
1.3338 + }
1.3339 +
1.3340 + if (matchtoken('='))
1.3341 + {
1.3342 + needtoken('0');
1.3343 + arg->ommittable = TRUE;
1.3344 + func.ommittable = TRUE;
1.3345 + } else if (func.ommittable) {
1.3346 + error(95);
1.3347 + }
1.3348 + func.argcount++;
1.3349 + } else if (l == tELLIPS) {
1.3350 + if (arg->ident == iVARIABLE)
1.3351 + {
1.3352 + error(10);
1.3353 + }
1.3354 + arg->ident = iVARARGS;
1.3355 + func.argcount++;
1.3356 + } else {
1.3357 + error(10);
1.3358 + }
1.3359 + } while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(','));
1.3360 + needtoken(')');
1.3361 + for (i=0; i<func.argcount; i++)
1.3362 + {
1.3363 + if (func.args[i].tagcount == 0)
1.3364 + {
1.3365 + func.args[i].tags[0] = 0;
1.3366 + func.args[i].tagcount = 1;
1.3367 + }
1.3368 + }
1.3369 + functags_add(fenum, &func);
1.3370 + if (!listmode)
1.3371 + {
1.3372 + break;
1.3373 + }
1.3374 + } while (matchtoken(','));
1.3375 + if (listmode)
1.3376 + {
1.3377 + needtoken('}');
1.3378 + }
1.3379 + matchtoken(';'); /* eat an optional semicolon. nom nom nom */
1.3380 + errorset(sRESET, 0);
1.3381 +}
1.3382 +
1.3383 +/* decl_enum - declare enumerated constants
1.3384 + *
1.3385 + */
1.3386 +static void decl_enum(int vclass)
1.3387 +{
1.3388 + char enumname[sNAMEMAX+1],constname[sNAMEMAX+1];
1.3389 + cell val,value,size;
1.3390 + char *str;
1.3391 + int tag,explicittag;
1.3392 + cell increment,multiplier;
1.3393 + constvalue *enumroot;
1.3394 + symbol *enumsym;
1.3395 +
1.3396 + /* get an explicit tag, if any (we need to remember whether an explicit
1.3397 + * tag was passed, even if that explicit tag was "_:", so we cannot call
1.3398 + * pc_addtag() here
1.3399 + */
1.3400 + if (lex(&val,&str)==tLABEL) {
1.3401 + tag=pc_addtag(str);
1.3402 + explicittag=TRUE;
1.3403 + } else {
1.3404 + lexpush();
1.3405 + tag=0;
1.3406 + explicittag=FALSE;
1.3407 + } /* if */
1.3408 +
1.3409 + /* get optional enum name (also serves as a tag if no explicit tag was set) */
1.3410 + if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */
1.3411 + strcpy(enumname,str); /* save enum name (last constant) */
1.3412 + if (!explicittag)
1.3413 + tag=pc_addtag(enumname);
1.3414 + } else {
1.3415 + lexpush(); /* analyze again */
1.3416 + enumname[0]='\0';
1.3417 + } /* if */
1.3418 +
1.3419 + /* get increment and multiplier */
1.3420 + increment=1;
1.3421 + multiplier=1;
1.3422 + if (matchtoken('(')) {
1.3423 + if (matchtoken(taADD)) {
1.3424 + constexpr(&increment,NULL,NULL);
1.3425 + } else if (matchtoken(taMULT)) {
1.3426 + constexpr(&multiplier,NULL,NULL);
1.3427 + } else if (matchtoken(taSHL)) {
1.3428 + constexpr(&val,NULL,NULL);
1.3429 + while (val-->0)
1.3430 + multiplier*=2;
1.3431 + } /* if */
1.3432 + needtoken(')');
1.3433 + } /* if */
1.3434 +
1.3435 + if (strlen(enumname)>0) {
1.3436 + /* already create the root symbol, so the fields can have it as their "parent" */
1.3437 + enumsym=add_constant(enumname,0,vclass,tag);
1.3438 + if (enumsym!=NULL)
1.3439 + enumsym->usage |= uENUMROOT;
1.3440 + /* start a new list for the element names */
1.3441 + if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL)
1.3442 + error(123); /* insufficient memory (fatal error) */
1.3443 + memset(enumroot,0,sizeof(constvalue));
1.3444 + } else {
1.3445 + enumsym=NULL;
1.3446 + enumroot=NULL;
1.3447 + } /* if */
1.3448 +
1.3449 + needtoken('{');
1.3450 + /* go through all constants */
1.3451 + value=0; /* default starting value */
1.3452 + do {
1.3453 + int idxtag,fieldtag;
1.3454 + symbol *sym;
1.3455 + if (matchtoken('}')) { /* quick exit if '}' follows ',' */
1.3456 + lexpush();
1.3457 + break;
1.3458 + } /* if */
1.3459 + idxtag=pc_addtag(NULL); /* optional explicit item tag */
1.3460 + if (needtoken(tSYMBOL)) { /* read in (new) token */
1.3461 + tokeninfo(&val,&str); /* get the information */
1.3462 + strcpy(constname,str); /* save symbol name */
1.3463 + } else {
1.3464 + constname[0]='\0';
1.3465 + } /* if */
1.3466 + size=increment; /* default increment of 'val' */
1.3467 + fieldtag=0; /* default field tag */
1.3468 + if (matchtoken('[')) {
1.3469 + constexpr(&size,&fieldtag,NULL); /* get size */
1.3470 + needtoken(']');
1.3471 + } /* if */
1.3472 + /* :TODO: do we need a size modifier here for pc_tag_string? */
1.3473 + if (matchtoken('='))
1.3474 + constexpr(&value,NULL,NULL); /* get value */
1.3475 + /* add_constant() checks whether a variable (global or local) or
1.3476 + * a constant with the same name already exists
1.3477 + */
1.3478 + sym=add_constant(constname,value,vclass,tag);
1.3479 + if (sym==NULL)
1.3480 + continue; /* error message already given */
1.3481 + /* set the item tag and the item size, for use in indexing arrays */
1.3482 + sym->x.tags.index=idxtag;
1.3483 + sym->x.tags.field=fieldtag;
1.3484 + sym->dim.array.length=size;
1.3485 + sym->dim.array.level=0;
1.3486 + sym->dim.array.slength=0;
1.3487 + sym->parent=enumsym;
1.3488 + /* add the constant to a separate list as well */
1.3489 + if (enumroot!=NULL) {
1.3490 + sym->usage |= uENUMFIELD;
1.3491 + append_constval(enumroot,constname,value,tag);
1.3492 + } /* if */
1.3493 + if (multiplier==1)
1.3494 + value+=size;
1.3495 + else
1.3496 + value*=size*multiplier;
1.3497 + } while (matchtoken(','));
1.3498 + needtoken('}'); /* terminates the constant list */
1.3499 + matchtoken(';'); /* eat an optional ; */
1.3500 +
1.3501 + /* set the enum name to the "next" value (typically the last value plus one) */
1.3502 + if (enumsym!=NULL) {
1.3503 + assert((enumsym->usage & uENUMROOT)!=0);
1.3504 + enumsym->addr=value;
1.3505 + /* assign the constant list */
1.3506 + assert(enumroot!=NULL);
1.3507 + enumsym->dim.enumlist=enumroot;
1.3508 + sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */
1.3509 + } /* if */
1.3510 +}
1.3511 +
1.3512 +static int getstates(const char *funcname)
1.3513 +{
1.3514 + char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1];
1.3515 + cell val;
1.3516 + char *str;
1.3517 + constvalue *automaton;
1.3518 + constvalue *state;
1.3519 + int fsa,islabel;
1.3520 + int *list;
1.3521 + int count,listsize,state_id;
1.3522 +
1.3523 + if (!matchtoken('<'))
1.3524 + return 0;
1.3525 + if (matchtoken('>'))
1.3526 + return -1; /* special construct: all other states (fall-back) */
1.3527 +
1.3528 + count=0;
1.3529 + listsize=0;
1.3530 + list=NULL;
1.3531 + fsa=-1;
1.3532 +
1.3533 + do {
1.3534 + if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
1.3535 + break;
1.3536 + tokeninfo(&val,&str);
1.3537 + assert(strlen(str)<sizeof fsaname);
1.3538 + strcpy(fsaname,str); /* assume this is the name of the automaton */
1.3539 + if (islabel || matchtoken(':')) {
1.3540 + /* token is an automaton name, add the name and get a new token */
1.3541 + if (!needtoken(tSYMBOL))
1.3542 + break;
1.3543 + tokeninfo(&val,&str);
1.3544 + assert(strlen(str)<sizeof statename);
1.3545 + strcpy(statename,str);
1.3546 + } else {
1.3547 + /* the token was the state name (part of an anynymous automaton) */
1.3548 + assert(strlen(fsaname)<sizeof statename);
1.3549 + strcpy(statename,fsaname);
1.3550 + fsaname[0]='\0';
1.3551 + } /* if */
1.3552 + if (fsa<0 || fsaname[0]!='\0') {
1.3553 + automaton=automaton_add(fsaname);
1.3554 + assert(automaton!=NULL);
1.3555 + if (fsa>=0 && automaton->index!=fsa)
1.3556 + error(83,funcname); /* multiple automatons for a single function/variable */
1.3557 + fsa=automaton->index;
1.3558 + } /* if */
1.3559 + state=state_add(statename,fsa);
1.3560 + /* add this state to the state combination list (it will be attached to the
1.3561 + * automaton later) */
1.3562 + state_buildlist(&list,&listsize,&count,(int)state->value);
1.3563 + } while (matchtoken(','));
1.3564 + needtoken('>');
1.3565 +
1.3566 + if (count>0) {
1.3567 + assert(automaton!=NULL);
1.3568 + assert(fsa>=0);
1.3569 + state_id=state_addlist(list,count,fsa);
1.3570 + assert(state_id>0);
1.3571 + } else {
1.3572 + /* error is already given */
1.3573 + state_id=0;
1.3574 + } /* if */
1.3575 + if (list!=NULL)
1.3576 + free(list);
1.3577 +
1.3578 + return state_id;
1.3579 +}
1.3580 +
1.3581 +static void attachstatelist(symbol *sym, int state_id)
1.3582 +{
1.3583 + assert(sym!=NULL);
1.3584 + if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0))
1.3585 + error(21,sym->name); /* function already defined, either without states or the current definition has no states */
1.3586 +
1.3587 + if (state_id!=0) {
1.3588 + /* add the state list id */
1.3589 + constvalue *stateptr;
1.3590 + if (sym->states==NULL) {
1.3591 + if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL)
1.3592 + error(123); /* insufficient memory (fatal error) */
1.3593 + memset(sym->states,0,sizeof(constvalue));
1.3594 + } /* if */
1.3595 + /* see whether the id already exists (add new state only if it does not
1.3596 + * yet exist
1.3597 + */
1.3598 + assert(sym->states!=NULL);
1.3599 + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next)
1.3600 + /* nothing */;
1.3601 + assert(state_id<=SHRT_MAX);
1.3602 + if (stateptr==NULL)
1.3603 + append_constval(sym->states,"",code_idx,(short)state_id);
1.3604 + else if (stateptr->value==0)
1.3605 + stateptr->value=code_idx;
1.3606 + else
1.3607 + error(84,sym->name);
1.3608 + /* also check for another conflicting situation: a fallback function
1.3609 + * without any states
1.3610 + */
1.3611 + if (state_id==-1 && sc_status!=statFIRST) {
1.3612 + /* in the second round, all states should have been accumulated */
1.3613 + assert(sym->states!=NULL);
1.3614 + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next)
1.3615 + /* nothing */;
1.3616 + if (stateptr==NULL)
1.3617 + error(85,sym->name); /* no states are defined for this function */
1.3618 + } /* if */
1.3619 + } /* if */
1.3620 +}
1.3621 +
1.3622 +/*
1.3623 + * Finds a function in the global symbol table or creates a new entry.
1.3624 + * It does some basic processing and error checking.
1.3625 + */
1.3626 +SC_FUNC symbol *fetchfunc(char *name,int tag)
1.3627 +{
1.3628 + symbol *sym;
1.3629 +
1.3630 + if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */
1.3631 + if (sym->ident!=iFUNCTN) {
1.3632 + error(21,name); /* yes, but not as a function */
1.3633 + return NULL; /* make sure the old symbol is not damaged */
1.3634 + } else if ((sym->usage & uNATIVE)!=0) {
1.3635 + error(21,name); /* yes, and it is a native */
1.3636 + } /* if */
1.3637 + assert(sym->vclass==sGLOBAL);
1.3638 + if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag)
1.3639 + error(25); /* mismatch from earlier prototype */
1.3640 + if ((sym->usage & uDEFINE)==0) {
1.3641 + /* as long as the function stays undefined, update the address and the tag */
1.3642 + if (sym->states==NULL)
1.3643 + sym->addr=code_idx;
1.3644 + sym->tag=tag;
1.3645 + } /* if */
1.3646 + } else {
1.3647 + /* don't set the "uDEFINE" flag; it may be a prototype */
1.3648 + sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0);
1.3649 + assert(sym!=NULL); /* fatal error 103 must be given on error */
1.3650 + /* assume no arguments */
1.3651 + sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo));
1.3652 + sym->dim.arglist[0].ident=0;
1.3653 + /* set library ID to NULL (only for native functions) */
1.3654 + sym->x.lib=NULL;
1.3655 + /* set the required stack size to zero (only for non-native functions) */
1.3656 + sym->x.stacksize=1; /* 1 for PROC opcode */
1.3657 + } /* if */
1.3658 + if (pc_deprecate!=NULL) {
1.3659 + assert(sym!=NULL);
1.3660 + sym->flags|=flgDEPRECATED;
1.3661 + if (sc_status==statWRITE) {
1.3662 + if (sym->documentation!=NULL) {
1.3663 + free(sym->documentation);
1.3664 + sym->documentation=NULL;
1.3665 + } /* if */
1.3666 + sym->documentation=pc_deprecate;
1.3667 + } else {
1.3668 + free(pc_deprecate);
1.3669 + } /* if */
1.3670 + pc_deprecate=NULL;
1.3671 + } /* if */
1.3672 +
1.3673 + return sym;
1.3674 +}
1.3675 +
1.3676 +/* This routine adds symbolic information for each argument.
1.3677 + */
1.3678 +static void define_args(void)
1.3679 +{
1.3680 + symbol *sym;
1.3681 +
1.3682 + /* At this point, no local variables have been declared. All
1.3683 + * local symbols are function arguments.
1.3684 + */
1.3685 + sym=loctab.next;
1.3686 + while (sym!=NULL) {
1.3687 + assert(sym->ident!=iLABEL);
1.3688 + assert(sym->vclass==sLOCAL);
1.3689 + markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */
1.3690 + sym=sym->next;
1.3691 + } /* while */
1.3692 +}
1.3693 +
1.3694 +static int operatorname(char *name)
1.3695 +{
1.3696 + int opertok;
1.3697 + char *str;
1.3698 + cell val;
1.3699 +
1.3700 + assert(name!=NULL);
1.3701 +
1.3702 + /* check the operator */
1.3703 + opertok=lex(&val,&str);
1.3704 + switch (opertok) {
1.3705 + case '+':
1.3706 + case '-':
1.3707 + case '*':
1.3708 + case '/':
1.3709 + case '%':
1.3710 + case '>':
1.3711 + case '<':
1.3712 + case '!':
1.3713 + case '~':
1.3714 + case '=':
1.3715 + name[0]=(char)opertok;
1.3716 + name[1]='\0';
1.3717 + break;
1.3718 + case tINC:
1.3719 + strcpy(name,"++");
1.3720 + break;
1.3721 + case tDEC:
1.3722 + strcpy(name,"--");
1.3723 + break;
1.3724 + case tlEQ:
1.3725 + strcpy(name,"==");
1.3726 + break;
1.3727 + case tlNE:
1.3728 + strcpy(name,"!=");
1.3729 + break;
1.3730 + case tlLE:
1.3731 + strcpy(name,"<=");
1.3732 + break;
1.3733 + case tlGE:
1.3734 + strcpy(name,">=");
1.3735 + break;
1.3736 + default:
1.3737 + name[0]='\0';
1.3738 + error(7); /* operator cannot be redefined (or bad operator name) */
1.3739 + return 0;
1.3740 + } /* switch */
1.3741 +
1.3742 + return opertok;
1.3743 +}
1.3744 +
1.3745 +static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag)
1.3746 +{
1.3747 + int tags[2]={0,0};
1.3748 + int count=0;
1.3749 + arginfo *arg;
1.3750 + char tmpname[sNAMEMAX+1];
1.3751 + symbol *oldsym;
1.3752 +
1.3753 + if (opertok==0)
1.3754 + return TRUE;
1.3755 +
1.3756 + assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL);
1.3757 + /* count arguments and save (first two) tags */
1.3758 + while (arg=&sym->dim.arglist[count], arg->ident!=0) {
1.3759 + if (count<2) {
1.3760 + if (arg->numtags>1)
1.3761 + error(65,count+1); /* function argument may only have a single tag */
1.3762 + else if (arg->numtags==1)
1.3763 + tags[count]=arg->tags[0];
1.3764 + } /* if */
1.3765 + if (opertok=='~' && count==0) {
1.3766 + if (arg->ident!=iREFARRAY)
1.3767 + error(73,arg->name);/* must be an array argument */
1.3768 + } else {
1.3769 + //if (arg->ident!=iVARIABLE)
1.3770 + //error(66,arg->name);/* must be non-reference argument */
1.3771 + } /* if */
1.3772 + if (arg->hasdefault)
1.3773 + error(59,arg->name); /* arguments of an operator may not have a default value */
1.3774 + count++;
1.3775 + } /* while */
1.3776 +
1.3777 + /* for '!', '++' and '--', count must be 1
1.3778 + * for '-', count may be 1 or 2
1.3779 + * for '=', count must be 1, and the resulttag is also important
1.3780 + * for all other (binary) operators and the special '~' operator, count must be 2
1.3781 + */
1.3782 + switch (opertok) {
1.3783 + case '!':
1.3784 + case '=':
1.3785 + case tINC:
1.3786 + case tDEC:
1.3787 + if (count!=1)
1.3788 + error(62); /* number or placement of the operands does not fit the operator */
1.3789 + break;
1.3790 + case '-':
1.3791 + if (count!=1 && count!=2)
1.3792 + error(62); /* number or placement of the operands does not fit the operator */
1.3793 + break;
1.3794 + default:
1.3795 + if (count!=2)
1.3796 + error(62); /* number or placement of the operands does not fit the operator */
1.3797 + } /* switch */
1.3798 +
1.3799 + if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0))
1.3800 + error(64); /* cannot change predefined operators */
1.3801 +
1.3802 + /* change the operator name */
1.3803 + assert(strlen(opername)>0);
1.3804 + operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag);
1.3805 + if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) {
1.3806 + int i;
1.3807 + if ((oldsym->usage & uDEFINE)!=0) {
1.3808 + char errname[2*sNAMEMAX+16];
1.3809 + funcdisplayname(errname,tmpname);
1.3810 + error(21,errname); /* symbol already defined */
1.3811 + } /* if */
1.3812 + sym->usage|=oldsym->usage; /* copy flags from the previous definition */
1.3813 + for (i=0; i<oldsym->numrefers; i++)
1.3814 + if (oldsym->refer[i]!=NULL)
1.3815 + refer_symbol(sym,oldsym->refer[i]);
1.3816 + delete_symbol(&glbtab,oldsym);
1.3817 + } /* if */
1.3818 + strcpy(sym->name,tmpname);
1.3819 + sym->hash=namehash(sym->name);/* calculate new hash */
1.3820 +
1.3821 + /* operators should return a value, except the '~' operator */
1.3822 + if (opertok!='~')
1.3823 + sym->usage |= uRETVALUE;
1.3824 +
1.3825 + return TRUE;
1.3826 +}
1.3827 +
1.3828 +static int check_operatortag(int opertok,int resulttag,char *opername)
1.3829 +{
1.3830 + assert(opername!=NULL && strlen(opername)>0);
1.3831 + switch (opertok) {
1.3832 + case '!':
1.3833 + case '<':
1.3834 + case '>':
1.3835 + case tlEQ:
1.3836 + case tlNE:
1.3837 + case tlLE:
1.3838 + case tlGE:
1.3839 + if (resulttag!=pc_addtag("bool")) {
1.3840 + error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */
1.3841 + return FALSE;
1.3842 + } /* if */
1.3843 + break;
1.3844 + case '~':
1.3845 + if (resulttag!=0) {
1.3846 + error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */
1.3847 + return FALSE;
1.3848 + } /* if */
1.3849 + break;
1.3850 + } /* switch */
1.3851 + return TRUE;
1.3852 +}
1.3853 +
1.3854 +static char *tag2str(char *dest,int tag)
1.3855 +{
1.3856 + tag &= TAGMASK;
1.3857 + assert(tag>=0);
1.3858 + sprintf(dest,"0%x",tag);
1.3859 + return isdigit(dest[1]) ? &dest[1] : dest;
1.3860 +}
1.3861 +
1.3862 +SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag)
1.3863 +{
1.3864 + char tagstr1[10], tagstr2[10];
1.3865 + int opertok;
1.3866 +
1.3867 + assert(numtags>=1 && numtags<=2);
1.3868 + opertok= (opername[1]=='\0') ? opername[0] : 0;
1.3869 + if (opertok=='=')
1.3870 + sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1));
1.3871 + else if (numtags==1 || opertok=='~')
1.3872 + sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1));
1.3873 + else
1.3874 + sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2));
1.3875 + return symname;
1.3876 +}
1.3877 +
1.3878 +static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname)
1.3879 +{
1.3880 + char *ptr,*name;
1.3881 + int unary;
1.3882 +
1.3883 + /* tags are only positive, so if the function name starts with a '-',
1.3884 + * the operator is an unary '-' or '--' operator.
1.3885 + */
1.3886 + if (*fname=='-') {
1.3887 + *tag1=0;
1.3888 + unary=TRUE;
1.3889 + ptr=fname;
1.3890 + } else {
1.3891 + *tag1=(int)strtol(fname,&ptr,16);
1.3892 + unary= ptr==fname; /* unary operator if it doesn't start with a tag name */
1.3893 + } /* if */
1.3894 + assert(!unary || *tag1==0);
1.3895 + assert(*ptr!='\0');
1.3896 + for (name=opname; !isdigit(*ptr); )
1.3897 + *name++ = *ptr++;
1.3898 + *name='\0';
1.3899 + *tag2=(int)strtol(ptr,NULL,16);
1.3900 + return unary;
1.3901 +}
1.3902 +
1.3903 +constvalue *find_tag_byval(int tag)
1.3904 +{
1.3905 + constvalue *tagsym;
1.3906 + tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG);
1.3907 + if (tagsym==NULL)
1.3908 + tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);
1.3909 + return tagsym;
1.3910 +}
1.3911 +
1.3912 +SC_FUNC char *funcdisplayname(char *dest,char *funcname)
1.3913 +{
1.3914 + int tags[2];
1.3915 + char opname[10];
1.3916 + constvalue *tagsym[2];
1.3917 + int unary;
1.3918 +
1.3919 + if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') {
1.3920 + if (dest!=funcname)
1.3921 + strcpy(dest,funcname);
1.3922 + return dest;
1.3923 + } /* if */
1.3924 +
1.3925 + unary=parse_funcname(funcname,&tags[0],&tags[1],opname);
1.3926 + tagsym[1]=find_tag_byval(tags[1]);
1.3927 + assert(tagsym[1]!=NULL);
1.3928 + if (unary) {
1.3929 + sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name);
1.3930 + } else {
1.3931 + tagsym[0]=find_tag_byval(tags[0]);
1.3932 + assert(tagsym[0]!=NULL);
1.3933 + /* special case: the assignment operator has the return value as the 2nd tag */
1.3934 + if (opname[0]=='=' && opname[1]=='\0')
1.3935 + sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name);
1.3936 + else
1.3937 + sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name);
1.3938 + } /* if */
1.3939 + return dest;
1.3940 +}
1.3941 +
1.3942 +static void funcstub(int fnative)
1.3943 +{
1.3944 + int tok,tag,fpublic;
1.3945 + char *str;
1.3946 + cell val,size;
1.3947 + char symbolname[sNAMEMAX+1];
1.3948 + int idxtag[sDIMEN_MAX];
1.3949 + int dim[sDIMEN_MAX];
1.3950 + int numdim;
1.3951 + symbol *sym,*sub;
1.3952 + int opertok;
1.3953 +
1.3954 + opertok=0;
1.3955 + lastst=0;
1.3956 + litidx=0; /* clear the literal pool */
1.3957 + assert(loctab.next==NULL); /* local symbol table should be empty */
1.3958 +
1.3959 + tag=pc_addtag(NULL); /* get the tag of the return value */
1.3960 + numdim=0;
1.3961 + while (matchtoken('[')) {
1.3962 + /* the function returns an array, get this tag for the index and the array
1.3963 + * dimensions
1.3964 + */
1.3965 + if (numdim == sDIMEN_MAX) {
1.3966 + error(53); /* exceeding maximum number of dimensions */
1.3967 + return;
1.3968 + } /* if */
1.3969 + size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
1.3970 + if (size==0)
1.3971 + error(9); /* invalid array size */
1.3972 + #if INT_MAX < LONG_MAX
1.3973 + if (size > INT_MAX)
1.3974 + error(125); /* overflow, exceeding capacity */
1.3975 + #endif
1.3976 + dim[numdim++]=(int)size;
1.3977 + } /* while */
1.3978 +
1.3979 + if (tag == pc_tag_string && numdim && dim[numdim-1])
1.3980 + dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
1.3981 +
1.3982 + tok=lex(&val,&str);
1.3983 + fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR);
1.3984 + if (fnative) {
1.3985 + if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR)
1.3986 + error(42); /* invalid combination of class specifiers */
1.3987 + } else {
1.3988 + if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC)
1.3989 + tok=lex(&val,&str);
1.3990 + } /* if */
1.3991 +
1.3992 + if (tok==tOPERATOR) {
1.3993 + opertok=operatorname(symbolname);
1.3994 + if (opertok==0)
1.3995 + return; /* error message already given */
1.3996 + check_operatortag(opertok,tag,symbolname);
1.3997 + } else {
1.3998 + if (tok!=tSYMBOL && freading) {
1.3999 + error(10); /* illegal function or declaration */
1.4000 + return;
1.4001 + } /* if */
1.4002 + strcpy(symbolname,str);
1.4003 + } /* if */
1.4004 + needtoken('('); /* only functions may be native/forward */
1.4005 +
1.4006 + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
1.4007 + if (sym==NULL)
1.4008 + return;
1.4009 + if (fnative) {
1.4010 + sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
1.4011 + sym->x.lib=curlibrary;
1.4012 + } else if (fpublic) {
1.4013 + sym->usage|=uPUBLIC;
1.4014 + } /* if */
1.4015 + sym->usage|=uFORWARD;
1.4016 +
1.4017 + declargs(sym,FALSE);
1.4018 + /* "declargs()" found the ")" */
1.4019 + sc_attachdocumentation(sym); /* attach any documenation to the function */
1.4020 + if (!operatoradjust(opertok,sym,symbolname,tag))
1.4021 + sym->usage &= ~uDEFINE;
1.4022 +
1.4023 + if (getstates(symbolname)!=0) {
1.4024 + if (fnative || opertok!=0)
1.4025 + error(82); /* native functions and operators may not have states */
1.4026 + else
1.4027 + error(231); /* ignoring state specifications on forward declarations */
1.4028 + } /* if */
1.4029 +
1.4030 + /* for a native operator, also need to specify an "exported" function name;
1.4031 + * for a native function, this is optional
1.4032 + */
1.4033 + if (fnative) {
1.4034 + if (opertok!=0) {
1.4035 + needtoken('=');
1.4036 + lexpush(); /* push back, for matchtoken() to retrieve again */
1.4037 + } /* if */
1.4038 + if (matchtoken('=')) {
1.4039 + /* allow number or symbol */
1.4040 + if (matchtoken(tSYMBOL)) {
1.4041 + tokeninfo(&val,&str);
1.4042 + insert_alias(sym->name,str);
1.4043 + } else {
1.4044 + constexpr(&val,NULL,NULL);
1.4045 + sym->addr=val;
1.4046 + /* At the moment, I have assumed that this syntax is only valid if
1.4047 + * val < 0. To properly mix "normal" native functions and indexed
1.4048 + * native functions, one should use negative indices anyway.
1.4049 + * Special code for a negative index in sym->addr exists in SC4.C
1.4050 + * (ffcall()) and in SC6.C (the loops for counting the number of native
1.4051 + * variables and for writing them).
1.4052 + */
1.4053 + } /* if */
1.4054 + } /* if */
1.4055 + } /* if */
1.4056 + needtoken(tTERM);
1.4057 +
1.4058 + /* attach the array to the function symbol */
1.4059 + if (numdim>0) {
1.4060 + assert(sym!=NULL);
1.4061 + sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag);
1.4062 + sub->parent=sym;
1.4063 + } /* if */
1.4064 +
1.4065 + litidx=0; /* clear the literal pool */
1.4066 + delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
1.4067 +}
1.4068 +
1.4069 +/* newfunc - begin a function
1.4070 + *
1.4071 + * This routine is called from "parse" and tries to make a function
1.4072 + * out of the following text
1.4073 + *
1.4074 + * Global references: funcstatus,lastst,litidx
1.4075 + * rettype (altered)
1.4076 + * curfunc (altered)
1.4077 + * declared (altered)
1.4078 + * glb_declared (altered)
1.4079 + * sc_alignnext (altered)
1.4080 + */
1.4081 +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock)
1.4082 +{
1.4083 + symbol *sym;
1.4084 + int argcnt,tok,tag,funcline;
1.4085 + int opertok,opererror;
1.4086 + char symbolname[sNAMEMAX+1];
1.4087 + char *str;
1.4088 + cell val,cidx,glbdecl;
1.4089 + short filenum;
1.4090 + int state_id;
1.4091 +
1.4092 + assert(litidx==0); /* literal queue should be empty */
1.4093 + litidx=0; /* clear the literal pool (should already be empty) */
1.4094 + opertok=0;
1.4095 + lastst=0; /* no statement yet */
1.4096 + cidx=0; /* just to avoid compiler warnings */
1.4097 + glbdecl=0;
1.4098 + assert(loctab.next==NULL); /* local symbol table should be empty */
1.4099 + filenum=fcurrent; /* save file number at the start of the declaration */
1.4100 +
1.4101 + if (firstname!=NULL) {
1.4102 + assert(strlen(firstname)<=sNAMEMAX);
1.4103 + strcpy(symbolname,firstname); /* save symbol name */
1.4104 + tag=firsttag;
1.4105 + } else {
1.4106 + tag= (firsttag>=0) ? firsttag : pc_addtag(NULL);
1.4107 + tok=lex(&val,&str);
1.4108 + assert(!fpublic);
1.4109 + if (tok==tNATIVE || tok==tPUBLIC && stock)
1.4110 + error(42); /* invalid combination of class specifiers */
1.4111 + if (tok==tOPERATOR) {
1.4112 + opertok=operatorname(symbolname);
1.4113 + if (opertok==0)
1.4114 + return TRUE; /* error message already given */
1.4115 + check_operatortag(opertok,tag,symbolname);
1.4116 + } else {
1.4117 + if (tok!=tSYMBOL && freading) {
1.4118 + error(20,str); /* invalid symbol name */
1.4119 + return FALSE;
1.4120 + } /* if */
1.4121 + assert(strlen(str)<=sNAMEMAX);
1.4122 + strcpy(symbolname,str);
1.4123 + } /* if */
1.4124 + } /* if */
1.4125 + /* check whether this is a function or a variable declaration */
1.4126 + if (!matchtoken('('))
1.4127 + return FALSE;
1.4128 + /* so it is a function, proceed */
1.4129 + funcline=fline; /* save line at which the function is defined */
1.4130 + if (symbolname[0]==PUBLIC_CHAR) {
1.4131 + fpublic=TRUE; /* implicitly public function */
1.4132 + if (stock)
1.4133 + error(42); /* invalid combination of class specifiers */
1.4134 + } /* if */
1.4135 + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
1.4136 + if (sym==NULL || (sym->usage & uNATIVE)!=0)
1.4137 + return TRUE; /* it was recognized as a function declaration, but not as a valid one */
1.4138 + if (fpublic)
1.4139 + sym->usage|=uPUBLIC;
1.4140 + if (fstatic)
1.4141 + sym->fnumber=filenum;
1.4142 + /* if the function was used before being declared, and it has a tag for the
1.4143 + * result, add a third pass (as second "skimming" parse) because the function
1.4144 + * result may have been used with user-defined operators, which have now
1.4145 + * been incorrectly flagged (as the return tag was unknown at the time of
1.4146 + * the call)
1.4147 + */
1.4148 + if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) {
1.4149 + int curstatus=sc_status;
1.4150 + sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */
1.4151 +#if 0 /* SourceMod - silly, should be removed in first pass, so removed */
1.4152 + error(208);
1.4153 +#endif
1.4154 + sc_status=curstatus;
1.4155 + sc_reparse=TRUE; /* must add another pass to "initial scan" phase */
1.4156 + } /* if */
1.4157 +#if 0 /* Not used for SourceMod */
1.4158 + /* we want public functions to be explicitly prototyped, as they are called
1.4159 + * from the outside
1.4160 + */
1.4161 + if (fpublic && (sym->usage & uFORWARD)==0)
1.4162 + error(235,symbolname);
1.4163 +#endif
1.4164 + /* declare all arguments */
1.4165 + argcnt=declargs(sym,TRUE);
1.4166 + opererror=!operatoradjust(opertok,sym,symbolname,tag);
1.4167 + if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) {
1.4168 + if (argcnt>0)
1.4169 + error(5); /* "main()" and "entry()" functions may not have any arguments */
1.4170 + sym->usage|=uREAD; /* "main()" is the program's entry point: always used */
1.4171 + } /* if */
1.4172 + state_id=getstates(symbolname);
1.4173 + if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0))
1.4174 + error(82); /* operators may not have states, main() may neither */
1.4175 + attachstatelist(sym,state_id);
1.4176 + /* "declargs()" found the ")"; if a ";" appears after this, it was a
1.4177 + * prototype */
1.4178 + if (matchtoken(';')) {
1.4179 + if (sym->usage & uPUBLIC)
1.4180 + error(10);
1.4181 + sym->usage|=uFORWARD;
1.4182 + if (!sc_needsemicolon)
1.4183 + error(10); /* old style prototypes used with optional semicolumns */
1.4184 + delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */
1.4185 + return TRUE;
1.4186 + } /* if */
1.4187 + /* so it is not a prototype, proceed */
1.4188 + /* if this is a function that is not referred to (this can only be detected
1.4189 + * in the second stage), shut code generation off */
1.4190 + if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) {
1.4191 + sc_status=statSKIP;
1.4192 + cidx=code_idx;
1.4193 + glbdecl=glb_declared;
1.4194 + } /* if */
1.4195 + if ((sym->flags & flgDEPRECATED) != 0 && (sym->usage & uSTOCK) == 0) {
1.4196 + char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
1.4197 + error(234,symbolname,ptr); /* deprecated (probably a public function) */
1.4198 + } /* if */
1.4199 + begcseg();
1.4200 + sym->usage|=uDEFINE; /* set the definition flag */
1.4201 + if (stock)
1.4202 + sym->usage|=uSTOCK;
1.4203 + if (opertok!=0 && opererror)
1.4204 + sym->usage &= ~uDEFINE;
1.4205 + /* if the function has states, dump the label to the start of the function */
1.4206 + if (state_id!=0) {
1.4207 + constvalue *ptr=sym->states->next;
1.4208 + while (ptr!=NULL) {
1.4209 + assert(sc_status!=statWRITE || strlen(ptr->name)>0);
1.4210 + if (ptr->index==state_id) {
1.4211 + setlabel((int)strtol(ptr->name,NULL,16));
1.4212 + break;
1.4213 + } /* if */
1.4214 + ptr=ptr->next;
1.4215 + } /* while */
1.4216 + } /* if */
1.4217 + startfunc(sym->name); /* creates stack frame */
1.4218 + insert_dbgline(funcline);
1.4219 + setline(FALSE);
1.4220 + if (sc_alignnext) {
1.4221 + alignframe(sc_dataalign);
1.4222 + sc_alignnext=FALSE;
1.4223 + } /* if */
1.4224 + declared=0; /* number of local cells */
1.4225 + resetstacklist();
1.4226 + resetheaplist();
1.4227 + rettype=(sym->usage & uRETVALUE); /* set "return type" variable */
1.4228 + curfunc=sym;
1.4229 + define_args(); /* add the symbolic info for the function arguments */
1.4230 + #if !defined SC_LIGHT
1.4231 + if (matchtoken('{')) {
1.4232 + lexpush();
1.4233 + } else {
1.4234 + /* Insert a separator so that comments following the statement will not
1.4235 + * be attached to this function; they should be attached to the next
1.4236 + * function. This is not a problem for functions having a compound block,
1.4237 + * because the closing brace is an explicit "end token" for the function.
1.4238 + * With single statement functions, the preprocessor may overread the
1.4239 + * source code before the parser determines an "end of statement".
1.4240 + */
1.4241 + insert_docstring_separator();
1.4242 + } /* if */
1.4243 + #endif
1.4244 + sc_curstates=state_id;/* set state id, for accessing global state variables */
1.4245 + statement(NULL,FALSE);
1.4246 + sc_curstates=0;
1.4247 + if ((rettype & uRETVALUE)!=0)
1.4248 + sym->usage|=uRETVALUE;
1.4249 + if (declared!=0) {
1.4250 + /* This happens only in a very special (and useless) case, where a function
1.4251 + * has only a single statement in its body (no compound block) and that
1.4252 + * statement declares a new variable
1.4253 + */
1.4254 + popstacklist(1);
1.4255 + declared=0;
1.4256 + } /* if */
1.4257 + if ((lastst!=tRETURN) && (lastst!=tGOTO)){
1.4258 + ldconst(0,sPRI);
1.4259 + ffret(strcmp(sym->name,uENTRYFUNC)!=0);
1.4260 + if ((sym->usage & uRETVALUE)!=0) {
1.4261 + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
1.4262 + funcdisplayname(symname,sym->name);
1.4263 + error(209,symname); /* function should return a value */
1.4264 + } /* if */
1.4265 + } /* if */
1.4266 + endfunc();
1.4267 + sym->codeaddr=code_idx;
1.4268 + sc_attachdocumentation(sym); /* attach collected documenation to the function */
1.4269 + if (litidx) { /* if there are literals defined */
1.4270 + glb_declared+=litidx;
1.4271 + begdseg(); /* flip to DATA segment */
1.4272 + dumplits(); /* dump literal strings */
1.4273 + litidx=0;
1.4274 + } /* if */
1.4275 + testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */
1.4276 + delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */
1.4277 + assert(loctab.next==NULL);
1.4278 + curfunc=NULL;
1.4279 + if (sc_status==statSKIP) {
1.4280 + sc_status=statWRITE;
1.4281 + code_idx=cidx;
1.4282 + glb_declared=glbdecl;
1.4283 + } /* if */
1.4284 + return TRUE;
1.4285 +}
1.4286 +
1.4287 +static int argcompare(arginfo *a1,arginfo *a2)
1.4288 +{
1.4289 + int result,level,i;
1.4290 +
1.4291 +#if 0 /* SourceMod uses case insensitive args for forwards */
1.4292 + result= strcmp(a1->name,a2->name)==0; /* name */
1.4293 +#else
1.4294 + result=1;
1.4295 +#endif
1.4296 + if (result)
1.4297 + result= a1->ident==a2->ident; /* type/class */
1.4298 + if (result)
1.4299 + result= a1->usage==a2->usage; /* "const" flag */
1.4300 + if (result)
1.4301 + result= a1->numtags==a2->numtags; /* tags (number and names) */
1.4302 + for (i=0; result && i<a1->numtags; i++)
1.4303 + result= a1->tags[i]==a2->tags[i];
1.4304 + if (result)
1.4305 + result= a1->numdim==a2->numdim; /* array dimensions & index tags */
1.4306 + for (level=0; result && level<a1->numdim; level++)
1.4307 + result= a1->dim[level]==a2->dim[level];
1.4308 + for (level=0; result && level<a1->numdim; level++)
1.4309 + result= a1->idxtag[level]==a2->idxtag[level];
1.4310 + if (result)
1.4311 + result= a1->hasdefault==a2->hasdefault; /* availability of default value */
1.4312 + if (a1->hasdefault) {
1.4313 + if (a1->ident==iREFARRAY) {
1.4314 + if (result)
1.4315 + result= a1->defvalue.array.size==a2->defvalue.array.size;
1.4316 + if (result)
1.4317 + result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize;
1.4318 + /* ??? should also check contents of the default array (these troubles
1.4319 + * go away in a 2-pass compiler that forbids double declarations, but
1.4320 + * Pawn currently does not forbid them) */
1.4321 + } else {
1.4322 + if (result) {
1.4323 + if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0 || (a1->hasdefault & uCOUNTOF)!=0)
1.4324 + result= a1->hasdefault==a2->hasdefault
1.4325 + && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0
1.4326 + && a1->defvalue.size.level==a2->defvalue.size.level;
1.4327 + else
1.4328 + result= a1->defvalue.val==a2->defvalue.val;
1.4329 + } /* if */
1.4330 + } /* if */
1.4331 + if (result)
1.4332 + result= a1->defvalue_tag==a2->defvalue_tag;
1.4333 + } /* if */
1.4334 + return result;
1.4335 +}
1.4336 +
1.4337 +/* declargs()
1.4338 + *
1.4339 + * This routine adds an entry in the local symbol table for each argument
1.4340 + * found in the argument list. It returns the number of arguments.
1.4341 + */
1.4342 +static int declargs(symbol *sym,int chkshadow)
1.4343 +{
1.4344 + #define MAXTAGS 16
1.4345 + char *ptr;
1.4346 + int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags;
1.4347 + cell val;
1.4348 + arginfo arg, *arglist;
1.4349 + char name[sNAMEMAX+1];
1.4350 + int ident,fpublic,fconst;
1.4351 + int idx;
1.4352 +
1.4353 + /* if the function is already defined earlier, get the number of arguments
1.4354 + * of the existing definition
1.4355 + */
1.4356 + oldargcnt=0;
1.4357 + if ((sym->usage & uPROTOTYPED)!=0)
1.4358 + while (sym->dim.arglist[oldargcnt].ident!=0)
1.4359 + oldargcnt++;
1.4360 + argcnt=0; /* zero aruments up to now */
1.4361 + ident=iVARIABLE;
1.4362 + numtags=0;
1.4363 + fconst=FALSE;
1.4364 + fpublic= (sym->usage & (uPUBLIC|uSTOCK))!=0;
1.4365 + /* the '(' parantheses has already been parsed */
1.4366 + if (!matchtoken(')')){
1.4367 + do { /* there are arguments; process them */
1.4368 + /* any legal name increases argument count (and stack offset) */
1.4369 + tok=lex(&val,&ptr);
1.4370 + switch (tok) {
1.4371 + case 0:
1.4372 + /* nothing */
1.4373 + break;
1.4374 + case '&':
1.4375 + if (ident!=iVARIABLE || numtags>0)
1.4376 + error(1,"-identifier-","&");
1.4377 + ident=iREFERENCE;
1.4378 + break;
1.4379 + case tCONST:
1.4380 + if (ident!=iVARIABLE || numtags>0)
1.4381 + error(1,"-identifier-","const");
1.4382 + fconst=TRUE;
1.4383 + break;
1.4384 + case tLABEL:
1.4385 + if (numtags>0)
1.4386 + error(1,"-identifier-","-tagname-");
1.4387 + tags[0]=pc_addtag(ptr);
1.4388 + numtags=1;
1.4389 + break;
1.4390 + case '{':
1.4391 + if (numtags>0)
1.4392 + error(1,"-identifier-","-tagname-");
1.4393 + numtags=0;
1.4394 + while (numtags<MAXTAGS) {
1.4395 + if (!matchtoken('_') && !needtoken(tSYMBOL))
1.4396 + break;
1.4397 + tokeninfo(&val,&ptr);
1.4398 + tags[numtags++]=pc_addtag(ptr);
1.4399 + if (matchtoken('}'))
1.4400 + break;
1.4401 + needtoken(',');
1.4402 + } /* for */
1.4403 + needtoken(':');
1.4404 + tok=tLABEL; /* for outer loop: flag that we have seen a tagname */
1.4405 + break;
1.4406 + case tSYMBOL:
1.4407 + if (argcnt>=sMAXARGS)
1.4408 + error(45); /* too many function arguments */
1.4409 + strcpy(name,ptr); /* save symbol name */
1.4410 + if (name[0]==PUBLIC_CHAR)
1.4411 + error(56,name); /* function arguments cannot be public */
1.4412 + if (numtags==0)
1.4413 + tags[numtags++]=0; /* default tag */
1.4414 + /* Stack layout:
1.4415 + * base + 0*sizeof(cell) == previous "base"
1.4416 + * base + 1*sizeof(cell) == function return address
1.4417 + * base + 2*sizeof(cell) == number of arguments
1.4418 + * base + 3*sizeof(cell) == first argument of the function
1.4419 + * So the offset of each argument is "(argcnt+3) * sizeof(cell)".
1.4420 + */
1.4421 + doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg);
1.4422 + /* :TODO: fix this so stocks that are func pointers can't have default arguments? */
1.4423 + if ((sym->usage & uPUBLIC) && arg.hasdefault)
1.4424 + error(59,name); /* arguments of a public function may not have a default value */
1.4425 + if ((sym->usage & uPROTOTYPED)==0) {
1.4426 + /* redimension the argument list, add the entry */
1.4427 + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
1.4428 + if (sym->dim.arglist==0)
1.4429 + error(123); /* insufficient memory */
1.4430 + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
1.4431 + sym->dim.arglist[argcnt]=arg;
1.4432 + } else {
1.4433 + /* check the argument with the earlier definition */
1.4434 + if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg))
1.4435 + error(25); /* function definition does not match prototype */
1.4436 + /* may need to free default array argument and the tag list */
1.4437 + if (arg.ident==iREFARRAY && arg.hasdefault)
1.4438 + free(arg.defvalue.array.data);
1.4439 + else if (arg.ident==iVARIABLE
1.4440 + && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0) || (arg.hasdefault & uCOUNTOF)!=0)
1.4441 + free(arg.defvalue.size.symname);
1.4442 + free(arg.tags);
1.4443 + } /* if */
1.4444 + argcnt++;
1.4445 + ident=iVARIABLE;
1.4446 + numtags=0;
1.4447 + fconst=FALSE;
1.4448 + break;
1.4449 + case tELLIPS:
1.4450 + if (ident!=iVARIABLE)
1.4451 + error(10); /* illegal function or declaration */
1.4452 + if (numtags==0)
1.4453 + tags[numtags++]=0; /* default tag */
1.4454 + if ((sym->usage & uPROTOTYPED)==0) {
1.4455 + /* redimension the argument list, add the entry iVARARGS */
1.4456 + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
1.4457 + if (sym->dim.arglist==0)
1.4458 + error(123); /* insufficient memory */
1.4459 + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
1.4460 + sym->dim.arglist[argcnt].ident=iVARARGS;
1.4461 + sym->dim.arglist[argcnt].hasdefault=FALSE;
1.4462 + sym->dim.arglist[argcnt].defvalue.val=0;
1.4463 + sym->dim.arglist[argcnt].defvalue_tag=0;
1.4464 + sym->dim.arglist[argcnt].numtags=numtags;
1.4465 + sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]);
1.4466 + if (sym->dim.arglist[argcnt].tags==NULL)
1.4467 + error(123); /* insufficient memory */
1.4468 + memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]);
1.4469 + } else {
1.4470 + if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS)
1.4471 + error(25); /* function definition does not match prototype */
1.4472 + } /* if */
1.4473 + argcnt++;
1.4474 + break;
1.4475 + default:
1.4476 + error(10); /* illegal function or declaration */
1.4477 + } /* switch */
1.4478 + } while (tok=='&' || tok==tLABEL || tok==tCONST
1.4479 + || tok!=tELLIPS && matchtoken(',')); /* more? */
1.4480 + /* if the next token is not ",", it should be ")" */
1.4481 + needtoken(')');
1.4482 + } /* if */
1.4483 + /* resolve any "sizeof" arguments (now that all arguments are known) */
1.4484 + assert(sym->dim.arglist!=NULL);
1.4485 + arglist=sym->dim.arglist;
1.4486 + for (idx=0; idx<argcnt && arglist[idx].ident!=0; idx++) {
1.4487 + if ((arglist[idx].hasdefault & uSIZEOF)!=0
1.4488 + || (arglist[idx].hasdefault & uTAGOF)!=0
1.4489 + || (arglist[idx].hasdefault & uCOUNTOF)!=0) {
1.4490 + int altidx;
1.4491 + /* Find the argument with the name mentioned after the "sizeof". Note
1.4492 + * that we cannot use findloc here because we need the arginfo struct,
1.4493 + * not the symbol.
1.4494 + */
1.4495 + ptr=arglist[idx].defvalue.size.symname;
1.4496 + assert(ptr!=NULL);
1.4497 + for (altidx=0; altidx<argcnt && strcmp(ptr,arglist[altidx].name)!=0; altidx++)
1.4498 + /* nothing */;
1.4499 + if (altidx>=argcnt) {
1.4500 + error(17,ptr); /* undefined symbol */
1.4501 + } else {
1.4502 + assert(arglist[idx].defvalue.size.symname!=NULL);
1.4503 + /* check the level against the number of dimensions */
1.4504 + if (arglist[idx].defvalue.size.level>0
1.4505 + && arglist[idx].defvalue.size.level>=arglist[altidx].numdim)
1.4506 + error(28,arglist[idx].name); /* invalid subscript */
1.4507 + /* check the type of the argument whose size to take; for a iVARIABLE
1.4508 + * or a iREFERENCE, this is always 1 (so the code is redundant)
1.4509 + */
1.4510 + assert(arglist[altidx].ident!=iVARARGS);
1.4511 + if (arglist[altidx].ident!=iREFARRAY
1.4512 + && (((arglist[idx].hasdefault & uSIZEOF)!=0)
1.4513 + || (arglist[idx].hasdefault & uCOUNTOF)!=0)) {
1.4514 + if ((arglist[idx].hasdefault & uTAGOF)!=0) {
1.4515 + error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */
1.4516 + } else {
1.4517 + assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE);
1.4518 + error(223,ptr); /* redundant sizeof */
1.4519 + } /* if */
1.4520 + } /* if */
1.4521 + } /* if */
1.4522 + } /* if */
1.4523 + } /* for */
1.4524 +
1.4525 + sym->usage|=uPROTOTYPED;
1.4526 + errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
1.4527 + return argcnt;
1.4528 +}
1.4529 +
1.4530 +/* doarg - declare one argument type
1.4531 + *
1.4532 + * this routine is called from "declargs()" and adds an entry in the local
1.4533 + * symbol table for one argument.
1.4534 + *
1.4535 + * "fpublic" indicates whether the function for this argument list is public.
1.4536 + * The arguments themselves are never public.
1.4537 + */
1.4538 +static void doarg(char *name,int ident,int offset,int tags[],int numtags,
1.4539 + int fpublic,int fconst,int chkshadow,arginfo *arg)
1.4540 +{
1.4541 + symbol *argsym;
1.4542 + constvalue *enumroot;
1.4543 + cell size;
1.4544 + int slength=0;
1.4545 +
1.4546 + strcpy(arg->name,name);
1.4547 + arg->hasdefault=FALSE; /* preset (most common case) */
1.4548 + arg->defvalue.val=0; /* clear */
1.4549 + arg->defvalue_tag=0;
1.4550 + arg->numdim=0;
1.4551 + if (matchtoken('[')) {
1.4552 + if (ident==iREFERENCE)
1.4553 + error(67,name); /* illegal declaration ("&name[]" is unsupported) */
1.4554 + do {
1.4555 + if (arg->numdim == sDIMEN_MAX) {
1.4556 + error(53); /* exceeding maximum number of dimensions */
1.4557 + return;
1.4558 + } /* if */
1.4559 + size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */
1.4560 + #if INT_MAX < LONG_MAX
1.4561 + if (size > INT_MAX)
1.4562 + error(125); /* overflow, exceeding capacity */
1.4563 + #endif
1.4564 + arg->dim[arg->numdim]=(int)size;
1.4565 + arg->numdim+=1;
1.4566 + } while (matchtoken('['));
1.4567 + ident=iREFARRAY; /* "reference to array" (is a pointer) */
1.4568 + if (checktag(tags, numtags, pc_tag_string)) {
1.4569 + slength = arg->dim[arg->numdim - 1];
1.4570 + arg->dim[arg->numdim - 1] = (size + sizeof(cell) - 1) / sizeof(cell);
1.4571 + }
1.4572 + if (matchtoken('=')) {
1.4573 + assert(litidx==0); /* at the start of a function, this is reset */
1.4574 + assert(numtags>0);
1.4575 + /* Check if there is a symbol */
1.4576 + if (matchtoken(tSYMBOL)) {
1.4577 + symbol *sym;
1.4578 + char *name;
1.4579 + cell val;
1.4580 + tokeninfo(&val,&name);
1.4581 + if ((sym=findglb(name, sGLOBAL)) == NULL) {
1.4582 + error(17, name); /* undefined symbol */
1.4583 + } else {
1.4584 + arg->hasdefault=TRUE; /* argument as a default value */
1.4585 + memset(&arg->defvalue, 0, sizeof(arg->defvalue));
1.4586 + arg->defvalue.array.data=NULL;
1.4587 + arg->defvalue.array.addr=sym->addr;
1.4588 + arg->defvalue_tag=sym->tag;
1.4589 + if (sc_status==statWRITE && (sym->usage & uREAD)==0) {
1.4590 + markusage(sym, uREAD);
1.4591 + }
1.4592 + }
1.4593 + } else {
1.4594 + initials2(ident,tags[0],&size,arg->dim,arg->numdim,enumroot, 1, 0);
1.4595 + assert(size>=litidx);
1.4596 + /* allocate memory to hold the initial values */
1.4597 + arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell));
1.4598 + if (arg->defvalue.array.data!=NULL) {
1.4599 + int i;
1.4600 + memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell));
1.4601 + arg->hasdefault=TRUE; /* argument has default value */
1.4602 + arg->defvalue.array.size=litidx;
1.4603 + arg->defvalue.array.addr=-1;
1.4604 + /* calulate size to reserve on the heap */
1.4605 + arg->defvalue.array.arraysize=1;
1.4606 + for (i=0; i<arg->numdim; i++)
1.4607 + arg->defvalue.array.arraysize*=arg->dim[i];
1.4608 + if (arg->defvalue.array.arraysize < arg->defvalue.array.size)
1.4609 + arg->defvalue.array.arraysize = arg->defvalue.array.size;
1.4610 + } /* if */
1.4611 + litidx=0; /* reset */
1.4612 + }
1.4613 + } /* if */
1.4614 + } else {
1.4615 + if (matchtoken('=')) {
1.4616 + unsigned char size_tag_token;
1.4617 + assert(ident==iVARIABLE || ident==iREFERENCE);
1.4618 + arg->hasdefault=TRUE; /* argument has a default value */
1.4619 + size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0);
1.4620 + if (size_tag_token==0)
1.4621 + size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0);
1.4622 + if (size_tag_token==0)
1.4623 + size_tag_token=(unsigned char)(matchtoken(tCELLSOF) ? uCOUNTOF : 0);
1.4624 + if (size_tag_token!=0) {
1.4625 + int paranthese;
1.4626 + if (ident==iREFERENCE)
1.4627 + error(66,name); /* argument may not be a reference */
1.4628 + paranthese=0;
1.4629 + while (matchtoken('('))
1.4630 + paranthese++;
1.4631 + if (needtoken(tSYMBOL)) {
1.4632 + /* save the name of the argument whose size id to take */
1.4633 + char *name;
1.4634 + cell val;
1.4635 + tokeninfo(&val,&name);
1.4636 + if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL)
1.4637 + error(123); /* insufficient memory */
1.4638 + arg->defvalue.size.level=0;
1.4639 + if (size_tag_token==uSIZEOF || size_tag_token==uCOUNTOF) {
1.4640 + while (matchtoken('[')) {
1.4641 + arg->defvalue.size.level+=(short)1;
1.4642 + needtoken(']');
1.4643 + } /* while */
1.4644 + } /* if */
1.4645 + if (ident==iVARIABLE) /* make sure we set this only if not a reference */
1.4646 + arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */
1.4647 + } /* if */
1.4648 + while (paranthese--)
1.4649 + needtoken(')');
1.4650 + } else {
1.4651 + constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL);
1.4652 + assert(numtags>0);
1.4653 + if (!matchtag(tags[0],arg->defvalue_tag,TRUE))
1.4654 + error(213); /* tagname mismatch */
1.4655 + } /* if */
1.4656 + } /* if */
1.4657 + } /* if */
1.4658 + arg->ident=(char)ident;
1.4659 + arg->usage=(char)(fconst ? uCONST : 0);
1.4660 + arg->numtags=numtags;
1.4661 + arg->tags=(int*)malloc(numtags*sizeof tags[0]);
1.4662 + if (arg->tags==NULL)
1.4663 + error(123); /* insufficient memory */
1.4664 + memcpy(arg->tags,tags,numtags*sizeof tags[0]);
1.4665 + argsym=findloc(name);
1.4666 + if (argsym!=NULL) {
1.4667 + error(21,name); /* symbol already defined */
1.4668 + } else {
1.4669 + if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN)
1.4670 + error(219,name); /* variable shadows another symbol */
1.4671 + /* add details of type and address */
1.4672 + assert(numtags>0);
1.4673 + argsym=addvariable2(name,offset,ident,sLOCAL,tags[0],
1.4674 + arg->dim,arg->numdim,arg->idxtag,slength);
1.4675 + argsym->compound=0;
1.4676 + if (ident==iREFERENCE)
1.4677 + argsym->usage|=uREAD; /* because references are passed back */
1.4678 + if (fpublic)
1.4679 + argsym->usage|=uREAD; /* arguments of public functions are always "used" */
1.4680 + if (fconst)
1.4681 + argsym->usage|=uCONST;
1.4682 + } /* if */
1.4683 +}
1.4684 +
1.4685 +static int count_referrers(symbol *entry)
1.4686 +{
1.4687 + int i,count;
1.4688 +
1.4689 + count=0;
1.4690 + for (i=0; i<entry->numrefers; i++)
1.4691 + if (entry->refer[i]!=NULL)
1.4692 + count++;
1.4693 + return count;
1.4694 +}
1.4695 +
1.4696 +#if !defined SC_LIGHT
1.4697 +static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue,
1.4698 + char **outer_start,int *outer_length,
1.4699 + char **inner_start,int *inner_length)
1.4700 +{
1.4701 + char *ptr,*inner_end;
1.4702 + int xmltag_len,xmlparam_len,xmlvalue_len;
1.4703 + int match;
1.4704 +
1.4705 + assert(source!=NULL);
1.4706 + assert(xmltag!=NULL);
1.4707 + assert(outer_start!=NULL);
1.4708 + assert(outer_length!=NULL);
1.4709 + assert(inner_start!=NULL);
1.4710 + assert(inner_length!=NULL);
1.4711 +
1.4712 + /* both NULL or both non-NULL */
1.4713 + assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL);
1.4714 +
1.4715 + xmltag_len=strlen(xmltag);
1.4716 + xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0;
1.4717 + xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0;
1.4718 + ptr=source;
1.4719 + /* find an opening '<' */
1.4720 + while ((ptr=strchr(ptr,'<'))!=NULL) {
1.4721 + *outer_start=ptr; /* be optimistic... */
1.4722 + match=FALSE; /* ...and pessimistic at the same time */
1.4723 + ptr++; /* skip '<' */
1.4724 + while (*ptr!='\0' && *ptr<=' ')
1.4725 + ptr++; /* skip white space */
1.4726 + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
1.4727 + /* xml tag found, optionally check the parameter */
1.4728 + ptr+=xmltag_len;
1.4729 + while (*ptr!='\0' && *ptr<=' ')
1.4730 + ptr++; /* skip white space */
1.4731 + if (xmlparam!=NULL) {
1.4732 + if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) {
1.4733 + ptr+=xmlparam_len;
1.4734 + while (*ptr!='\0' && *ptr<=' ')
1.4735 + ptr++; /* skip white space */
1.4736 + if (*ptr=='=') {
1.4737 + ptr++; /* skip '=' */
1.4738 + while (*ptr!='\0' && *ptr<=' ')
1.4739 + ptr++; /* skip white space */
1.4740 + if (*ptr=='"' || *ptr=='\'')
1.4741 + ptr++; /* skip " or ' */
1.4742 + assert(xmlvalue!=NULL);
1.4743 + if (strncmp(ptr,xmlvalue,xmlvalue_len)==0
1.4744 + && (*(ptr+xmlvalue_len)<=' '
1.4745 + || *(ptr+xmlvalue_len)=='>'
1.4746 + || *(ptr+xmlvalue_len)=='"'
1.4747 + || *(ptr+xmlvalue_len)=='\''))
1.4748 + match=TRUE; /* found it */
1.4749 + } /* if */
1.4750 + } /* if */
1.4751 + } else {
1.4752 + match=TRUE; /* don't check the parameter */
1.4753 + } /* if */
1.4754 + } /* if */
1.4755 + if (match) {
1.4756 + /* now find the end of the opening tag */
1.4757 + while (*ptr!='\0' && *ptr!='>')
1.4758 + ptr++;
1.4759 + if (*ptr=='>')
1.4760 + ptr++;
1.4761 + while (*ptr!='\0' && *ptr<=' ')
1.4762 + ptr++; /* skip white space */
1.4763 + *inner_start=ptr;
1.4764 + /* find the start of the closing tag (assume no nesting) */
1.4765 + while ((ptr=strchr(ptr,'<'))!=NULL) {
1.4766 + inner_end=ptr;
1.4767 + ptr++; /* skip '<' */
1.4768 + while (*ptr!='\0' && *ptr<=' ')
1.4769 + ptr++; /* skip white space */
1.4770 + if (*ptr=='/') {
1.4771 + ptr++; /* skip / */
1.4772 + while (*ptr!='\0' && *ptr<=' ')
1.4773 + ptr++; /* skip white space */
1.4774 + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
1.4775 + /* find the end of the closing tag */
1.4776 + while (*ptr!='\0' && *ptr!='>')
1.4777 + ptr++;
1.4778 + if (*ptr=='>')
1.4779 + ptr++;
1.4780 + /* set the lengths of the inner and outer segment */
1.4781 + assert(*inner_start!=NULL);
1.4782 + *inner_length=(int)(inner_end-*inner_start);
1.4783 + assert(*outer_start!=NULL);
1.4784 + *outer_length=(int)(ptr-*outer_start);
1.4785 + break; /* break out of the loop */
1.4786 + } /* if */
1.4787 + } /* if */
1.4788 + } /* while */
1.4789 + return TRUE;
1.4790 + } /* if */
1.4791 + } /* while */
1.4792 + return FALSE; /* not found */
1.4793 +}
1.4794 +
1.4795 +static char *xmlencode(char *dest,char *source)
1.4796 +{
1.4797 + char temp[2*sNAMEMAX+20],*ptr;
1.4798 +
1.4799 + /* replace < by < and such; normally, such a symbol occurs at most once in
1.4800 + * a symbol name (e.g. "operator<")
1.4801 + */
1.4802 + ptr=temp;
1.4803 + while (*source!='\0') {
1.4804 + switch (*source) {
1.4805 + case '<':
1.4806 + strcpy(ptr,"<");
1.4807 + ptr+=4;
1.4808 + break;
1.4809 + case '>':
1.4810 + strcpy(ptr,">");
1.4811 + ptr+=4;
1.4812 + break;
1.4813 + case '&':
1.4814 + strcpy(ptr,"&");
1.4815 + ptr+=5;
1.4816 + break;
1.4817 + default:
1.4818 + *ptr++=*source;
1.4819 + } /* switch */
1.4820 + source++;
1.4821 + } /* while */
1.4822 + *ptr='\0';
1.4823 + strcpy(dest,temp);
1.4824 + return dest;
1.4825 +}
1.4826 +
1.4827 +static void make_report(symbol *root,FILE *log,char *sourcefile)
1.4828 +{
1.4829 + char symname[_MAX_PATH];
1.4830 + int i,arg;
1.4831 + symbol *sym,*ref;
1.4832 + constvalue *tagsym;
1.4833 + constvalue *enumroot;
1.4834 + char *ptr;
1.4835 +
1.4836 + /* adapt the installation directory */
1.4837 + strcpy(symname,sc_rootpath);
1.4838 + #if DIRSEP_CHAR=='\\'
1.4839 + while ((ptr=strchr(symname,':'))!=NULL)
1.4840 + *ptr='|';
1.4841 + while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL)
1.4842 + *ptr='/';
1.4843 + #endif
1.4844 +
1.4845 + /* the XML header */
1.4846 + fprintf(log,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
1.4847 + fprintf(log,"<?xml-stylesheet href=\"file:///%s/xml/pawndoc.xsl\" type=\"text/xsl\"?>\n",symname);
1.4848 + fprintf(log,"<doc source=\"%s\">\n",sourcefile);
1.4849 + ptr=strrchr(sourcefile,DIRSEP_CHAR);
1.4850 + if (ptr!=NULL)
1.4851 + ptr++;
1.4852 + else
1.4853 + ptr=sourcefile;
1.4854 + fprintf(log,"\t<assembly>\n\t\t<name>%s</name>\n\t</assembly>\n",ptr);
1.4855 +
1.4856 + /* attach the global documentation, if any */
1.4857 + if (sc_documentation!=NULL) {
1.4858 + fprintf(log,"\n\t<!-- general -->\n");
1.4859 + fprintf(log,"\t<general>\n\t\t");
1.4860 + fputs(sc_documentation,log);
1.4861 + fprintf(log,"\n\t</general>\n\n");
1.4862 + } /* if */
1.4863 +
1.4864 + /* use multiple passes to print constants variables and functions in
1.4865 + * separate sections
1.4866 + */
1.4867 + fprintf(log,"\t<members>\n");
1.4868 +
1.4869 + fprintf(log,"\n\t\t<!-- enumerations -->\n");
1.4870 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.4871 + if (sym->parent!=NULL)
1.4872 + continue; /* hierarchical data type */
1.4873 + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
1.4874 + || sym->ident==iARRAY || sym->ident==iFUNCTN);
1.4875 + if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0)
1.4876 + continue;
1.4877 + if ((sym->usage & uREAD)==0)
1.4878 + continue;
1.4879 + fprintf(log,"\t\t<member name=\"T:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
1.4880 + if (sym->tag!=0) {
1.4881 + tagsym=find_tag_byval(sym->tag);
1.4882 + assert(tagsym!=NULL);
1.4883 + fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
1.4884 + } /* if */
1.4885 + /* browse through all fields */
1.4886 + if ((enumroot=sym->dim.enumlist)!=NULL) {
1.4887 + enumroot=enumroot->next; /* skip root */
1.4888 + while (enumroot!=NULL) {
1.4889 + fprintf(log,"\t\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,enumroot->name),enumroot->value);
1.4890 + /* find the constant with this name and get the tag */
1.4891 + ref=findglb(enumroot->name,sGLOBAL);
1.4892 + if (ref!=NULL) {
1.4893 + if (ref->x.tags.index!=0) {
1.4894 + tagsym=find_tag_byval(ref->x.tags.index);
1.4895 + assert(tagsym!=NULL);
1.4896 + fprintf(log,"\t\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
1.4897 + } /* if */
1.4898 + if (ref->dim.array.length!=1)
1.4899 + fprintf(log,"\t\t\t\t<size value=\"%ld\"/>\n",(long)ref->dim.array.length);
1.4900 + } /* if */
1.4901 + fprintf(log,"\t\t\t</member>\n");
1.4902 + enumroot=enumroot->next;
1.4903 + } /* while */
1.4904 + } /* if */
1.4905 + assert(sym->refer!=NULL);
1.4906 + for (i=0; i<sym->numrefers; i++) {
1.4907 + if ((ref=sym->refer[i])!=NULL)
1.4908 + fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
1.4909 + } /* for */
1.4910 + if (sym->documentation!=NULL)
1.4911 + fprintf(log,"\t\t\t%s\n",sym->documentation);
1.4912 + fprintf(log,"\t\t</member>\n");
1.4913 + } /* for */
1.4914 +
1.4915 + fprintf(log,"\n\t\t<!-- constants -->\n");
1.4916 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.4917 + if (sym->parent!=NULL)
1.4918 + continue; /* hierarchical data type */
1.4919 + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
1.4920 + || sym->ident==iARRAY || sym->ident==iFUNCTN);
1.4921 + if (sym->ident!=iCONSTEXPR)
1.4922 + continue;
1.4923 + if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0)
1.4924 + continue;
1.4925 + fprintf(log,"\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
1.4926 + if (sym->tag!=0) {
1.4927 + tagsym=find_tag_byval(sym->tag);
1.4928 + assert(tagsym!=NULL);
1.4929 + fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
1.4930 + } /* if */
1.4931 + assert(sym->refer!=NULL);
1.4932 + for (i=0; i<sym->numrefers; i++) {
1.4933 + if ((ref=sym->refer[i])!=NULL)
1.4934 + fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
1.4935 + } /* for */
1.4936 + if (sym->documentation!=NULL)
1.4937 + fprintf(log,"\t\t\t%s\n",sym->documentation);
1.4938 + fprintf(log,"\t\t</member>\n");
1.4939 + } /* for */
1.4940 +
1.4941 + fprintf(log,"\n\t\t<!-- variables -->\n");
1.4942 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.4943 + if (sym->parent!=NULL)
1.4944 + continue; /* hierarchical data type */
1.4945 + if (sym->ident!=iVARIABLE && sym->ident!=iARRAY)
1.4946 + continue;
1.4947 + fprintf(log,"\t\t<member name=\"F:%s\">\n",funcdisplayname(symname,sym->name));
1.4948 + if (sym->tag!=0) {
1.4949 + tagsym=find_tag_byval(sym->tag);
1.4950 + assert(tagsym!=NULL);
1.4951 + fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
1.4952 + } /* if */
1.4953 + assert(sym->refer!=NULL);
1.4954 + if ((sym->usage & uPUBLIC)!=0)
1.4955 + fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
1.4956 + for (i=0; i<sym->numrefers; i++) {
1.4957 + if ((ref=sym->refer[i])!=NULL)
1.4958 + fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
1.4959 + } /* for */
1.4960 + if (sym->documentation!=NULL)
1.4961 + fprintf(log,"\t\t\t%s\n",sym->documentation);
1.4962 + fprintf(log,"\t\t</member>\n");
1.4963 + } /* for */
1.4964 +
1.4965 + fprintf(log,"\n\t\t<!-- functions -->\n");
1.4966 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.4967 + if (sym->parent!=NULL)
1.4968 + continue; /* hierarchical data type */
1.4969 + if (sym->ident!=iFUNCTN)
1.4970 + continue;
1.4971 + if ((sym->usage & (uREAD | uNATIVE))==uNATIVE)
1.4972 + continue; /* unused native function */
1.4973 + funcdisplayname(symname,sym->name);
1.4974 + xmlencode(symname,symname);
1.4975 + fprintf(log,"\t\t<member name=\"M:%s\" syntax=\"%s(",symname,symname);
1.4976 + /* print only the names of the parameters between the parentheses */
1.4977 + assert(sym->dim.arglist!=NULL);
1.4978 + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
1.4979 + int dim;
1.4980 + if (arg>0)
1.4981 + fprintf(log,", ");
1.4982 + switch (sym->dim.arglist[arg].ident) {
1.4983 + case iVARIABLE:
1.4984 + fprintf(log,"%s",sym->dim.arglist[arg].name);
1.4985 + break;
1.4986 + case iREFERENCE:
1.4987 + fprintf(log,"&%s",sym->dim.arglist[arg].name);
1.4988 + break;
1.4989 + case iREFARRAY:
1.4990 + fprintf(log,"%s",sym->dim.arglist[arg].name);
1.4991 + for (dim=0; dim<sym->dim.arglist[arg].numdim;dim++)
1.4992 + fprintf(log,"[]");
1.4993 + break;
1.4994 + case iVARARGS:
1.4995 + fprintf(log,"...");
1.4996 + break;
1.4997 + } /* switch */
1.4998 + } /* for */
1.4999 + /* ??? should also print an "array return" size */
1.5000 + fprintf(log,")\">\n");
1.5001 + if (sym->tag!=0) {
1.5002 + tagsym=find_tag_byval(sym->tag);
1.5003 + assert(tagsym!=NULL);
1.5004 + fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
1.5005 + } /* if */
1.5006 + /* check whether this function is called from the outside */
1.5007 + if ((sym->usage & uNATIVE)!=0)
1.5008 + fprintf(log,"\t\t\t<attribute name=\"native\"/>\n");
1.5009 + if ((sym->usage & uPUBLIC)!=0)
1.5010 + fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
1.5011 + if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0)
1.5012 + fprintf(log,"\t\t\t<attribute name=\"entry\"/>\n");
1.5013 + if ((sym->usage & uNATIVE)==0)
1.5014 + fprintf(log,"\t\t\t<stacksize value=\"%ld\"/>\n",(long)sym->x.stacksize);
1.5015 + if (sym->states!=NULL) {
1.5016 + constvalue *stlist=sym->states->next;
1.5017 + assert(stlist!=NULL); /* there should be at least one state item */
1.5018 + while (stlist!=NULL && stlist->index==-1)
1.5019 + stlist=stlist->next;
1.5020 + assert(stlist!=NULL); /* state id should be found */
1.5021 + i=state_getfsa(stlist->index);
1.5022 + assert(i>=0); /* automaton 0 exists */
1.5023 + stlist=automaton_findid(i);
1.5024 + assert(stlist!=NULL); /* automaton should be found */
1.5025 + fprintf(log,"\t\t\t<automaton name=\"%s\"/>\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)");
1.5026 + //??? dump state decision table
1.5027 + } /* if */
1.5028 + assert(sym->refer!=NULL);
1.5029 + for (i=0; i<sym->numrefers; i++)
1.5030 + if ((ref=sym->refer[i])!=NULL)
1.5031 + fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
1.5032 + /* print all symbols that are required for this function to compile */
1.5033 + for (ref=root->next; ref!=NULL; ref=ref->next) {
1.5034 + if (ref==sym)
1.5035 + continue;
1.5036 + for (i=0; i<ref->numrefers; i++)
1.5037 + if (ref->refer[i]==sym)
1.5038 + fprintf(log,"\t\t\t<dependency name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
1.5039 + } /* for */
1.5040 + /* print parameter list, with tag & const information, plus descriptions */
1.5041 + assert(sym->dim.arglist!=NULL);
1.5042 + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
1.5043 + int dim,paraminfo;
1.5044 + char *outer_start,*inner_start;
1.5045 + int outer_length,inner_length;
1.5046 + if (sym->dim.arglist[arg].ident==iVARARGS)
1.5047 + fprintf(log,"\t\t\t<param name=\"...\">\n");
1.5048 + else
1.5049 + fprintf(log,"\t\t\t<param name=\"%s\">\n",sym->dim.arglist[arg].name);
1.5050 + /* print the tag name(s) for each parameter */
1.5051 + assert(sym->dim.arglist[arg].numtags>0);
1.5052 + assert(sym->dim.arglist[arg].tags!=NULL);
1.5053 + paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0)
1.5054 + || sym->dim.arglist[arg].ident==iREFERENCE
1.5055 + || sym->dim.arglist[arg].ident==iREFARRAY;
1.5056 + if (paraminfo)
1.5057 + fprintf(log,"\t\t\t\t<paraminfo>");
1.5058 + if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) {
1.5059 + assert(paraminfo);
1.5060 + if (sym->dim.arglist[arg].numtags>1)
1.5061 + fprintf(log," {");
1.5062 + for (i=0; i<sym->dim.arglist[arg].numtags; i++) {
1.5063 + if (i>0)
1.5064 + fprintf(log,",");
1.5065 + tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]);
1.5066 + assert(tagsym!=NULL);
1.5067 + fprintf(log,"%s",tagsym->name);
1.5068 + } /* for */
1.5069 + if (sym->dim.arglist[arg].numtags>1)
1.5070 + fprintf(log,"}");
1.5071 + } /* if */
1.5072 + switch (sym->dim.arglist[arg].ident) {
1.5073 + case iREFERENCE:
1.5074 + fprintf(log," &");
1.5075 + break;
1.5076 + case iREFARRAY:
1.5077 + fprintf(log," ");
1.5078 + for (dim=0; dim<sym->dim.arglist[arg].numdim; dim++) {
1.5079 + if (sym->dim.arglist[arg].dim[dim]==0) {
1.5080 + fprintf(log,"[]");
1.5081 + } else {
1.5082 + //??? find index tag
1.5083 + fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]);
1.5084 + } /* if */
1.5085 + } /* for */
1.5086 + break;
1.5087 + } /* switch */
1.5088 + if (paraminfo)
1.5089 + fprintf(log," </paraminfo>\n");
1.5090 + /* print the user description of the parameter (parse through
1.5091 + * sym->documentation)
1.5092 + */
1.5093 + if (sym->documentation!=NULL
1.5094 + && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name,
1.5095 + &outer_start, &outer_length, &inner_start, &inner_length))
1.5096 + {
1.5097 + char *tail;
1.5098 + fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start);
1.5099 + /* delete from documentation string */
1.5100 + tail=outer_start+outer_length;
1.5101 + memmove(outer_start,tail,strlen(tail)+1);
1.5102 + } /* if */
1.5103 + fprintf(log,"\t\t\t</param>\n");
1.5104 + } /* for */
1.5105 + if (sym->documentation!=NULL)
1.5106 + fprintf(log,"\t\t\t%s\n",sym->documentation);
1.5107 + fprintf(log,"\t\t</member>\n");
1.5108 + } /* for */
1.5109 +
1.5110 + fprintf(log,"\n\t</members>\n");
1.5111 + fprintf(log,"</doc>\n");
1.5112 +}
1.5113 +#endif
1.5114 +
1.5115 +/* Every symbol has a referrer list, that contains the functions that use
1.5116 + * the symbol. Now, if function "apple" is accessed by functions "banana" and
1.5117 + * "citron", but neither function "banana" nor "citron" are used by anyone
1.5118 + * else, then, by inference, function "apple" is not used either.
1.5119 + */
1.5120 +static void reduce_referrers(symbol *root)
1.5121 +{
1.5122 + int i,restart;
1.5123 + symbol *sym,*ref;
1.5124 +
1.5125 + do {
1.5126 + restart=0;
1.5127 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.5128 + if (sym->parent!=NULL)
1.5129 + continue; /* hierarchical data type */
1.5130 + if (sym->ident==iFUNCTN
1.5131 + && (sym->usage & uNATIVE)==0
1.5132 + && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0
1.5133 + && count_referrers(sym)==0)
1.5134 + {
1.5135 + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
1.5136 + /* find all symbols that are referred by this symbol */
1.5137 + for (ref=root->next; ref!=NULL; ref=ref->next) {
1.5138 + if (ref->parent!=NULL)
1.5139 + continue; /* hierarchical data type */
1.5140 + assert(ref->refer!=NULL);
1.5141 + for (i=0; i<ref->numrefers && ref->refer[i]!=sym; i++)
1.5142 + /* nothing */;
1.5143 + if (i<ref->numrefers) {
1.5144 + assert(ref->refer[i]==sym);
1.5145 + ref->refer[i]=NULL;
1.5146 + restart++;
1.5147 + } /* if */
1.5148 + } /* for */
1.5149 + } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY)
1.5150 + && (sym->usage & uPUBLIC)==0
1.5151 + && sym->parent==NULL
1.5152 + && count_referrers(sym)==0)
1.5153 + {
1.5154 + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
1.5155 + } /* if */
1.5156 + } /* for */
1.5157 + /* after removing a symbol, check whether more can be removed */
1.5158 + } while (restart>0);
1.5159 +}
1.5160 +
1.5161 +#if !defined SC_LIGHT
1.5162 +static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion)
1.5163 +{
1.5164 + long size,maxsize;
1.5165 + int i;
1.5166 +
1.5167 + assert(sym!=NULL);
1.5168 + assert(sym->ident==iFUNCTN);
1.5169 + assert((sym->usage & uNATIVE)==0);
1.5170 + assert(recursion!=NULL);
1.5171 +
1.5172 + maxsize=sym->x.stacksize;
1.5173 + for (i=0; i<sym->numrefers; i++) {
1.5174 + if (sym->refer[i]!=NULL) {
1.5175 + assert(sym->refer[i]->ident==iFUNCTN);
1.5176 + assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */
1.5177 + if (sym->refer[i]==sourcesym) { /* recursion detection */
1.5178 + *recursion=1;
1.5179 + break; /* recursion was detected, quit loop */
1.5180 + } /* if */
1.5181 + size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion);
1.5182 + if (maxsize<size)
1.5183 + maxsize=size;
1.5184 + } /* if */
1.5185 + } /* for */
1.5186 +
1.5187 + if ((sym->usage & uPUBLIC)!=0) {
1.5188 + /* Find out how many parameters a public function has, then see if this
1.5189 + * is bigger than some maximum
1.5190 + */
1.5191 + arginfo *arg=sym->dim.arglist;
1.5192 + int count=0;
1.5193 + assert(arg!=0);
1.5194 + while (arg->ident!=0) {
1.5195 + count++;
1.5196 + arg++;
1.5197 + } /* while */
1.5198 + assert(pubfuncparams!=0);
1.5199 + if (count>*pubfuncparams)
1.5200 + *pubfuncparams=count;
1.5201 + } /* if */
1.5202 +
1.5203 + return maxsize+basesize;
1.5204 +}
1.5205 +
1.5206 +static symbol *save_symbol;
1.5207 +
1.5208 +static long max_stacksize(symbol *root,int *recursion)
1.5209 +{
1.5210 + /* Loop over all non-native functions. For each function, loop
1.5211 + * over all of its referrers, accumulating the stack requirements.
1.5212 + * Detect (indirect) recursion with a "mark-and-sweep" algorithm.
1.5213 + * I (mis-)use the "compound" field of the symbol structure for
1.5214 + * the marker, as this field is unused for functions.
1.5215 + *
1.5216 + * Note that the stack is shared with the heap. A host application
1.5217 + * may "eat" cells from the heap as well, through amx_Allot(). The
1.5218 + * stack requirements are thus only an estimate.
1.5219 + */
1.5220 + long size,maxsize;
1.5221 + int maxparams;
1.5222 + symbol *sym;
1.5223 +
1.5224 + assert(root!=NULL);
1.5225 + assert(recursion!=NULL);
1.5226 + #if !defined NDEBUG
1.5227 + for (sym=root->next; sym!=NULL; sym=sym->next)
1.5228 + if (sym->ident==iFUNCTN)
1.5229 + assert(sym->compound==0);
1.5230 + #endif
1.5231 +
1.5232 + maxsize=0;
1.5233 + maxparams=0;
1.5234 + *recursion=0; /* assume no recursion */
1.5235 + for (sym=root->next; sym!=NULL; sym=sym->next) {
1.5236 + /* drop out if this is not a user-implemented function */
1.5237 + if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0)
1.5238 + continue;
1.5239 + /* accumulate stack size for this symbol */
1.5240 + save_symbol = sym;
1.5241 + size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion);
1.5242 + assert(size>=0);
1.5243 + if (maxsize<size)
1.5244 + maxsize=size;
1.5245 + } /* for */
1.5246 +
1.5247 + maxsize++; /* +1 because a zero cell is always pushed on top
1.5248 + * of the stack to catch stack overwrites */
1.5249 + return maxsize+(maxparams+1);/* +1 because # of parameters is always pushed on entry */
1.5250 +}
1.5251 +#endif
1.5252 +
1.5253 +/* testsymbols - test for unused local or global variables
1.5254 + *
1.5255 + * "Public" functions are excluded from the check, since these
1.5256 + * may be exported to other object modules.
1.5257 + * Labels are excluded from the check if the argument 'testlabs'
1.5258 + * is 0. Thus, labels are not tested until the end of the function.
1.5259 + * Constants may also be excluded (convenient for global constants).
1.5260 + *
1.5261 + * When the nesting level drops below "level", the check stops.
1.5262 + *
1.5263 + * The function returns whether there is an "entry" point for the file.
1.5264 + * This flag will only be 1 when browsing the global symbol table.
1.5265 + */
1.5266 +static int testsymbols(symbol *root,int level,int testlabs,int testconst)
1.5267 +{
1.5268 + char symname[2*sNAMEMAX+16];
1.5269 + int entry=FALSE;
1.5270 +
1.5271 + symbol *sym=root->next;
1.5272 + while (sym!=NULL && sym->compound>=level) {
1.5273 + switch (sym->ident) {
1.5274 + case iLABEL:
1.5275 + if (testlabs) {
1.5276 + if ((sym->usage & uDEFINE)==0) {
1.5277 + error(19,sym->name); /* not a label: ... */
1.5278 + } else if ((sym->usage & uREAD)==0) {
1.5279 + errorset(sSETPOS,sym->lnumber);
1.5280 + error(203,sym->name); /* symbol isn't used: ... */
1.5281 + } /* if */
1.5282 + } /* if */
1.5283 + break;
1.5284 + case iFUNCTN:
1.5285 + if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) {
1.5286 + funcdisplayname(symname,sym->name);
1.5287 + if (strlen(symname)>0)
1.5288 + error(203,symname); /* symbol isn't used ... (and not public/native/stock) */
1.5289 + } /* if */
1.5290 + if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0)
1.5291 + entry=TRUE; /* there is an entry point */
1.5292 + /* also mark the function to the debug information */
1.5293 + if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0)
1.5294 + insert_dbgsymbol(sym);
1.5295 + break;
1.5296 + case iCONSTEXPR:
1.5297 + if (testconst && (sym->usage & uREAD)==0) {
1.5298 + errorset(sSETPOS,sym->lnumber);
1.5299 + error(203,sym->name); /* symbol isn't used: ... */
1.5300 + } /* if */
1.5301 + break;
1.5302 + default:
1.5303 + /* a variable */
1.5304 + if (sym->parent!=NULL)
1.5305 + break; /* hierarchical data type */
1.5306 + if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) {
1.5307 + errorset(sSETPOS,sym->lnumber);
1.5308 + error(203,sym->name); /* symbol isn't used (and not stock) */
1.5309 + } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) {
1.5310 + errorset(sSETPOS,sym->lnumber);
1.5311 + error(204,sym->name); /* value assigned to symbol is never used */
1.5312 +#if 0 // ??? not sure whether it is a good idea to force people use "const"
1.5313 + } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) {
1.5314 + errorset(sSETPOS,sym->lnumber);
1.5315 + error(214,sym->name); /* make array argument "const" */
1.5316 +#endif
1.5317 + } /* if */
1.5318 + /* also mark the variable (local or global) to the debug information */
1.5319 + if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0)
1.5320 + insert_dbgsymbol(sym);
1.5321 + } /* if */
1.5322 + sym=sym->next;
1.5323 + } /* while */
1.5324 +
1.5325 + return entry;
1.5326 +}
1.5327 +
1.5328 +static cell calc_array_datasize(symbol *sym, cell *offset)
1.5329 +{
1.5330 + cell length;
1.5331 +
1.5332 + assert(sym!=NULL);
1.5333 + assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
1.5334 + length=sym->dim.array.length;
1.5335 + if (sym->dim.array.level > 0) {
1.5336 + cell sublength=calc_array_datasize(finddepend(sym),offset);
1.5337 + if (offset!=NULL)
1.5338 + *offset=length*(*offset+sizeof(cell));
1.5339 + if (sublength>0)
1.5340 + length*=length*sublength;
1.5341 + else
1.5342 + length=0;
1.5343 + } else {
1.5344 + if (offset!=NULL)
1.5345 + *offset=0;
1.5346 + } /* if */
1.5347 + return length;
1.5348 +}
1.5349 +
1.5350 +static void destructsymbols(symbol *root,int level)
1.5351 +{
1.5352 + cell offset=0;
1.5353 + int savepri=FALSE;
1.5354 + symbol *sym=root->next;
1.5355 + while (sym!=NULL && sym->compound>=level) {
1.5356 + if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
1.5357 + char symbolname[16];
1.5358 + symbol *opsym;
1.5359 + cell elements;
1.5360 + /* check that the '~' operator is defined for this tag */
1.5361 + operator_symname(symbolname,"~",sym->tag,0,1,0);
1.5362 + if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) {
1.5363 + /* save PRI, in case of a return statment */
1.5364 + if (!savepri) {
1.5365 + pushreg(sPRI); /* right-hand operand is in PRI */
1.5366 + savepri=TRUE;
1.5367 + } /* if */
1.5368 + /* if the variable is an array, get the number of elements */
1.5369 + if (sym->ident==iARRAY) {
1.5370 + elements=calc_array_datasize(sym,&offset);
1.5371 + /* "elements" can be zero when the variable is declared like
1.5372 + * new mytag: myvar[2][] = { {1, 2}, {3, 4} }
1.5373 + * one should declare all dimensions!
1.5374 + */
1.5375 + if (elements==0)
1.5376 + error(46,sym->name); /* array size is unknown */
1.5377 + } else {
1.5378 + elements=1;
1.5379 + offset=0;
1.5380 + } /* if */
1.5381 + pushval(elements);
1.5382 + /* call the '~' operator */
1.5383 + address(sym,sPRI);
1.5384 + addconst(offset); /* add offset to array data to the address */
1.5385 + pushreg(sPRI);
1.5386 + pushval(2 /* *sizeof(cell)*/ );/* 2 parameters */
1.5387 + assert(opsym->ident==iFUNCTN);
1.5388 + ffcall(opsym,NULL,1);
1.5389 + if (sc_status!=statSKIP)
1.5390 + markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */
1.5391 + if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL)
1.5392 + opsym->x.lib->value += 1; /* increment "usage count" of the library */
1.5393 + } /* if */
1.5394 + } /* if */
1.5395 + sym=sym->next;
1.5396 + } /* while */
1.5397 + /* restore PRI, if it was saved */
1.5398 + if (savepri)
1.5399 + popreg(sPRI);
1.5400 +}
1.5401 +
1.5402 +static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,
1.5403 + int index)
1.5404 +{
1.5405 + constvalue *cur;
1.5406 +
1.5407 + if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL)
1.5408 + error(123); /* insufficient memory (fatal error) */
1.5409 + memset(cur,0,sizeof(constvalue));
1.5410 + if (name!=NULL) {
1.5411 + assert(strlen(name)<sNAMEMAX);
1.5412 + strcpy(cur->name,name);
1.5413 + } /* if */
1.5414 + cur->value=val;
1.5415 + cur->index=index;
1.5416 + cur->next=next;
1.5417 + prev->next=cur;
1.5418 + return cur;
1.5419 +}
1.5420 +
1.5421 +SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index)
1.5422 +{
1.5423 + constvalue *cur,*prev;
1.5424 +
1.5425 + /* find the end of the constant table */
1.5426 + for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next)
1.5427 + /* nothing */;
1.5428 + return insert_constval(prev,NULL,name,val,index);
1.5429 +}
1.5430 +
1.5431 +SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index)
1.5432 +{
1.5433 + constvalue *ptr = table->next;
1.5434 +
1.5435 + while (ptr!=NULL) {
1.5436 + if (strcmp(name,ptr->name)==0 && ptr->index==index)
1.5437 + return ptr;
1.5438 + ptr=ptr->next;
1.5439 + } /* while */
1.5440 + return NULL;
1.5441 +}
1.5442 +
1.5443 +static constvalue *find_constval_byval(constvalue *table,cell val)
1.5444 +{
1.5445 + constvalue *ptr = table->next;
1.5446 +
1.5447 + while (ptr!=NULL) {
1.5448 + if (ptr->value==val)
1.5449 + return ptr;
1.5450 + ptr=ptr->next;
1.5451 + } /* while */
1.5452 + return NULL;
1.5453 +}
1.5454 +
1.5455 +#if 0 /* never used */
1.5456 +static int delete_constval(constvalue *table,char *name)
1.5457 +{
1.5458 + constvalue *prev = table;
1.5459 + constvalue *cur = prev->next;
1.5460 +
1.5461 + while (cur!=NULL) {
1.5462 + if (strcmp(name,cur->name)==0) {
1.5463 + prev->next=cur->next;
1.5464 + free(cur);
1.5465 + return TRUE;
1.5466 + } /* if */
1.5467 + prev=cur;
1.5468 + cur=cur->next;
1.5469 + } /* while */
1.5470 + return FALSE;
1.5471 +}
1.5472 +#endif
1.5473 +
1.5474 +SC_FUNC void delete_consttable(constvalue *table)
1.5475 +{
1.5476 + constvalue *cur=table->next, *next;
1.5477 +
1.5478 + while (cur!=NULL) {
1.5479 + next=cur->next;
1.5480 + free(cur);
1.5481 + cur=next;
1.5482 + } /* while */
1.5483 + memset(table,0,sizeof(constvalue));
1.5484 +}
1.5485 +
1.5486 +/* add_constant
1.5487 + *
1.5488 + * Adds a symbol to the symbol table. Returns NULL on failure.
1.5489 + */
1.5490 +SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag)
1.5491 +{
1.5492 + symbol *sym;
1.5493 +
1.5494 + /* Test whether a global or local symbol with the same name exists. Since
1.5495 + * constants are stored in the symbols table, this also finds previously
1.5496 + * defind constants. */
1.5497 + sym=findglb(name,sSTATEVAR);
1.5498 + if (!sym)
1.5499 + sym=findloc(name);
1.5500 + if (sym) {
1.5501 + int redef=0;
1.5502 + if (sym->ident!=iCONSTEXPR)
1.5503 + redef=1; /* redefinition a function/variable to a constant is not allowed */
1.5504 + if ((sym->usage & uENUMFIELD)!=0) {
1.5505 + /* enum field, special case if it has a different tag and the new symbol is also an enum field */
1.5506 + constvalue *tagid;
1.5507 + symbol *tagsym;
1.5508 + if (sym->tag==tag)
1.5509 + redef=1; /* enumeration field is redefined (same tag) */
1.5510 + tagid=find_tag_byval(tag);
1.5511 + if (tagid==NULL) {
1.5512 + redef=1; /* new constant does not have a tag */
1.5513 + } else {
1.5514 + tagsym=findconst(tagid->name,NULL);
1.5515 + if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0)
1.5516 + redef=1; /* new constant is not an enumeration field */
1.5517 + } /* if */
1.5518 + /* in this particular case (enumeration field that is part of a different
1.5519 + * enum, and non-conflicting with plain constants) we want to be able to
1.5520 + * redefine it
1.5521 + */
1.5522 + if (!redef)
1.5523 + goto redef_enumfield;
1.5524 + } else if (sym->tag!=tag) {
1.5525 + redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */
1.5526 + } /* if */
1.5527 + if (redef) {
1.5528 + error(21,name); /* symbol already defined */
1.5529 + return NULL;
1.5530 + } else if (sym->addr!=val) {
1.5531 + error(201,name); /* redefinition of constant (different value) */
1.5532 + sym->addr=val; /* set new value */
1.5533 + } /* if */
1.5534 + /* silently ignore redefinitions of constants with the same value & tag */
1.5535 + return sym;
1.5536 + } /* if */
1.5537 +
1.5538 + /* constant doesn't exist yet (or is allowed to be redefined) */
1.5539 +redef_enumfield:
1.5540 + sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE);
1.5541 + assert(sym!=NULL); /* fatal error 103 must be given on error */
1.5542 + if (sc_status == statIDLE)
1.5543 + sym->usage |= uPREDEF;
1.5544 + return sym;
1.5545 +}
1.5546 +
1.5547 +/* statement - The Statement Parser
1.5548 + *
1.5549 + * This routine is called whenever the parser needs to know what statement
1.5550 + * it encounters (i.e. whenever program syntax requires a statement).
1.5551 + */
1.5552 +static void statement(int *lastindent,int allow_decl)
1.5553 +{
1.5554 + int tok,save;
1.5555 + cell val;
1.5556 + char *st;
1.5557 +
1.5558 + if (!freading) {
1.5559 + error(36); /* empty statement */
1.5560 + return;
1.5561 + } /* if */
1.5562 + errorset(sRESET,0);
1.5563 +
1.5564 + tok=lex(&val,&st);
1.5565 + if (tok!='{') {
1.5566 + insert_dbgline(fline);
1.5567 + setline(TRUE);
1.5568 + } /* if */
1.5569 + /* lex() has set stmtindent */
1.5570 + if (lastindent!=NULL && tok!=tLABEL) {
1.5571 + if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0)
1.5572 + error(217); /* loose indentation */
1.5573 + *lastindent=stmtindent;
1.5574 + indent_nowarn=FALSE; /* if warning was blocked, re-enable it */
1.5575 + } /* if */
1.5576 + switch (tok) {
1.5577 + case 0:
1.5578 + /* nothing */
1.5579 + break;
1.5580 + case tNEW:
1.5581 + if (allow_decl) {
1.5582 + autozero=1;
1.5583 + declloc(FALSE);
1.5584 + lastst=tNEW;
1.5585 + } else {
1.5586 + error(3); /* declaration only valid in a block */
1.5587 + } /* if */
1.5588 + break;
1.5589 + case tDECL:
1.5590 + if (allow_decl) {
1.5591 + autozero=0;
1.5592 + declloc(FALSE);
1.5593 + lastst=tDECL;
1.5594 + } else {
1.5595 + error(3); /* declaration only valid in a block */
1.5596 + } /* if */
1.5597 + break;
1.5598 + case tSTATIC:
1.5599 + if (allow_decl) {
1.5600 + declloc(TRUE);
1.5601 + lastst=tNEW;
1.5602 + } else {
1.5603 + error(3); /* declaration only valid in a block */
1.5604 + } /* if */
1.5605 + break;
1.5606 + case '{':
1.5607 + case tBEGIN:
1.5608 + save=fline;
1.5609 + if (!matchtoken('}')) /* {} is the empty statement */
1.5610 + compound(save==fline,tok);
1.5611 + /* lastst (for "last statement") does not change */
1.5612 + break;
1.5613 + case ';':
1.5614 + error(36); /* empty statement */
1.5615 + break;
1.5616 + case tIF:
1.5617 + lastst=doif();
1.5618 + break;
1.5619 + case tWHILE:
1.5620 + lastst=dowhile();
1.5621 + break;
1.5622 + case tDO:
1.5623 + lastst=dodo();
1.5624 + break;
1.5625 + case tFOR:
1.5626 + lastst=dofor();
1.5627 + break;
1.5628 + case tSWITCH:
1.5629 + doswitch();
1.5630 + lastst=tSWITCH;
1.5631 + break;
1.5632 + case tCASE:
1.5633 + case tDEFAULT:
1.5634 + error(14); /* not in switch */
1.5635 + break;
1.5636 + case tGOTO:
1.5637 + dogoto();
1.5638 + lastst=tGOTO;
1.5639 + break;
1.5640 + case tLABEL:
1.5641 + dolabel();
1.5642 + lastst=tLABEL;
1.5643 + break;
1.5644 + case tRETURN:
1.5645 + doreturn();
1.5646 + lastst=tRETURN;
1.5647 + break;
1.5648 + case tBREAK:
1.5649 + dobreak();
1.5650 + lastst=tBREAK;
1.5651 + break;
1.5652 + case tCONTINUE:
1.5653 + docont();
1.5654 + lastst=tCONTINUE;
1.5655 + break;
1.5656 + case tEXIT:
1.5657 + doexit();
1.5658 + lastst=tEXIT;
1.5659 + break;
1.5660 + case tASSERT:
1.5661 + doassert();
1.5662 + lastst=tASSERT;
1.5663 + break;
1.5664 + case tSLEEP:
1.5665 + dosleep();
1.5666 + lastst=tSLEEP;
1.5667 + break;
1.5668 + case tSTATE:
1.5669 + dostate();
1.5670 + lastst=tSTATE;
1.5671 + break;
1.5672 + case tCONST:
1.5673 + decl_const(sLOCAL);
1.5674 + break;
1.5675 + case tENUM:
1.5676 + decl_enum(sLOCAL);
1.5677 + break;
1.5678 + default: /* non-empty expression */
1.5679 + sc_allowproccall=optproccall;
1.5680 + lexpush(); /* analyze token later */
1.5681 + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);
1.5682 + needtoken(tTERM);
1.5683 + lastst=tEXPR;
1.5684 + sc_allowproccall=FALSE;
1.5685 + } /* switch */
1.5686 +}
1.5687 +
1.5688 +static void compound(int stmt_sameline,int starttok)
1.5689 +{
1.5690 + int indent=-1;
1.5691 + cell save_decl=declared;
1.5692 + int count_stmt=0;
1.5693 + int block_start=fline; /* save line where the compound block started */
1.5694 + int endtok;
1.5695 +
1.5696 + pushstacklist();
1.5697 + pushheaplist();
1.5698 + /* if there is more text on this line, we should adjust the statement indent */
1.5699 + if (stmt_sameline) {
1.5700 + int i;
1.5701 + const unsigned char *p=lptr;
1.5702 + /* go back to the opening brace */
1.5703 + while (*p!=starttok) {
1.5704 + assert(p>pline);
1.5705 + p--;
1.5706 + } /* while */
1.5707 + assert(*p==starttok); /* it should be found */
1.5708 + /* go forward, skipping white-space */
1.5709 + p++;
1.5710 + while (*p<=' ' && *p!='\0')
1.5711 + p++;
1.5712 + assert(*p!='\0'); /* a token should be found */
1.5713 + stmtindent=0;
1.5714 + for (i=0; i<(int)(p-pline); i++)
1.5715 + if (pline[i]=='\t' && sc_tabsize>0)
1.5716 + stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
1.5717 + else
1.5718 + stmtindent++;
1.5719 + } /* if */
1.5720 +
1.5721 + endtok=(starttok=='{') ? '}' : tEND;
1.5722 + nestlevel+=1; /* increase compound statement level */
1.5723 + while (matchtoken(endtok)==0){/* repeat until compound statement is closed */
1.5724 + if (!freading){
1.5725 + error(30,block_start); /* compound block not closed at end of file */
1.5726 + break;
1.5727 + } else {
1.5728 + if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE || lastst==tENDLESS))
1.5729 + error(225); /* unreachable code */
1.5730 + statement(&indent,TRUE); /* do a statement */
1.5731 + count_stmt++;
1.5732 + } /* if */
1.5733 + } /* while */
1.5734 + if (lastst!=tRETURN)
1.5735 + destructsymbols(&loctab,nestlevel);
1.5736 + if (lastst!=tRETURN && lastst!=tGOTO) {
1.5737 + popheaplist();
1.5738 + popstacklist(1);
1.5739 + } else {
1.5740 + popheaplist();
1.5741 + popstacklist(0);
1.5742 + }
1.5743 + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
1.5744 + declared=save_decl;
1.5745 + delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but
1.5746 + * retain block local labels
1.5747 + * (within the function) */
1.5748 + nestlevel-=1; /* decrease compound statement level */
1.5749 +}
1.5750 +
1.5751 +/* doexpr
1.5752 + *
1.5753 + * Global references: stgidx (referred to only)
1.5754 + */
1.5755 +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
1.5756 + int *tag,symbol **symptr,int chkfuncresult)
1.5757 +{
1.5758 + return doexpr2(comma,chkeffect,allowarray,mark_endexpr,tag,symptr,chkfuncresult,NULL);
1.5759 +}
1.5760 +
1.5761 +/* doexpr2
1.5762 + *
1.5763 + * Global references: stgidx (referred to only)
1.5764 + */
1.5765 +static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
1.5766 + int *tag,symbol **symptr,int chkfuncresult,value *lval)
1.5767 +{
1.5768 + int index,ident;
1.5769 + int localstaging=FALSE;
1.5770 + cell val;
1.5771 +
1.5772 + if (!staging) {
1.5773 + stgset(TRUE); /* start stage-buffering */
1.5774 + localstaging=TRUE;
1.5775 + assert(stgidx==0);
1.5776 + } /* if */
1.5777 + index=stgidx;
1.5778 + errorset(sEXPRMARK,0);
1.5779 + do {
1.5780 + /* on second round through, mark the end of the previous expression */
1.5781 + if (index!=stgidx)
1.5782 + markexpr(sEXPR,NULL,0);
1.5783 + sideeffect=FALSE;
1.5784 + ident=expression(&val,tag,symptr,chkfuncresult,lval);
1.5785 + if (!allowarray && (ident==iARRAY || ident==iREFARRAY))
1.5786 + error(33,"-unknown-"); /* array must be indexed */
1.5787 + if (chkeffect && !sideeffect)
1.5788 + error(215); /* expression has no effect */
1.5789 + sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */
1.5790 + } while (comma && matchtoken(',')); /* more? */
1.5791 + if (mark_endexpr)
1.5792 + markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */
1.5793 + errorset(sEXPRRELEASE,0);
1.5794 + if (localstaging) {
1.5795 + stgout(index);
1.5796 + stgset(FALSE); /* stop staging */
1.5797 + } /* if */
1.5798 + return ident;
1.5799 +}
1.5800 +
1.5801 +/* constexpr
1.5802 + */
1.5803 +SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr)
1.5804 +{
1.5805 + int ident,index;
1.5806 + cell cidx;
1.5807 +
1.5808 + stgset(TRUE); /* start stage-buffering */
1.5809 + stgget(&index,&cidx); /* mark position in code generator */
1.5810 + errorset(sEXPRMARK,0);
1.5811 + ident=expression(val,tag,symptr,FALSE,NULL);
1.5812 + stgdel(index,cidx); /* scratch generated code */
1.5813 + stgset(FALSE); /* stop stage-buffering */
1.5814 + if (ident!=iCONSTEXPR) {
1.5815 + error(8); /* must be constant expression */
1.5816 + if (val!=NULL)
1.5817 + *val=0;
1.5818 + if (tag!=NULL)
1.5819 + *tag=0;
1.5820 + if (symptr!=NULL)
1.5821 + *symptr=NULL;
1.5822 + } /* if */
1.5823 + errorset(sEXPRRELEASE,0);
1.5824 + return (ident==iCONSTEXPR);
1.5825 +}
1.5826 +
1.5827 +/* test
1.5828 + *
1.5829 + * In the case a "simple assignment" operator ("=") is used within a test,
1.5830 + * the warning "possibly unintended assignment" is displayed. This routine
1.5831 + * sets the global variable "sc_intest" to true, it is restored upon termination.
1.5832 + * In the case the assignment was intended, use parentheses around the
1.5833 + * expression to avoid the warning; primary() sets "sc_intest" to 0.
1.5834 + *
1.5835 + * Global references: sc_intest (altered, but restored upon termination)
1.5836 + */
1.5837 +static int test(int label,int parens,int invert)
1.5838 +{
1.5839 + int index,tok;
1.5840 + cell cidx;
1.5841 + int ident,tag;
1.5842 + int endtok;
1.5843 + cell constval;
1.5844 + symbol *sym;
1.5845 + int localstaging=FALSE;
1.5846 +
1.5847 + if (!staging) {
1.5848 + stgset(TRUE); /* start staging */
1.5849 + localstaging=TRUE;
1.5850 + #if !defined NDEBUG
1.5851 + stgget(&index,&cidx); /* should start at zero if started locally */
1.5852 + assert(index==0);
1.5853 + #endif
1.5854 + } /* if */
1.5855 +
1.5856 + PUSHSTK_I(sc_intest);
1.5857 + sc_intest=TRUE;
1.5858 + endtok=0;
1.5859 + if (parens!=TEST_PLAIN) {
1.5860 + if (matchtoken('('))
1.5861 + endtok=')';
1.5862 + else if (parens==TEST_THEN)
1.5863 + endtok=tTHEN;
1.5864 + else if (parens==TEST_DO)
1.5865 + endtok=tDO;
1.5866 + } /* if */
1.5867 + do {
1.5868 + stgget(&index,&cidx); /* mark position (of last expression) in
1.5869 + * code generator */
1.5870 + ident=expression(&constval,&tag,&sym,TRUE,NULL);
1.5871 + tok=matchtoken(',');
1.5872 + if (tok)
1.5873 + markexpr(sEXPR,NULL,0);
1.5874 + } while (tok); /* do */
1.5875 + if (endtok!=0)
1.5876 + needtoken(endtok);
1.5877 + if (ident==iARRAY || ident==iREFARRAY) {
1.5878 + char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-";
1.5879 + error(33,ptr); /* array must be indexed */
1.5880 + } /* if */
1.5881 + if (ident==iCONSTEXPR) { /* constant expression */
1.5882 + int testtype=0;
1.5883 + sc_intest=(short)POPSTK_I();/* restore stack */
1.5884 + stgdel(index,cidx);
1.5885 + if (constval) { /* code always executed */
1.5886 + error(206); /* redundant test: always non-zero */
1.5887 + testtype=tENDLESS;
1.5888 + } else {
1.5889 + error(205); /* redundant code: never executed */
1.5890 + jumplabel(label);
1.5891 + } /* if */
1.5892 + if (localstaging) {
1.5893 + stgout(0); /* write "jumplabel" code */
1.5894 + stgset(FALSE); /* stop staging */
1.5895 + } /* if */
1.5896 + return testtype;
1.5897 + } /* if */
1.5898 + if (tag!=0 && tag!=pc_addtag("bool"))
1.5899 + if (check_userop(lneg,tag,0,1,NULL,&tag))
1.5900 + invert= !invert; /* user-defined ! operator inverted result */
1.5901 + if (invert)
1.5902 + jmp_ne0(label); /* jump to label if true (different from 0) */
1.5903 + else
1.5904 + jmp_eq0(label); /* jump to label if false (equal to 0) */
1.5905 + markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */
1.5906 + sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */
1.5907 + if (localstaging) {
1.5908 + stgout(0); /* output queue from the very beginning (see
1.5909 + * assert() when localstaging is set to TRUE) */
1.5910 + stgset(FALSE); /* stop staging */
1.5911 + } /* if */
1.5912 + return 0;
1.5913 +}
1.5914 +
1.5915 +static int doif(void)
1.5916 +{
1.5917 + int flab1,flab2;
1.5918 + int ifindent;
1.5919 + int lastst_true;
1.5920 +
1.5921 + ifindent=stmtindent; /* save the indent of the "if" instruction */
1.5922 + flab1=getlabel(); /* get label number for false branch */
1.5923 + test(flab1,TEST_THEN,FALSE); /* get expression, branch to flab1 if false */
1.5924 + statement(NULL,FALSE); /* if true, do a statement */
1.5925 + if (!matchtoken(tELSE)) { /* if...else ? */
1.5926 + setlabel(flab1); /* no, simple if..., print false label */
1.5927 + } else {
1.5928 + lastst_true=lastst; /* save last statement of the "true" branch */
1.5929 + /* to avoid the "dangling else" error, we want a warning if the "else"
1.5930 + * has a lower indent than the matching "if" */
1.5931 + if (stmtindent<ifindent && sc_tabsize>0)
1.5932 + error(217); /* loose indentation */
1.5933 + flab2=getlabel();
1.5934 + if ((lastst!=tRETURN) && (lastst!=tGOTO))
1.5935 + jumplabel(flab2); /* "true" branch jumps around "else" clause, unless the "true" branch statement already jumped */
1.5936 + setlabel(flab1); /* print false label */
1.5937 + statement(NULL,FALSE); /* do "else" clause */
1.5938 + setlabel(flab2); /* print true label */
1.5939 + /* if both the "true" branch and the "false" branch ended with the same
1.5940 + * kind of statement, set the last statement id to that kind, rather than
1.5941 + * to the generic tIF; this allows for better "unreachable code" checking
1.5942 + */
1.5943 + if (lastst==lastst_true)
1.5944 + return lastst;
1.5945 + } /* if */
1.5946 + return tIF;
1.5947 +}
1.5948 +
1.5949 +static int dowhile(void)
1.5950 +{
1.5951 + int wq[wqSIZE]; /* allocate local queue */
1.5952 + int save_endlessloop,retcode;
1.5953 +
1.5954 + save_endlessloop=endlessloop;
1.5955 + addwhile(wq); /* add entry to queue for "break" */
1.5956 + setlabel(wq[wqLOOP]); /* loop label */
1.5957 + /* The debugger uses the "break" opcode to be able to "break" out of
1.5958 + * a loop. To make sure that each loop has a break opcode, even for the
1.5959 + * tiniest loop, set it below the top of the loop
1.5960 + */
1.5961 + setline(TRUE);
1.5962 + endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
1.5963 + statement(NULL,FALSE); /* if so, do a statement */
1.5964 + jumplabel(wq[wqLOOP]); /* and loop to "while" start */
1.5965 + setlabel(wq[wqEXIT]); /* exit label */
1.5966 + delwhile(); /* delete queue entry */
1.5967 +
1.5968 + retcode=endlessloop ? tENDLESS : tWHILE;
1.5969 + endlessloop=save_endlessloop;
1.5970 + return retcode;
1.5971 +}
1.5972 +
1.5973 +/*
1.5974 + * Note that "continue" will in this case not jump to the top of the loop, but
1.5975 + * to the end: just before the TRUE-or-FALSE testing code.
1.5976 + */
1.5977 +static int dodo(void)
1.5978 +{
1.5979 + int wq[wqSIZE],top;
1.5980 + int save_endlessloop,retcode;
1.5981 +
1.5982 + save_endlessloop=endlessloop;
1.5983 + addwhile(wq); /* see "dowhile" for more info */
1.5984 + top=getlabel(); /* make a label first */
1.5985 + setlabel(top); /* loop label */
1.5986 + statement(NULL,FALSE);
1.5987 + needtoken(tWHILE);
1.5988 + setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */
1.5989 + setline(TRUE);
1.5990 + endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
1.5991 + jumplabel(top);
1.5992 + setlabel(wq[wqEXIT]);
1.5993 + delwhile();
1.5994 + needtoken(tTERM);
1.5995 +
1.5996 + retcode=endlessloop ? tENDLESS : tDO;
1.5997 + endlessloop=save_endlessloop;
1.5998 + return retcode;
1.5999 +}
1.6000 +
1.6001 +static int dofor(void)
1.6002 +{
1.6003 + int wq[wqSIZE],skiplab;
1.6004 + cell save_decl;
1.6005 + int save_nestlevel,save_endlessloop;
1.6006 + int index,endtok;
1.6007 + int *ptr;
1.6008 +
1.6009 + save_decl=declared;
1.6010 + save_nestlevel=nestlevel;
1.6011 + save_endlessloop=endlessloop;
1.6012 + pushstacklist();
1.6013 +
1.6014 + addwhile(wq);
1.6015 + skiplab=getlabel();
1.6016 + endtok= matchtoken('(') ? ')' : tDO;
1.6017 + if (matchtoken(';')==0) {
1.6018 + /* new variable declarations are allowed here */
1.6019 + if (matchtoken(tNEW)) {
1.6020 + /* The variable in expr1 of the for loop is at a
1.6021 + * 'compound statement' level of it own.
1.6022 + */
1.6023 + nestlevel++;
1.6024 + autozero=1;
1.6025 + declloc(FALSE); /* declare local variable */
1.6026 + } else {
1.6027 + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */
1.6028 + needtoken(';');
1.6029 + } /* if */
1.6030 + } /* if */
1.6031 + /* Adjust the "declared" field in the "while queue", in case that
1.6032 + * local variables were declared in the first expression of the
1.6033 + * "for" loop. These are deleted in separately, so a "break" or a "continue"
1.6034 + * must ignore these fields.
1.6035 + */
1.6036 + ptr=readwhile();
1.6037 + assert(ptr!=NULL);
1.6038 + /*ptr[wqBRK]=(int)declared;
1.6039 + *ptr[wqCONT]=(int)declared;
1.6040 + */
1.6041 + ptr[wqBRK] = stackusage->list_id;
1.6042 + ptr[wqCONT] = stackusage->list_id;
1.6043 + jumplabel(skiplab); /* skip expression 3 1st time */
1.6044 + setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */
1.6045 + setline(TRUE);
1.6046 + /* Expressions 2 and 3 are reversed in the generated code: expression 3
1.6047 + * precedes expression 2. When parsing, the code is buffered and marks for
1.6048 + * the start of each expression are insterted in the buffer.
1.6049 + */
1.6050 + assert(!staging);
1.6051 + stgset(TRUE); /* start staging */
1.6052 + assert(stgidx==0);
1.6053 + index=stgidx;
1.6054 + stgmark(sSTARTREORDER);
1.6055 + stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */
1.6056 + setlabel(skiplab); /* jump to this point after 1st expression */
1.6057 + if (matchtoken(';')) {
1.6058 + endlessloop=1;
1.6059 + } else {
1.6060 + endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
1.6061 + needtoken(';');
1.6062 + } /* if */
1.6063 + stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */
1.6064 + if (!matchtoken(endtok)) {
1.6065 + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */
1.6066 + needtoken(endtok);
1.6067 + } /* if */
1.6068 + stgmark(sENDREORDER); /* mark end of reversed evaluation */
1.6069 + stgout(index);
1.6070 + stgset(FALSE); /* stop staging */
1.6071 + statement(NULL,FALSE);
1.6072 + jumplabel(wq[wqLOOP]);
1.6073 + setlabel(wq[wqEXIT]);
1.6074 + delwhile();
1.6075 +
1.6076 + assert(nestlevel>=save_nestlevel);
1.6077 + if (nestlevel>save_nestlevel) {
1.6078 + /* Clean up the space and the symbol table for the local
1.6079 + * variable in "expr1".
1.6080 + */
1.6081 + destructsymbols(&loctab,nestlevel);
1.6082 + popstacklist(1);
1.6083 + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
1.6084 + declared=save_decl;
1.6085 + delete_symbols(&loctab,nestlevel,FALSE,TRUE);
1.6086 + nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */
1.6087 + } else {
1.6088 + popstacklist(0);
1.6089 + } /* if */
1.6090 +
1.6091 + index=endlessloop ? tENDLESS : tFOR;
1.6092 + endlessloop=save_endlessloop;
1.6093 + return index;
1.6094 +}
1.6095 +
1.6096 +/* The switch statement is incompatible with its C sibling:
1.6097 + * 1. the cases are not drop through
1.6098 + * 2. only one instruction may appear below each case, use a compound
1.6099 + * instruction to execute multiple instructions
1.6100 + * 3. the "case" keyword accepts a comma separated list of values to
1.6101 + * match
1.6102 + *
1.6103 + * SWITCH param
1.6104 + * PRI = expression result
1.6105 + * param = table offset (code segment)
1.6106 + *
1.6107 + */
1.6108 +static void doswitch(void)
1.6109 +{
1.6110 + int lbl_table,lbl_exit,lbl_case;
1.6111 + int swdefault,casecount;
1.6112 + int tok,endtok;
1.6113 + cell val;
1.6114 + char *str;
1.6115 + constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */
1.6116 + constvalue *cse,*csp;
1.6117 + char labelname[sNAMEMAX+1];
1.6118 +
1.6119 + endtok= matchtoken('(') ? ')' : tDO;
1.6120 + doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */
1.6121 + needtoken(endtok);
1.6122 + /* generate the code for the switch statement, the label is the address
1.6123 + * of the case table (to be generated later).
1.6124 + */
1.6125 + lbl_table=getlabel();
1.6126 + lbl_case=0; /* just to avoid a compiler warning */
1.6127 + ffswitch(lbl_table);
1.6128 +
1.6129 + if (matchtoken(tBEGIN)) {
1.6130 + endtok=tEND;
1.6131 + } else {
1.6132 + endtok='}';
1.6133 + needtoken('{');
1.6134 + } /* if */
1.6135 + lbl_exit=getlabel(); /* get label number for jumping out of switch */
1.6136 + swdefault=FALSE;
1.6137 + casecount=0;
1.6138 + do {
1.6139 + tok=lex(&val,&str); /* read in (new) token */
1.6140 + switch (tok) {
1.6141 + case tCASE:
1.6142 + if (swdefault!=FALSE)
1.6143 + error(15); /* "default" case must be last in switch statement */
1.6144 + lbl_case=getlabel();
1.6145 + PUSHSTK_I(sc_allowtags);
1.6146 + sc_allowtags=FALSE; /* do not allow tagnames here */
1.6147 + do {
1.6148 + casecount++;
1.6149 +
1.6150 + /* ??? enforce/document that, in a switch, a statement cannot start
1.6151 + * with a label. Then, you can search for:
1.6152 + * * the first semicolon (marks the end of a statement)
1.6153 + * * an opening brace (marks the start of a compound statement)
1.6154 + * and search for the right-most colon before that statement
1.6155 + * Now, by replacing the ':' by a special COLON token, you can
1.6156 + * parse all expressions until that special token.
1.6157 + */
1.6158 +
1.6159 + constexpr(&val,NULL,NULL);
1.6160 + /* Search the insertion point (the table is kept in sorted order, so
1.6161 + * that advanced abstract machines can sift the case table with a
1.6162 + * binary search). Check for duplicate case values at the same time.
1.6163 + */
1.6164 + for (csp=&caselist, cse=caselist.next;
1.6165 + cse!=NULL && cse->value<val;
1.6166 + csp=cse, cse=cse->next)
1.6167 + /* nothing */;
1.6168 + if (cse!=NULL && cse->value==val)
1.6169 + error(40,val); /* duplicate "case" label */
1.6170 + /* Since the label is stored as a string in the "constvalue", the
1.6171 + * size of an identifier must be at least 8, as there are 8
1.6172 + * hexadecimal digits in a 32-bit number.
1.6173 + */
1.6174 + #if sNAMEMAX < 8
1.6175 + #error Length of identifier (sNAMEMAX) too small.
1.6176 + #endif
1.6177 + assert(csp!=NULL);
1.6178 + assert(csp->next==cse);
1.6179 + insert_constval(csp,cse,itoh(lbl_case),val,0);
1.6180 + if (matchtoken(tDBLDOT)) {
1.6181 + error(1, ":", "..");
1.6182 + } /* if */
1.6183 + } while (matchtoken(','));
1.6184 + needtoken(':'); /* ':' ends the case */
1.6185 + sc_allowtags=(short)POPSTK_I(); /* reset */
1.6186 + setlabel(lbl_case);
1.6187 + statement(NULL,FALSE);
1.6188 + jumplabel(lbl_exit);
1.6189 + break;
1.6190 + case tDEFAULT:
1.6191 + if (swdefault!=FALSE)
1.6192 + error(16); /* multiple defaults in switch */
1.6193 + lbl_case=getlabel();
1.6194 + setlabel(lbl_case);
1.6195 + needtoken(':');
1.6196 + swdefault=TRUE;
1.6197 + statement(NULL,FALSE);
1.6198 + /* Jump to lbl_exit, even thouh this is the last clause in the
1.6199 + * switch, because the jump table is generated between the last
1.6200 + * clause of the switch and the exit label.
1.6201 + */
1.6202 + jumplabel(lbl_exit);
1.6203 + break;
1.6204 + default:
1.6205 + if (tok!=endtok) {
1.6206 + error(2);
1.6207 + indent_nowarn=TRUE; /* disable this check */
1.6208 + tok=endtok; /* break out of the loop after an error */
1.6209 + } /* if */
1.6210 + } /* switch */
1.6211 + } while (tok!=endtok);
1.6212 +
1.6213 + #if !defined NDEBUG
1.6214 + /* verify that the case table is sorted (unfortunatly, duplicates can
1.6215 + * occur; there really shouldn't be duplicate cases, but the compiler
1.6216 + * may not crash or drop into an assertion for a user error). */
1.6217 + for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next)
1.6218 + assert(cse->value <= cse->next->value);
1.6219 + #endif
1.6220 + /* generate the table here, before lbl_exit (general jump target) */
1.6221 + setlabel(lbl_table);
1.6222 + assert(swdefault==FALSE || swdefault==TRUE);
1.6223 + if (swdefault==FALSE) {
1.6224 + /* store lbl_exit as the "none-matched" label in the switch table */
1.6225 + strcpy(labelname,itoh(lbl_exit));
1.6226 + } else {
1.6227 + /* lbl_case holds the label of the "default" clause */
1.6228 + strcpy(labelname,itoh(lbl_case));
1.6229 + } /* if */
1.6230 + ffcase(casecount,labelname,TRUE);
1.6231 + /* generate the rest of the table */
1.6232 + for (cse=caselist.next; cse!=NULL; cse=cse->next)
1.6233 + ffcase(cse->value,cse->name,FALSE);
1.6234 +
1.6235 + setlabel(lbl_exit);
1.6236 + delete_consttable(&caselist); /* clear list of case labels */
1.6237 +}
1.6238 +
1.6239 +static void doassert(void)
1.6240 +{
1.6241 + int flab1,index;
1.6242 + cell cidx;
1.6243 +
1.6244 + if ((sc_debug & sCHKBOUNDS)!=0) {
1.6245 + flab1=getlabel(); /* get label number for "OK" branch */
1.6246 + test(flab1,TEST_PLAIN,TRUE);/* get expression and branch to flab1 if true */
1.6247 + insert_dbgline(fline); /* make sure we can find the correct line number */
1.6248 + ffabort(xASSERTION);
1.6249 + setlabel(flab1);
1.6250 + } else {
1.6251 + stgset(TRUE); /* start staging */
1.6252 + stgget(&index,&cidx); /* mark position in code generator */
1.6253 + do {
1.6254 + expression(NULL,NULL,NULL,FALSE,NULL);
1.6255 + stgdel(index,cidx); /* just scrap the code */
1.6256 + } while (matchtoken(','));
1.6257 + stgset(FALSE); /* stop staging */
1.6258 + } /* if */
1.6259 + needtoken(tTERM);
1.6260 +}
1.6261 +
1.6262 +static void dogoto(void)
1.6263 +{
1.6264 + char *st;
1.6265 + cell val;
1.6266 + symbol *sym;
1.6267 +
1.6268 + /* if we were inside an endless loop, assume that we jump out of it */
1.6269 + endlessloop=0;
1.6270 +
1.6271 + if (lex(&val,&st)==tSYMBOL) {
1.6272 + sym=fetchlab(st);
1.6273 + jumplabel((int)sym->addr);
1.6274 + sym->usage|=uREAD; /* set "uREAD" bit */
1.6275 + // ??? if the label is defined (check sym->usage & uDEFINE), check
1.6276 + // sym->compound (nesting level of the label) against nestlevel;
1.6277 + // if sym->compound < nestlevel, call the destructor operator
1.6278 + } else {
1.6279 + error(20,st); /* illegal symbol name */
1.6280 + } /* if */
1.6281 + needtoken(tTERM);
1.6282 +}
1.6283 +
1.6284 +static void dolabel(void)
1.6285 +{
1.6286 + char *st;
1.6287 + cell val;
1.6288 + symbol *sym;
1.6289 +
1.6290 + tokeninfo(&val,&st); /* retrieve label name again */
1.6291 + if (find_constval(&tagname_tab,st,0)!=NULL)
1.6292 + error(221,st); /* label name shadows tagname */
1.6293 + sym=fetchlab(st);
1.6294 + setlabel((int)sym->addr);
1.6295 + /* since one can jump around variable declarations or out of compound
1.6296 + * blocks, the stack must be manually adjusted
1.6297 + */
1.6298 + //:TODO: This is actually generated, egads!
1.6299 + //We have to support this and LCTRL/SCTRL
1.6300 + setstk(-declared*sizeof(cell));
1.6301 + sym->usage|=uDEFINE; /* label is now defined */
1.6302 +}
1.6303 +
1.6304 +/* fetchlab
1.6305 + *
1.6306 + * Finds a label from the (local) symbol table or adds one to it.
1.6307 + * Labels are local in scope.
1.6308 + *
1.6309 + * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()"
1.6310 + * must set this bit accordingly.
1.6311 + */
1.6312 +static symbol *fetchlab(char *name)
1.6313 +{
1.6314 + symbol *sym;
1.6315 +
1.6316 + sym=findloc(name); /* labels are local in scope */
1.6317 + if (sym){
1.6318 + if (sym->ident!=iLABEL)
1.6319 + error(19,sym->name); /* not a label: ... */
1.6320 + } else {
1.6321 + sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0);
1.6322 + assert(sym!=NULL); /* fatal error 103 must be given on error */
1.6323 + sym->x.declared=(int)declared;
1.6324 + sym->compound=nestlevel;
1.6325 + } /* if */
1.6326 + return sym;
1.6327 +}
1.6328 +
1.6329 +/* doreturn
1.6330 + *
1.6331 + * Global references: rettype (altered)
1.6332 + */
1.6333 +static void doreturn(void)
1.6334 +{
1.6335 + int tag,ident;
1.6336 + int level;
1.6337 + symbol *sym,*sub;
1.6338 +
1.6339 + if (!matchtoken(tTERM)) {
1.6340 + /* "return <value>" */
1.6341 + if ((rettype & uRETNONE)!=0)
1.6342 + error(78); /* mix "return;" and "return value;" */
1.6343 + ident=doexpr(TRUE,FALSE,TRUE,FALSE,&tag,&sym,TRUE);
1.6344 + needtoken(tTERM);
1.6345 + /* see if this function already has a sub type (an array attached) */
1.6346 + sub=finddepend(curfunc);
1.6347 + assert(sub==NULL || sub->ident==iREFARRAY);
1.6348 + if ((rettype & uRETVALUE)!=0) {
1.6349 + int retarray=(ident==iARRAY || ident==iREFARRAY);
1.6350 + /* there was an earlier "return" statement in this function */
1.6351 + if (sub==NULL && retarray || sub!=NULL && !retarray)
1.6352 + error(79); /* mixing "return array;" and "return value;" */
1.6353 + if (retarray && (curfunc->usage & uPUBLIC)!=0)
1.6354 + error(90,curfunc->name); /* public function may not return array */
1.6355 + } /* if */
1.6356 + rettype|=uRETVALUE; /* function returns a value */
1.6357 + /* check tagname with function tagname */
1.6358 + assert(curfunc!=NULL);
1.6359 + if (!matchtag_string(ident, tag) && !matchtag(curfunc->tag,tag,TRUE))
1.6360 + error(213); /* tagname mismatch */
1.6361 + if (ident==iARRAY || ident==iREFARRAY) {
1.6362 + int dim[sDIMEN_MAX],numdim;
1.6363 + cell arraysize;
1.6364 + assert(sym!=NULL);
1.6365 + if (sub!=NULL) {
1.6366 + assert(sub->ident==iREFARRAY);
1.6367 + /* this function has an array attached already; check that the current
1.6368 + * "return" statement returns exactly the same array
1.6369 + */
1.6370 + level=sym->dim.array.level;
1.6371 + if (sub->dim.array.level!=level) {
1.6372 + error(48); /* array dimensions must match */
1.6373 + } else {
1.6374 + for (numdim=0; numdim<=level; numdim++) {
1.6375 + dim[numdim]=(int)sub->dim.array.length;
1.6376 + if (sym->dim.array.length!=dim[numdim])
1.6377 + error(47); /* array sizes must match */
1.6378 + if (numdim<level) {
1.6379 + sym=finddepend(sym);
1.6380 + sub=finddepend(sub);
1.6381 + assert(sym!=NULL && sub!=NULL);
1.6382 + /* ^^^ both arrays have the same dimensions (this was checked
1.6383 + * earlier) so the dependend should always be found
1.6384 + */
1.6385 + } /* if */
1.6386 + } /* for */
1.6387 + } /* if */
1.6388 + } else {
1.6389 + int idxtag[sDIMEN_MAX];
1.6390 + int argcount;
1.6391 + /* this function does not yet have an array attached; clone the
1.6392 + * returned symbol beneath the current function
1.6393 + */
1.6394 + sub=sym;
1.6395 + assert(sub!=NULL);
1.6396 + level=sub->dim.array.level;
1.6397 + for (numdim=0; numdim<=level; numdim++) {
1.6398 + dim[numdim]=(int)sub->dim.array.length;
1.6399 + idxtag[numdim]=sub->x.tags.index;
1.6400 + if (numdim<level) {
1.6401 + sub=finddepend(sub);
1.6402 + assert(sub!=NULL);
1.6403 + } /* if */
1.6404 + /* check that all dimensions are known */
1.6405 + if (dim[numdim]<=0)
1.6406 + error(46,sym->name);
1.6407 + } /* for */
1.6408 + /* the address of the array is stored in a hidden parameter; the address
1.6409 + * of this parameter is 1 + the number of parameters (times the size of
1.6410 + * a cell) + the size of the stack frame and the return address
1.6411 + * base + 0*sizeof(cell) == previous "base"
1.6412 + * base + 1*sizeof(cell) == function return address
1.6413 + * base + 2*sizeof(cell) == number of arguments
1.6414 + * base + 3*sizeof(cell) == first argument of the function
1.6415 + * ...
1.6416 + * base + ((n-1)+3)*sizeof(cell) == last argument of the function
1.6417 + * base + (n+3)*sizeof(cell) == hidden parameter with array address
1.6418 + */
1.6419 + assert(curfunc!=NULL);
1.6420 + assert(curfunc->dim.arglist!=NULL);
1.6421 + for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++)
1.6422 + /* nothing */;
1.6423 + sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag);
1.6424 + sub->parent=curfunc;
1.6425 + } /* if */
1.6426 + /* get the hidden parameter, copy the array (the array is on the heap;
1.6427 + * it stays on the heap for the moment, and it is removed -usually- at
1.6428 + * the end of the expression/statement, see expression() in SC3.C)
1.6429 + */
1.6430 + address(sub,sALT); /* ALT = destination */
1.6431 + arraysize=calc_arraysize(dim,numdim,0);
1.6432 + memcopy(arraysize*sizeof(cell)); /* source already in PRI */
1.6433 + /* moveto1(); is not necessary, callfunction() does a popreg() */
1.6434 + } /* if */
1.6435 + } else {
1.6436 + /* this return statement contains no expression */
1.6437 + ldconst(0,sPRI);
1.6438 + if ((rettype & uRETVALUE)!=0) {
1.6439 + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
1.6440 + assert(curfunc!=NULL);
1.6441 + funcdisplayname(symname,curfunc->name);
1.6442 + error(209,symname); /* function should return a value */
1.6443 + } /* if */
1.6444 + rettype|=uRETNONE; /* function does not return anything */
1.6445 + } /* if */
1.6446 + destructsymbols(&loctab,0); /* call destructor for *all* locals */
1.6447 + genheapfree(-1);
1.6448 + genstackfree(-1); /* free everything on the stack */
1.6449 + ffret(strcmp(curfunc->name,uENTRYFUNC)!=0);
1.6450 +}
1.6451 +
1.6452 +static void dobreak(void)
1.6453 +{
1.6454 + int *ptr;
1.6455 +
1.6456 + endlessloop=0; /* if we were inside an endless loop, we just jumped out */
1.6457 + ptr=readwhile(); /* readwhile() gives an error if not in loop */
1.6458 + needtoken(tTERM);
1.6459 + if (ptr==NULL)
1.6460 + return;
1.6461 + destructsymbols(&loctab,nestlevel);
1.6462 + genstackfree(ptr[wqBRK]);
1.6463 + jumplabel(ptr[wqEXIT]);
1.6464 +}
1.6465 +
1.6466 +static void docont(void)
1.6467 +{
1.6468 + int *ptr;
1.6469 +
1.6470 + ptr=readwhile(); /* readwhile() gives an error if not in loop */
1.6471 + needtoken(tTERM);
1.6472 + if (ptr==NULL)
1.6473 + return;
1.6474 + destructsymbols(&loctab,nestlevel);
1.6475 + genstackfree(ptr[wqCONT]);
1.6476 + genheapfree(ptr[wqCONT]);
1.6477 + jumplabel(ptr[wqLOOP]);
1.6478 +}
1.6479 +
1.6480 +SC_FUNC void exporttag(int tag)
1.6481 +{
1.6482 + /* find the tag by value in the table, then set the top bit to mark it
1.6483 + * "public"
1.6484 + */
1.6485 + if (tag!=0 && (tag & PUBLICTAG)==0) {
1.6486 + constvalue *ptr;
1.6487 + for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next)
1.6488 + /* nothing */;
1.6489 + if (ptr!=NULL)
1.6490 + ptr->value |= PUBLICTAG;
1.6491 + } /* if */
1.6492 +}
1.6493 +
1.6494 +static void doexit(void)
1.6495 +{
1.6496 + int tag=0;
1.6497 +
1.6498 + if (matchtoken(tTERM)==0){
1.6499 + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
1.6500 + needtoken(tTERM);
1.6501 + } else {
1.6502 + ldconst(0,sPRI);
1.6503 + } /* if */
1.6504 + ldconst(tag,sALT);
1.6505 + exporttag(tag);
1.6506 + destructsymbols(&loctab,0); /* call destructor for *all* locals */
1.6507 + ffabort(xEXIT);
1.6508 +}
1.6509 +
1.6510 +static void dosleep(void)
1.6511 +{
1.6512 + int tag=0;
1.6513 +
1.6514 + if (matchtoken(tTERM)==0){
1.6515 + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
1.6516 + needtoken(tTERM);
1.6517 + } else {
1.6518 + ldconst(0,sPRI);
1.6519 + } /* if */
1.6520 + ldconst(tag,sALT);
1.6521 + exporttag(tag);
1.6522 + ffabort(xSLEEP);
1.6523 +
1.6524 + /* for stack usage checking, mark the use of the sleep instruction */
1.6525 + pc_memflags |= suSLEEP_INSTR;
1.6526 +}
1.6527 +
1.6528 +static void dostate(void)
1.6529 +{
1.6530 + char name[sNAMEMAX+1];
1.6531 + constvalue *automaton;
1.6532 + constvalue *state;
1.6533 + constvalue *stlist;
1.6534 + int flabel;
1.6535 + symbol *sym;
1.6536 + #if !defined SC_LIGHT
1.6537 + int length,index,listid,listindex,stateindex;
1.6538 + char *doc;
1.6539 + #endif
1.6540 +
1.6541 + /* check for an optional condition */
1.6542 + if (matchtoken('(')) {
1.6543 + flabel=getlabel(); /* get label number for "false" branch */
1.6544 + pc_docexpr=TRUE; /* attach expression as a documentation string */
1.6545 + test(flabel,TEST_PLAIN,FALSE);/* get expression, branch to flabel if false */
1.6546 + pc_docexpr=FALSE;
1.6547 + needtoken(')');
1.6548 + } else {
1.6549 + flabel=-1;
1.6550 + } /* if */
1.6551 +
1.6552 + if (!sc_getstateid(&automaton,&state)) {
1.6553 + delete_autolisttable();
1.6554 + return;
1.6555 + } /* if */
1.6556 + needtoken(tTERM);
1.6557 +
1.6558 + /* store the new state id */
1.6559 + assert(state!=NULL);
1.6560 + ldconst(state->value,sPRI);
1.6561 + assert(automaton!=NULL);
1.6562 + assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
1.6563 + storereg(automaton->value,sPRI);
1.6564 +
1.6565 + /* find the optional entry() function for the state */
1.6566 + sym=findglb(uENTRYFUNC,sGLOBAL);
1.6567 + if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) {
1.6568 + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) {
1.6569 + assert(strlen(stlist->name)!=0);
1.6570 + if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value))
1.6571 + break; /* found! */
1.6572 + } /* for */
1.6573 + assert(stlist==NULL || state_inlist(stlist->index,state->value));
1.6574 + if (stlist!=NULL) {
1.6575 + /* the label to jump to is in stlist->name */
1.6576 + ffcall(sym,stlist->name,0);
1.6577 + } /* if */
1.6578 + } /* if */
1.6579 +
1.6580 + if (flabel>=0)
1.6581 + setlabel(flabel); /* condition was false, jump around the state switch */
1.6582 +
1.6583 + #if !defined SC_LIGHT
1.6584 + /* mark for documentation */
1.6585 + if (sc_status==statFIRST) {
1.6586 + char *str;
1.6587 + /* get the last list id attached to the function, this contains the source states */
1.6588 + assert(curfunc!=NULL);
1.6589 + if (curfunc->states!=NULL) {
1.6590 + stlist=curfunc->states->next;
1.6591 + assert(stlist!=NULL);
1.6592 + while (stlist->next!=NULL)
1.6593 + stlist=stlist->next;
1.6594 + listid=stlist->index;
1.6595 + } else {
1.6596 + listid=-1;
1.6597 + } /* if */
1.6598 + listindex=0;
1.6599 + length=strlen(name)+70; /* +70 for the fixed part "<transition ... />\n" */
1.6600 + /* see if there are any condition strings to attach */
1.6601 + for (index=0; (str=get_autolist(index))!=NULL; index++)
1.6602 + length+=strlen(str);
1.6603 + if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) {
1.6604 + do {
1.6605 + sprintf(doc,"<transition target=\"%s\"",name);
1.6606 + if (listid>=0) {
1.6607 + /* get the source state */
1.6608 + stateindex=state_listitem(listid,listindex);
1.6609 + state=state_findid(stateindex);
1.6610 + assert(state!=NULL);
1.6611 + sprintf(doc+strlen(doc)," source=\"%s\"",state->name);
1.6612 + } /* if */
1.6613 + if (get_autolist(0)!=NULL) {
1.6614 + /* add the condition */
1.6615 + strcat(doc," condition=\"");
1.6616 + for (index=0; (str=get_autolist(index))!=NULL; index++) {
1.6617 + /* remove the ')' token that may be appended before detecting that the expression has ended */
1.6618 + if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL)
1.6619 + strcat(doc,str);
1.6620 + } /* for */
1.6621 + strcat(doc,"\"");
1.6622 + } /* if */
1.6623 + strcat(doc,"/>\n");
1.6624 + insert_docstring(doc);
1.6625 + } while (listid>=0 && ++listindex<state_count(listid));
1.6626 + free(doc);
1.6627 + } /* if */
1.6628 + } /* if */
1.6629 + #endif
1.6630 + delete_autolisttable();
1.6631 +}
1.6632 +
1.6633 +
1.6634 +static void addwhile(int *ptr)
1.6635 +{
1.6636 + int k;
1.6637 +
1.6638 + ptr[wqBRK]=stackusage->list_id; /* stack pointer (for "break") */
1.6639 + ptr[wqCONT]=stackusage->list_id; /* for "continue", possibly adjusted later */
1.6640 + ptr[wqLOOP]=getlabel();
1.6641 + ptr[wqEXIT]=getlabel();
1.6642 + if (wqptr>=(wq+wqTABSZ-wqSIZE))
1.6643 + error(122,"loop table"); /* loop table overflow (too many active loops)*/
1.6644 + k=0;
1.6645 + while (k<wqSIZE){ /* copy "ptr" to while queue table */
1.6646 + *wqptr=*ptr;
1.6647 + wqptr+=1;
1.6648 + ptr+=1;
1.6649 + k+=1;
1.6650 + } /* while */
1.6651 +}
1.6652 +
1.6653 +static void delwhile(void)
1.6654 +{
1.6655 + if (wqptr>wq)
1.6656 + wqptr-=wqSIZE;
1.6657 +}
1.6658 +
1.6659 +static int *readwhile(void)
1.6660 +{
1.6661 + if (wqptr<=wq){
1.6662 + error(24); /* out of context */
1.6663 + return NULL;
1.6664 + } else {
1.6665 + return (wqptr-wqSIZE);
1.6666 + } /* if */
1.6667 +}
1.6668 +