3 * Function and variable definition and declaration, statement parser.
5 * Copyright (c) ITB CompuPhase, 1997-2006
7 * This software is provided "as-is", without any express or implied warranty.
8 * In no event will the authors be held liable for any damages arising from
9 * the use of this software.
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, subject to the following restrictions:
15 * 1. The origin of this software must not be misrepresented; you must not
16 * claim that you wrote the original software. If you use this software in
17 * a product, an acknowledgment in the product documentation would be
18 * appreciated but is not required.
19 * 2. Altered source versions must be plainly marked as such, and must not be
20 * misrepresented as being the original software.
21 * 3. This notice may not be removed or altered from any source distribution.
33 #if defined __WIN32__ || defined _WIN32 || defined __MSDOS__
38 #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
40 #include <binreloc.h> /* from BinReloc, see www.autopackage.org */
44 #include <alloc/fortify.h>
47 #if defined __BORLANDC__ || defined __WATCOMC__
49 static unsigned total_drives; /* dummy variable */
50 #define dos_setdrive(i) _dos_setdrive(i,&total_drives)
51 #elif defined _MSC_VER && defined _WIN32
52 #include <direct.h> /* for _chdrive() */
53 #define dos_setdrive(i) _chdrive(i)
55 #if defined __BORLANDC__
56 #include <dir.h> /* for chdir() */
57 #elif defined __WATCOMC__
58 #include <direct.h> /* for chdir() */
60 #if defined __WIN32__ || defined _WIN32 || defined _Windows
68 #include "svn_version.h"
69 #include "sctracker.h"
70 #define VERSION_STR "3.2.3636"
71 #define VERSION_INT 0x0302
74 int pc_tag_string = 0;
76 static void resetglobals(void);
77 static void initglobals(void);
78 static char *get_extension(char *filename);
79 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
80 char *rname,char *codepage);
81 static void setconfig(char *root);
82 static void setcaption(void);
83 static void about(void);
84 static void setconstants(void);
85 static void parse(void);
86 static void dumplits(void);
87 static void dumpzero(int count);
88 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst);
89 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
90 int stock,int fconst);
91 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
92 static int declloc(int fstatic);
93 static void decl_const(int table);
94 static void declstruct();
95 static void decl_enum(int table);
96 static cell needsub(int *tag,constvalue **enumroot);
97 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
98 constvalue *enumroot);
99 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
100 int startlit,int counteddim[],constvalue *lastdim,
101 constvalue *enumroot,int *errorfound);
102 static cell initvector(int ident,int tag,cell size,int fillzero,
103 constvalue *enumroot,int *errorfound);
104 static cell init(int ident,int *tag,int *errorfound);
105 static int getstates(const char *funcname);
106 static void attachstatelist(symbol *sym, int state_id);
107 static void funcstub(int fnative);
108 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
109 static int declargs(symbol *sym,int chkshadow);
110 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
111 int fpublic,int fconst,int chkshadow,arginfo *arg);
112 static void make_report(symbol *root,FILE *log,char *sourcefile);
113 static void reduce_referrers(symbol *root);
114 static long max_stacksize(symbol *root,int *recursion);
115 static int testsymbols(symbol *root,int level,int testlabs,int testconst);
116 static void destructsymbols(symbol *root,int level);
117 static constvalue *find_constval_byval(constvalue *table,cell val);
118 static symbol *fetchlab(char *name);
119 static void statement(int *lastindent,int allow_decl);
120 static void compound(int stmt_sameline,int starttok);
121 static int test(int label,int parens,int invert);
122 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
123 int *tag,symbol **symptr,int chkfuncresult);
124 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
125 int *tag,symbol **symptr,int chkfuncresult,value *lval);
126 static void doassert(void);
127 static void doexit(void);
128 static int doif(void);
129 static int dowhile(void);
130 static int dodo(void);
131 static int dofor(void);
132 static void doswitch(void);
133 static void dogoto(void);
134 static void dolabel(void);
135 static void doreturn(void);
136 static void dofuncenum(int listmode);
137 static void dobreak(void);
138 static void docont(void);
139 static void dosleep(void);
140 static void dostate(void);
141 static void addwhile(int *ptr);
142 static void delwhile(void);
143 static int *readwhile(void);
144 static void inst_datetime_defines(void);
147 TEST_PLAIN, /* no parentheses */
148 TEST_THEN, /* '(' <expr> ')' or <expr> 'then' */
149 TEST_DO, /* '(' <expr> ')' or <expr> 'do' */
150 TEST_OPT, /* '(' <expr> ')' or <expr> */
152 static int norun = 0; /* the compiler never ran */
153 static int autozero = 1; /* if 1 will zero out the variable, if 0 omit the zeroing */
154 static int lastst = 0; /* last executed statement type */
155 static int nestlevel = 0; /* number of active (open) compound statements */
156 static int endlessloop= 0; /* nesting level of endless loop */
157 static int rettype = 0; /* the type that a "return" expression should have */
158 static int skipinput = 0; /* number of lines to skip from the first input file */
159 static int optproccall = TRUE; /* support "procedure call" */
160 static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */
161 static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */
162 static int sc_parsenum = 0; /* number of the extra parses */
163 static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */
164 static int *wqptr; /* pointer to next entry */
165 #if !defined SC_LIGHT
166 static char sc_rootpath[_MAX_PATH];
167 static char *sc_documentation=NULL;/* main documentation */
169 #if defined __WIN32__ || defined _WIN32 || defined _Windows
170 static HWND hwndFinish = 0;
173 SC_VDECL int glbstringread = 0;
174 char g_tmpfile[_MAX_PATH] = {0};
176 /* "main" of the compiler
178 #if defined __cplusplus
181 int pc_compile(int argc, char *argv[])
185 char incfname[_MAX_PATH];
186 char reportname[_MAX_PATH];
187 char codepage[MAXCODEPAGE+1];
190 int lcl_packstr,lcl_needsemicolon,lcl_tabsize;
191 #if !defined SC_LIGHT
196 /* set global variables to their initial value */
200 errorset(sEXPRRELEASE,0);
203 /* make sure that we clean up on a fatal error; do this before the first
204 * call to error(). */
205 if ((jmpcode=setjmp(errbuf))!=0)
208 /* allocate memory for fixed tables */
209 inpfname=(char*)malloc(_MAX_PATH);
211 error(103); /* insufficient memory */
212 litq=(cell*)malloc(litmax*sizeof(cell));
214 error(103); /* insufficient memory */
216 error(103); /* insufficient memory */
218 setopt(argc,argv,outfname,errfname,incfname,reportname,codepage);
219 strcpy(binfname,outfname);
220 ptr=get_extension(binfname);
221 if (ptr!=NULL && stricmp(ptr,".asm")==0)
222 set_extension(binfname,".smx",TRUE);
224 set_extension(binfname,".smx",FALSE);
225 /* set output names that depend on the input name */
227 set_extension(outfname,".lst",TRUE);
229 set_extension(outfname,".asm",TRUE);
230 if (strlen(errfname)!=0)
231 remove(errfname); /* delete file on startup */
232 else if (verbosity>0)
234 setconfig(argv[0]); /* the path to the include and codepage files */
235 sc_ctrlchar_org=sc_ctrlchar;
236 lcl_packstr=sc_packstr;
237 lcl_needsemicolon=sc_needsemicolon;
238 lcl_tabsize=sc_tabsize;
239 #if !defined NO_CODEPAGE
240 if (!cp_set(codepage)) /* set codepage */
241 error(108); /* codepage mapping file not found */
243 /* optionally create a temporary input file that is a collection of all
246 assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */
247 if (get_sourcefile(1)!=NULL) {
248 /* there are at least two or more source files */
252 #if defined __WIN32__ || defined _WIN32
253 tname=_tempnam(NULL,"pawn");
254 #elif defined __MSDOS__ || defined _Windows
255 tname=tempnam(NULL,"pawn");
256 #elif defined(MACOS) && !defined(__MACH__)
257 /* tempnam is not supported for the Macintosh CFM build. */
258 error(104,get_sourcefile(1));
262 tname=tempnam(NULL,"pawn");
264 ftmp=(FILE*)pc_createsrc(tname);
265 for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) {
266 unsigned char tstring[128];
267 fsrc=(FILE*)pc_opensrc(sname);
271 strcpy(inpfname,sname); /* avoid invalid filename */
274 pc_writesrc(ftmp,(unsigned char*)"#file \"");
275 pc_writesrc(ftmp,(unsigned char*)sname);
276 pc_writesrc(ftmp,(unsigned char*)"\"\n");
277 while (!pc_eofsrc(fsrc) && pc_readsrc(fsrc,tstring,sizeof tstring)) {
278 pc_writesrc(ftmp,tstring);
283 strcpy(inpfname,tname);
284 strcpy(g_tmpfile,tname);
287 strcpy(inpfname,get_sourcefile(0));
289 inpf_org=(FILE*)pc_opensrc(inpfname);
293 outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */
296 /* immediately open the binary file, for other programs to check */
297 if (sc_asmfile || sc_listing) {
300 binf=(FILE*)pc_openbin(binfname);
304 setconstants(); /* set predefined constants and tagnames */
305 for (i=0; i<skipinput; i++) /* skip lines in the input file */
306 if (pc_readsrc(inpf_org,pline,sLINEMAX)!=NULL)
307 fline++; /* keep line number up to date */
310 /* write starting options (from the command line or the configuration file) */
313 sprintf(string,"#pragma ctrlchar 0x%02x\n"
315 "#pragma semicolon %s\n"
316 "#pragma tabsize %d\n",
318 sc_packstr ? "true" : "false",
319 sc_needsemicolon ? "true" : "false",
321 pc_writeasm(outf,string);
322 setfiledirect(inpfname);
324 /* do the first pass through the file (or possibly two or more "first passes") */
326 inpfmark=pc_getpossrc(inpf_org);
328 /* reset "defined" flag of all functions and global variables */
329 reduce_referrers(&glbtab);
330 delete_symbols(&glbtab,0,TRUE,FALSE);
331 #if !defined NO_DEFINE
333 inst_datetime_defines();
338 sc_ctrlchar=sc_ctrlchar_org;
339 sc_packstr=lcl_packstr;
340 sc_needsemicolon=lcl_needsemicolon;
341 sc_tabsize=lcl_tabsize;
343 /* reset the source file */
346 pc_resetsrc(inpf,inpfmark); /* reset file position */
347 fline=skipinput; /* reset line number */
348 sc_reparse=FALSE; /* assume no extra passes */
349 sc_status=statFIRST; /* resetglobals() resets it to IDLE */
351 if (strlen(incfname)>0) {
352 if (strcmp(incfname,sDEF_PREFIX)==0) {
353 plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */
355 if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */
356 error(100,incfname); /* cannot read from ... (fatal error) */
359 preprocess(); /* fetch first line */
360 parse(); /* process all input */
362 } while (sc_reparse);
364 /* second (or third) pass */
365 sc_status=statWRITE; /* set, to enable warnings */
366 state_conflict(&glbtab);
368 /* write a report, if requested */
369 #if !defined SC_LIGHT
372 if (strlen(reportname)>0)
373 frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */
375 make_report(&glbtab,frep,get_sourcefile(0));
376 if (strlen(reportname)>0)
379 if (sc_documentation!=NULL) {
380 free(sc_documentation);
381 sc_documentation=NULL;
388 /* ??? for re-parsing the listing file instead of the original source
389 * file (and doing preprocessing twice):
390 * - close input file, close listing file
391 * - re-open listing file for reading (inpf)
392 * - open assembler file (outf)
395 /* reset "defined" flag of all functions and global variables */
396 reduce_referrers(&glbtab);
397 delete_symbols(&glbtab,0,TRUE,FALSE);
400 #if !defined NO_DEFINE
402 inst_datetime_defines();
405 sc_ctrlchar=sc_ctrlchar_org;
406 sc_packstr=lcl_packstr;
407 sc_needsemicolon=lcl_needsemicolon;
408 sc_tabsize=lcl_tabsize;
410 /* reset the source file */
413 pc_resetsrc(inpf,inpfmark); /* reset file position */
414 fline=skipinput; /* reset line number */
415 lexinit(); /* clear internal flags of lex() */
416 sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */
417 writeleader(&glbtab);
418 insert_dbgfile(inpfname);
419 if (strlen(incfname)>0) {
420 if (strcmp(incfname,sDEF_PREFIX)==0)
421 plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */
423 plungequalifiedfile(incfname); /* parse implicit include file (again) */
425 preprocess(); /* fetch first line */
426 parse(); /* process all input */
427 /* inpf is already closed when readline() attempts to pop of a file */
428 writetrailer(); /* write remaining stuff */
430 entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined
431 * functions and variables */
433 error(13); /* no entry point (no public functions) */
436 if (inpf!=NULL) /* main source file is not closed, do it now */
438 /* write the binary file (the file is already open) */
439 if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) {
441 pc_resetasm(outf); /* flush and loop back, for reading */
442 #if !defined SC_LIGHT
445 assemble(binf,outf); /* assembler file is now input */
448 pc_closeasm(outf,!(sc_asmfile || sc_listing));
452 pc_closebin(binf,errnum!=0);
456 #if !defined SC_LIGHT
457 if (errnum==0 && strlen(errfname)==0) {
459 long stacksize=max_stacksize(&glbtab,&recursion);
462 long totalsize=hdrsize+code_idx;
464 totalsize+=(glb_declared+pc_stksize)*sizeof(cell);
465 if (totalsize>=pc_amxlimit)
468 if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram)
470 if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) {
471 pc_printf("Header size: %8ld bytes\n", (long)hdrsize);
472 pc_printf("Code size: %8ld bytes\n", (long)code_idx);
473 pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell));
474 pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell));
475 pc_printf("estimated max. usage");
477 pc_printf(": unknown, due to recursion\n");
478 else if ((pc_memflags & suSLEEP_INSTR)!=0)
479 pc_printf(": unknown, due to the \"sleep\" instruction\n");
481 pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell));
482 pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell));
485 error(106,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */
489 if (g_tmpfile[0] != '\0') {
492 if (inpfname!=NULL) {
500 assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols
501 * should already have been deleted */
502 delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet
503 * done (i.e. on a fatal error) */
504 delete_symbols(&glbtab,0,TRUE,TRUE);
505 delete_consttable(&tagname_tab);
506 delete_consttable(&libname_tab);
507 delete_consttable(&sc_automaton_tab);
508 delete_consttable(&sc_state_tab);
512 delete_sourcefiletable();
513 delete_dbgstringtable();
516 #if !defined NO_DEFINE
519 #if !defined SC_LIGHT
520 delete_docstringtable();
521 if (sc_documentation!=NULL)
522 free(sc_documentation);
524 delete_autolisttable();
526 if (strlen(errfname)==0)
527 pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : "");
529 } else if (warnnum!=0){
530 if (strlen(errfname)==0)
531 pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : "");
532 retcode=0; /* use "0", so that MAKE and similar tools continue */
535 if (retcode==0 && verbosity>=2)
536 pc_printf("\nDone.\n");
538 #if defined __WIN32__ || defined _WIN32 || defined _Windows
539 if (IsWindow(hwndFinish))
540 PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L);
543 Fortify_ListAllMemory();
548 #if defined __cplusplus
551 int pc_addconstant(char *name,cell value,int tag)
553 errorset(sFORCESET,0); /* make sure error engine is silenced */
555 add_constant(name,value,sGLOBAL,tag);
559 static void inst_datetime_defines(void)
567 curtime = localtime(&td);
568 strftime(date, 31, "\"%m/%d%Y\"", curtime);
569 strftime(ltime, 31, "\"%H:%M:%S\"", curtime);
571 insert_subst("__DATE__", date, 8);
572 insert_subst("__TIME__", ltime, 8);
575 #if defined __cplusplus
578 int pc_addtag(char *name)
585 /* no tagname was given, check for one */
586 if (lex(&val,&name)!=tLABEL) {
588 return 0; /* untagged */
592 assert(strchr(name,':')==NULL); /* colon should already have been stripped */
594 ptr=tagname_tab.next;
596 tag=(int)(ptr->value & TAGMASK);
597 if (strcmp(name,ptr->name)==0)
598 return tag; /* tagname is known, return its sequence number */
599 tag &= (int)~FIXEDTAG;
600 tag &= (int)~FUNCTAG;
606 /* tagname currently unknown, add it */
607 tag=last+1; /* guaranteed not to exist already */
609 tag |= (int)FIXEDTAG;
610 append_constval(&tagname_tab,name,(cell)tag,0);
614 int pc_addfunctag(char *name)
621 /* no tagname was given, check for one */
622 if (lex(&val,&name)!=tLABEL) {
624 return 0; /* untagged */
628 assert(strchr(name,':')==NULL); /* colon should already have been stripped */
630 ptr=tagname_tab.next;
632 tag=(int)(ptr->value & TAGMASK);
633 if (strcmp(name,ptr->name)==0)
634 return tag; /* tagname is known, return its sequence number */
635 tag &= (int)~FIXEDTAG;
636 tag &= (int)~FUNCTAG;
642 /* tagname currently unknown, add it */
643 tag=last+1; /* guaranteed not to exist already */
645 tag |= (int)FIXEDTAG;
647 append_constval(&tagname_tab,name,(cell)tag,0);
651 static void resetglobals(void)
653 /* reset the subset of global variables that is modified by the first pass */
654 curfunc=NULL; /* pointer to current function */
655 lastst=0; /* last executed statement type */
656 nestlevel=0; /* number of active (open) compound statements */
657 rettype=0; /* the type that a "return" expression should have */
658 litidx=0; /* index to literal table */
659 stgidx=0; /* index to the staging buffer */
660 sc_labnum=0; /* top value of (internal) labels */
661 staging=0; /* true if staging output */
662 declared=0; /* number of local cells declared */
663 glb_declared=0; /* number of global cells declared */
664 code_idx=0; /* number of bytes with generated code */
665 ntv_funcid=0; /* incremental number of native function */
666 curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */
667 freading=FALSE; /* no input file ready yet */
668 fline=0; /* the line number in the current file */
669 fnumber=0; /* the file number in the file table (debugging) */
670 fcurrent=0; /* current file being processed (debugging) */
671 sc_intest=FALSE; /* true if inside a test */
672 sideeffect=0; /* true if an expression causes a side-effect */
673 stmtindent=0; /* current indent of the statement */
674 indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */
675 sc_allowtags=TRUE; /* allow/detect tagnames */
677 sc_allowproccall=FALSE;
678 pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */
686 static void initglobals(void)
690 sc_asmfile=FALSE; /* do not create .ASM file */
691 sc_listing=FALSE; /* do not create .LST file */
692 skipinput=0; /* number of lines to skip from the first input file */
693 sc_ctrlchar=CTRL_CHAR;/* the escape character */
694 litmax=sDEF_LITMAX; /* current size of the literal table */
695 errnum=0; /* number of errors */
696 warnnum=0; /* number of warnings */
697 optproccall=FALSE; /* sourcemod: do not support "procedure call" */
698 verbosity=1; /* verbosity level, no copyright banner */
699 sc_debug=sCHKBOUNDS|sSYMBOLIC; /* sourcemod: full debug stuff */
700 pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */
701 sc_packstr=TRUE; /* strings are packed by default */
702 sc_compress=FALSE; /* always disable compact encoding! */
703 sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */
704 sc_dataalign=sizeof(cell);
705 pc_stksize=sDEF_AMXSTACK;/* default stack size */
706 pc_amxlimit=0; /* no limit on size of the abstract machine */
707 pc_amxram=0; /* no limit on data size of the abstract machine */
708 sc_tabsize=8; /* assume a TAB is 8 spaces */
709 sc_rationaltag=0; /* assume no support for rational numbers */
710 rational_digits=0; /* number of fractional digits */
712 outfname[0]='\0'; /* output file name */
713 errfname[0]='\0'; /* error file name */
714 inpf=NULL; /* file read from */
715 inpfname=NULL; /* pointer to name of the file currently read from */
716 outf=NULL; /* file written to */
717 litq=NULL; /* the literal queue */
718 glbtab.next=NULL; /* clear global variables/constants table */
719 loctab.next=NULL; /* " local " / " " */
720 tagname_tab.next=NULL;/* tagname table */
721 libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */
723 pline[0]='\0'; /* the line read from the input file */
724 lptr=NULL; /* points to the current position in "pline" */
725 curlibrary=NULL; /* current library */
726 inpf_org=NULL; /* main source file */
728 wqptr=wq; /* initialize while queue pointer */
730 #if !defined SC_LIGHT
731 sc_documentation=NULL;
732 sc_makereport=FALSE; /* do not generate a cross-reference report */
736 static char *get_extension(char *filename)
740 assert(filename!=NULL);
741 ptr=strrchr(filename,'.');
743 /* ignore extension on a directory or at the start of the filename */
744 if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR)
751 * Set the default extension, or force an extension. To erase the
752 * extension of a filename, set "extension" to an empty string.
754 SC_FUNC void set_extension(char *filename,char *extension,int force)
758 assert(extension!=NULL && (*extension=='\0' || *extension=='.'));
759 assert(filename!=NULL);
760 ptr=get_extension(filename);
761 if (force && ptr!=NULL)
762 *ptr='\0'; /* set zero terminator at the position of the period */
763 if (force || ptr==NULL)
764 strcat(filename,extension);
767 static const char *option_value(const char *optptr)
769 return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1;
772 static int toggle_option(const char *optptr, int option)
774 switch (*option_value(optptr)) {
790 /* Parsing command line options is indirectly recursive: parseoptions()
791 * calls parserespf() to handle options in a a response file and
792 * parserespf() calls parseoptions() at its turn after having created
793 * an "option list" from the contents of the file.
795 static void parserespf(char *filename,char *oname,char *ename,char *pname,
796 char *rname, char *codepage);
798 static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname,
799 char *rname, char *codepage)
801 char str[_MAX_PATH],*name;
805 for (arg=1; arg<argc; arg++) {
807 isoption= argv[arg][0]=='-';
809 isoption= argv[arg][0]=='/' || argv[arg][0]=='-';
815 i=atoi(option_value(ptr));
816 if ((i % sizeof(cell))==0)
824 sc_asmfile=TRUE; /* skip last pass of making binary file */
828 #if 0 /* not allowed in SourceMod */
830 #if AMX_COMPACTMARGIN > 2
831 sc_compress=toggle_option(ptr,sc_compress);
838 strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */
840 #if defined dos_setdrive
841 case 'D': /* set active directory */
842 ptr=option_value(ptr);
844 dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */
848 #if 0 /* not allowed to change for SourceMod */
850 switch (*option_value(ptr)) {
855 sc_debug=sCHKBOUNDS; /* assertions and bounds checking */
858 sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */
861 sc_debug=sCHKBOUNDS | sSYMBOLIC;
862 pc_optimize=sOPTIMIZE_NONE;
863 /* also avoid peephole optimization */
871 strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */
873 #if defined __WIN32__ || defined _WIN32 || defined _Windows
875 hwndFinish=(HWND)atoi(option_value(ptr));
876 if (!IsWindow(hwndFinish))
881 strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */
884 if (str[i-1]!=DIRSEP_CHAR) {
894 sc_listing=TRUE; /* skip second pass & code generation */
897 strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */
900 pc_optimize=*option_value(ptr) - '0';
901 if (pc_optimize<sOPTIMIZE_NONE || pc_optimize>=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO)
905 strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */
907 #if !defined SC_LIGHT
909 strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */
911 if (strlen(rname)>0) {
912 set_extension(rname,".xml",FALSE);
913 } else if ((name=get_sourcefile(0))!=NULL) {
914 assert(strlen(rname)==0);
915 assert(strlen(name)<_MAX_PATH);
916 if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
917 ptr++; /* strip path */
920 assert(strlen(ptr)<_MAX_PATH);
922 set_extension(rname,".xml",TRUE);
927 i=atoi(option_value(ptr));
929 pc_stksize=(cell)i; /* stack size has minimum size */
934 skipinput=atoi(option_value(ptr));
937 sc_tabsize=atoi(option_value(ptr));
940 verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2;
941 if (sc_asmfile && verbosity>1)
945 i=(int)strtol(option_value(ptr),(char **)&ptr,10);
947 pc_enablewarning(i,0);
949 pc_enablewarning(i,1);
951 pc_enablewarning(i,2);
955 i=atoi(option_value(ptr+1));
957 pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */
961 i=atoi(option_value(ptr));
963 pc_amxlimit=(cell)i;/* abstract machine has minimum size */
968 case '\\': /* use \ instead for escape characters */
971 case '^': /* use ^ instead for escape characters */
975 sc_needsemicolon=toggle_option(ptr,sc_needsemicolon);
977 #if 0 /* not allowed to change in SourceMod */
979 optproccall=!toggle_option(ptr,!optproccall);
982 default: /* wrong option */
985 } else if (argv[arg][0]=='@') {
986 #if !defined SC_LIGHT
987 parserespf(&argv[arg][1],oname,ename,pname,rname,codepage);
989 } else if ((ptr=strchr(argv[arg],'='))!=NULL) {
990 i=(int)(ptr-argv[arg]);
993 error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */
995 strlcpy(str,argv[arg],i+1); /* str holds symbol name */
997 add_constant(str,i,sGLOBAL,0);
999 strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".sp" */
1000 set_extension(str,".sp",FALSE);
1001 insert_sourcefile(str);
1002 /* The output name is the first input name with a different extension,
1003 * but it is stored in a different directory
1005 if (strlen(oname)==0) {
1006 if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1007 ptr++; /* strip path */
1010 assert(strlen(ptr)<_MAX_PATH);
1013 set_extension(oname,".asm",TRUE);
1014 #if !defined SC_LIGHT
1015 if (sc_makereport && strlen(rname)==0) {
1016 if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1017 ptr++; /* strip path */
1020 assert(strlen(ptr)<_MAX_PATH);
1022 set_extension(rname,".xml",TRUE);
1029 #if !defined SC_LIGHT
1030 static void parserespf(char *filename,char *oname,char *ename,char *pname,
1031 char *rname,char *codepage)
1033 #define MAX_OPTIONS 100
1035 char *string, *ptr, **argv;
1039 if ((fp=fopen(filename,"r"))==NULL)
1040 error(100,filename); /* error reading input file */
1041 /* load the complete file into memory */
1042 fseek(fp,0L,SEEK_END);
1044 fseek(fp,0L,SEEK_SET);
1045 assert(size<INT_MAX);
1046 if ((string=(char *)malloc((int)size+1))==NULL)
1047 error(103); /* insufficient memory */
1048 /* fill with zeros; in MS-DOS, fread() may collapse CR/LF pairs to
1049 * a single '\n', so the string size may be smaller than the file
1051 memset(string,0,(int)size+1);
1052 fread(string,1,(int)size,fp);
1054 /* allocate table for option pointers */
1055 if ((argv=(char **)malloc(MAX_OPTIONS*sizeof(char*)))==NULL)
1056 error(103); /* insufficient memory */
1057 /* fill the options table */
1058 ptr=strtok(string," \t\r\n");
1059 for (argc=1; argc<MAX_OPTIONS && ptr!=NULL; argc++) {
1060 /* note: the routine skips argv[0], for compatibility with main() */
1062 ptr=strtok(NULL," \t\r\n");
1065 error(102,"option table"); /* table overflow */
1066 /* parse the option table */
1067 parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1068 /* free allocated memory */
1074 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
1075 char *rname,char *codepage)
1077 delete_sourcefiletable(); /* make sure it is empty */
1083 strcpy(pname,sDEF_PREFIX);
1085 #if 0 /* needed to test with BoundsChecker for DOS (it does not pass
1086 * through arguments) */
1087 insert_sourcefile("test.p");
1088 strcpy(oname,"test.asm");
1091 #if !defined SC_LIGHT
1092 /* first parse a "config" file with default options */
1093 if (argv[0]!=NULL) {
1094 char cfgfile[_MAX_PATH];
1096 strcpy(cfgfile,argv[0]);
1097 if ((ext=strrchr(cfgfile,DIRSEP_CHAR))!=NULL) {
1098 *(ext+1)='\0'; /* strip the program filename */
1099 strcat(cfgfile,"pawn.cfg");
1101 strcpy(cfgfile,"pawn.cfg");
1103 if (access(cfgfile,4)==0)
1104 parserespf(cfgfile,oname,ename,pname,rname,codepage);
1107 parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1108 if (get_sourcefile(0)==NULL)
1112 #if defined __BORLANDC__ || defined __WATCOMC__
1115 static void setconfig(char *root)
1117 #if defined macintosh
1118 insert_path(":include:");
1120 char path[_MAX_PATH];
1124 /* add the default "include" directory */
1125 #if defined __WIN32__ || defined _WIN32
1126 GetModuleFileNameA(NULL,path,_MAX_PATH);
1129 strlcpy(path,root,sizeof path); /* path + filename (hopefully) */
1131 #if defined __MSDOS__
1132 /* strip the options (appended to the path + filename) */
1133 if ((ptr=strpbrk(path," \t/"))!=NULL)
1136 /* terminate just behind last \ or : */
1137 if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL || (ptr=strchr(path,':'))!=NULL) {
1138 /* If there is no "\" or ":", the string probably does not contain the
1139 * path; so we just don't add it to the list in that case
1143 strcat(path,"include");
1145 path[len]=DIRSEP_CHAR;
1147 /* see if it exists */
1148 if (access(path,0)!=0 && *base==DIRSEP_CHAR) {
1149 /* There is no "include" directory below the directory where the compiler
1150 * is found. This typically means that the compiler is in a "bin" sub-directory
1151 * and the "include" is below the *parent*. So find the parent...
1154 if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL) {
1156 strcat(path,"include");
1158 path[len]=DIRSEP_CHAR;
1165 /* same for the codepage root */
1166 #if !defined NO_CODEPAGE
1168 if (!cp_path(path,"codepage"))
1169 error(109,path); /* codepage path */
1171 /* also copy the root path (for the XML documentation) */
1172 #if !defined SC_LIGHT
1174 strcpy(sc_rootpath,path);
1177 #endif /* macintosh */
1180 static void setcaption(void)
1182 pc_printf("SourcePawn Compiler 1.0.0." SVN_REVISION_STRING "\n");
1183 pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2006 AlliedModders, LLC\n\n");
1186 static void about(void)
1188 if (strlen(errfname)==0) {
1190 pc_printf("Usage: pawncc <filename> [filename...] [options]\n\n");
1191 pc_printf("Options:\n");
1192 pc_printf(" -A<num> alignment in bytes of the data segment and the stack\n");
1193 pc_printf(" -a output assembler code\n");
1194 #if 0 /* not toggleable in SourceMod */
1195 pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-');
1197 pc_printf(" -c<name> codepage name or number; e.g. 1252 for Windows Latin-1\n");
1198 #if defined dos_setdrive
1199 pc_printf(" -Dpath active directory path\n");
1201 #if 0 /* not used for SourceMod */
1202 pc_printf(" -d<num> debugging level (default=-d%d)\n",sc_debug);
1203 pc_printf(" 0 no symbolic information, no run-time checks\n");
1204 pc_printf(" 1 run-time checks, no symbolic information\n");
1205 pc_printf(" 2 full debug information and dynamic checking\n");
1206 pc_printf(" 3 same as -d2, but implies -O0\n");
1208 pc_printf(" -e<name> set name of error file (quiet compile)\n");
1209 #if defined __WIN32__ || defined _WIN32 || defined _Windows
1210 pc_printf(" -H<hwnd> window handle to send a notification message on finish\n");
1212 pc_printf(" -i<name> path for include files\n");
1213 pc_printf(" -l create list file (preprocess only)\n");
1214 pc_printf(" -o<name> set base name of (P-code) output file\n");
1215 pc_printf(" -O<num> optimization level (default=-O%d)\n",pc_optimize);
1216 pc_printf(" 0 no optimization\n");
1217 #if 0 /* not used for SourceMod */
1218 pc_printf(" 1 JIT-compatible optimizations only\n");
1220 pc_printf(" 2 full optimizations\n");
1221 pc_printf(" -p<name> set name of \"prefix\" file\n");
1222 #if !defined SC_LIGHT
1223 pc_printf(" -r[name] write cross reference report to console or to specified file\n");
1225 pc_printf(" -S<num> stack/heap size in cells (default=%d)\n",(int)pc_stksize);
1226 pc_printf(" -s<num> skip lines from the input file\n");
1227 pc_printf(" -t<num> TAB indent size (in character positions, default=%d)\n",sc_tabsize);
1228 pc_printf(" -v<num> verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity);
1229 pc_printf(" -w<num> disable a specific warning by its number\n");
1230 pc_printf(" -X<num> abstract machine size limit in bytes\n");
1231 pc_printf(" -XD<num> abstract machine data/stack size limit in bytes\n");
1232 pc_printf(" -\\ use '\\' for escape characters\n");
1233 pc_printf(" -^ use '^' for escape characters\n");
1234 pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-');
1235 #if 0 /* not allowed in SourceMod */
1236 pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+');
1238 pc_printf(" sym=val define constant \"sym\" with value \"val\"\n");
1239 pc_printf(" sym= define constant \"sym\" with value 0\n");
1240 #if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__
1241 pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n");
1242 pc_printf("equivalent.\n");
1244 pc_printf("\nOptions with a value may optionally separate the value from the option letter\n");
1245 pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n");
1246 pc_printf("and \"-d:0\" are all equivalent.\n");
1249 longjmp(errbuf,3); /* user abort */
1252 static void setconstants(void)
1256 assert(sc_status==statIDLE);
1257 append_constval(&tagname_tab,"_",0,0);/* "untagged" */
1258 append_constval(&tagname_tab,"bool",1,0);
1259 pc_functag = pc_addfunctag("Function");
1260 pc_tag_string = pc_addtag("String");
1262 add_constant("true",1,sGLOBAL,1); /* boolean flags */
1263 add_constant("false",0,sGLOBAL,1);
1264 add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */
1265 #if PAWN_CELL_SIZE==16
1266 add_constant("cellbits",16,sGLOBAL,0);
1267 #if defined _I16_MAX
1268 add_constant("cellmax",_I16_MAX,sGLOBAL,0);
1269 add_constant("cellmin",_I16_MIN,sGLOBAL,0);
1271 add_constant("cellmax",SHRT_MAX,sGLOBAL,0);
1272 add_constant("cellmin",SHRT_MIN,sGLOBAL,0);
1274 #elif PAWN_CELL_SIZE==32
1275 add_constant("cellbits",32,sGLOBAL,0);
1276 #if defined _I32_MAX
1277 add_constant("cellmax",_I32_MAX,sGLOBAL,0);
1278 add_constant("cellmin",_I32_MIN,sGLOBAL,0);
1280 add_constant("cellmax",LONG_MAX,sGLOBAL,0);
1281 add_constant("cellmin",LONG_MIN,sGLOBAL,0);
1283 #elif PAWN_CELL_SIZE==64
1284 #if !defined _I64_MIN
1285 #define _I64_MIN (-9223372036854775807ULL - 1)
1286 #define _I64_MAX 9223372036854775807ULL
1288 add_constant("cellbits",64,sGLOBAL,0);
1289 add_constant("cellmax",_I64_MAX,sGLOBAL,0);
1290 add_constant("cellmin",_I64_MIN,sGLOBAL,0);
1292 #error Unsupported cell size
1294 add_constant("charbits",sCHARBITS,sGLOBAL,0);
1295 add_constant("charmin",0,sGLOBAL,0);
1296 add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0);
1297 add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0);
1299 add_constant("__Pawn",VERSION_INT,sGLOBAL,0);
1302 if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC))
1304 else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS)
1306 add_constant("debug",debug,sGLOBAL,0);
1308 append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */
1311 static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst)
1317 assert(fconst!=NULL);
1318 assert(fstock!=NULL);
1319 assert(fstatic!=NULL);
1320 assert(fpublic!=NULL);
1325 switch (initialtok) {
1342 tok=lex(&val,&str); /* read in (new) token */
1346 err=42; /* invalid combination of class specifiers */
1351 err=42; /* invalid combination of class specifiers */
1356 err=42; /* invalid combination of class specifiers */
1361 err=42; /* invalid combination of class specifiers */
1366 tok=0; /* force break out of loop */
1368 } while (tok && err==0);
1371 if (*fstatic && *fpublic) {
1372 err=42; /* invalid combination of class specifiers */
1373 *fstatic=*fpublic=FALSE;
1381 /* parse - process all input text
1383 * At this level, only static declarations and function definitions are legal.
1385 static void parse(void)
1387 int tok,fconst,fstock,fstatic,fpublic;
1392 /* first try whether a declaration possibly is native or public */
1393 tok=lex(&val,&str); /* read in (new) token */
1399 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst))
1400 declglb(NULL,0,fpublic,fstatic,fstock,fconst);
1403 /* This can be a static function or a static global variable; we know
1404 * which of the two as soon as we have parsed up to the point where an
1405 * opening paranthesis of a function would be expected. To back out after
1406 * deciding it was a declaration of a static variable after all, we have
1407 * to store the symbol name and tag.
1409 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1411 declfuncvar(fpublic,fstatic,fstock,fconst);
1415 decl_const(sGLOBAL);
1430 /* This can be a public function or a public variable; see the comment
1431 * above (for static functions/variables) for details.
1433 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1435 declfuncvar(fpublic,fstatic,fstock,fconst);
1439 /* This can be a stock function or a stock *global*) variable; see the
1440 * comment above (for static functions/variables) for details.
1442 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1444 declfuncvar(fpublic,fstatic,fstock,fconst);
1451 if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) {
1452 error(10); /* illegal function or declaration */
1453 lexclr(TRUE); /* drop the rest of the line */
1454 litidx=0; /* drop the literal queue too */
1458 funcstub(TRUE); /* create a dummy function */
1464 error(54); /* unmatched closing brace */
1467 error(55); /* start of function body without function header */
1471 error(10); /* illegal function or declaration */
1472 lexclr(TRUE); /* drop the rest of the line */
1473 litidx=0; /* drop any literal arrays (strings) */
1481 * Dump the literal pool (strings etc.)
1483 * Global references: litidx (referred to only)
1485 static void dumplits(void)
1489 if (sc_status==statSKIP)
1494 /* should be in the data segment */
1497 j=16; /* 16 values per line */
1498 while (j && k<litidx){
1499 outval(litq[k], FALSE);
1503 if (j==0 || k>=litidx)
1504 stgwrite("\n"); /* force a newline after 10 dumps */
1505 /* Note: stgwrite() buffers a line until it is complete. It recognizes
1506 * the end of line as a sequence of "\n\0", so something like "\n\t"
1507 * so should not be passed to stgwrite().
1515 * Dump zero's for default initial values
1517 static void dumpzero(int count)
1521 if (sc_status==statSKIP || count<=0)
1526 while (count-- > 0) {
1529 stgwrite((i==0 || count==0) ? "\n" : " ");
1530 if (i==0 && count>0)
1535 static void aligndata(int numbytes)
1537 assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of
1539 assert(numbytes!=0);
1541 if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) {
1542 while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0)
1548 #if !defined SC_LIGHT
1549 /* sc_attachdocumentation()
1550 * appends documentation comments to the passed-in symbol, or to a global
1551 * string if "sym" is NULL.
1553 void sc_attachdocumentation(symbol *sym)
1559 if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) {
1560 /* just clear the entire table */
1561 delete_docstringtable();
1564 /* in the case of state functions, multiple documentation sections may
1565 * appear; we should concatenate these
1566 * (with forward declarations, this is also already the case, so the assertion
1569 // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL);
1571 /* first check the size */
1573 for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
1575 length++; /* count 1 extra for a separating space */
1576 length+=strlen(str);
1578 if (sym==NULL && sc_documentation!=NULL) {
1579 length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
1580 assert(length>strlen(sc_documentation));
1584 /* allocate memory for the documentation */
1585 if (sym!=NULL && sym->documentation!=NULL)
1586 length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
1587 doc=(char*)malloc((length+1)*sizeof(char));
1589 /* initialize string or concatenate */
1590 if (sym==NULL && sc_documentation!=NULL) {
1591 strcpy(doc,sc_documentation);
1593 } else if (sym!=NULL && sym->documentation!=NULL) {
1594 strcpy(doc,sym->documentation);
1596 free(sym->documentation);
1597 sym->documentation=NULL;
1601 /* collect all documentation */
1602 while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
1606 delete_docstring(0);
1609 /* also delete the separator */
1610 assert(*str==sDOCSEP);
1611 delete_docstring(0);
1614 assert(sym->documentation==NULL);
1615 sym->documentation=doc;
1617 if (sc_documentation!=NULL)
1618 free(sc_documentation);
1619 sc_documentation=doc;
1623 /* delete an empty separator, if present */
1624 if ((str=get_docstring(0))!=NULL && *str==sDOCSEP)
1625 delete_docstring(0);
1629 static void insert_docstring_separator(void)
1631 char sep[2]={sDOCSEP,'\0'};
1632 insert_docstring(sep);
1635 #define sc_attachdocumentation(s) (void)(s)
1636 #define insert_docstring_separator()
1639 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst)
1641 char name[sNAMEMAX+11];
1646 pstruct_t *pstruct = NULL;
1649 if (tok == tLABEL) {
1650 pstruct=pstructs_find(str);
1656 /* if we arrived here, this may not be a declaration of a native function
1660 error(42); /* invalid combination of class specifiers */
1664 if (tok!=tSYMBOL && tok!=tOPERATOR) {
1667 lexclr(TRUE); /* drop the rest of the line */
1668 litidx=0; /* drop the literal queue too */
1671 if (tok==tOPERATOR) {
1672 lexpush(); /* push "operator" keyword back (for later analysis) */
1673 if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) {
1674 error(10); /* illegal function or declaration */
1675 lexclr(TRUE); /* drop the rest of the line */
1676 litidx=0; /* drop the literal queue too */
1679 /* so tok is tSYMBOL */
1680 assert(strlen(str)<=sNAMEMAX);
1682 /* only variables can be "const" or both "public" and "stock" */
1683 invalidfunc= fconst || (fpublic && fstock);
1684 if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) {
1685 /* if not a function, try a global variable */
1687 declstructvar(name,fpublic,pstruct);
1689 declglb(name,tag,fpublic,fstatic,fstock,fconst);
1695 /* declstruct - declare global struct symbols
1697 * global references: glb_declared (altered)
1699 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
1701 char name[sNAMEMAX+1];
1706 cell *values, *found;
1710 strcpy(name, firstname);
1712 values = (cell *)malloc(pstruct->argcount * sizeof(cell));
1713 found = (cell *)malloc(pstruct->argcount * sizeof(cell));
1715 memset(found, 0, sizeof(cell) * pstruct->argcount);
1717 //:TODO: Make this work with stock
1720 * Lastly, very lastly, we will insert a copy of this variable.
1721 * This is soley to expose the pubvar.
1723 usage = uREAD|uCONST|uSTRUCT;
1729 for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1731 if (strcmp(name, sym->name) == 0)
1733 if ((sym->usage & uSTRUCT) && sym->vclass == sGLOBAL)
1735 if (sym->usage & uDEFINE)
1739 if (sym->usage & uPUBLIC && !fpublic)
1753 mysym=addsym(name, 0, iVARIABLE, sGLOBAL, pc_addtag(pstruct->name), usage);
1756 if (!matchtoken('='))
1759 /* Mark it as undefined instead */
1760 mysym->usage = uSTOCK|uSTRUCT;
1763 mysym->usage = usage;
1769 /* Detect early exit */
1770 if (matchtoken('}'))
1778 error(1, "-identifier-", str);
1781 arg=pstructs_getarg(pstruct,str);
1784 /* :TODO: change to "Could not find member %s in struct %s" */
1788 cur_litidx = litidx;
1796 assert(litidx != 0);
1797 if (arg->dimcount != 1)
1800 } else if (arg->tag != pc_tag_string) {
1803 values[arg->index] = glb_declared * sizeof(cell);
1804 glb_declared += (litidx-cur_litidx);
1805 found[arg->index] = 1;
1806 } else if (tok == tNUMBER || tok == tRATIONAL) {
1807 /* eat optional 'f' */
1809 if (arg->ident != iVARIABLE && arg->ident != iREFERENCE)
1813 if (arg->tag == pc_addtag("Float") && tok == tNUMBER ||
1814 arg->tag == 0 && tok == tRATIONAL)
1818 if (arg->ident == iVARIABLE)
1820 values[arg->index] = val;
1821 } else if (arg->ident == iREFERENCE) {
1822 values[arg->index] = glb_declared * sizeof(cell);
1825 cur_litidx = litidx;
1827 found[arg->index] = 1;
1829 } else if (tok == tSYMBOL) {
1830 for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1832 if (sym->vclass != sGLOBAL)
1836 if (strcmp(sym->name, str) == 0)
1838 if (arg->ident == iREFERENCE && sym->ident != iVARIABLE)
1840 /* :TODO: Change to "symbol \"%s\" does not have a matching type */
1842 } else if (arg->ident == iARRAY) {
1843 if (sym->ident != iARRAY)
1845 /* :TODO: Change to "symbol \"%s\" does not have a matching type */
1848 /* :TODO: We should check dimension sizes here... */
1850 } else if (arg->ident == iREFARRAY) {
1851 if (sym->ident != iARRAY)
1853 /* :TODO: Change to "symbol \"%s\" does not have a matching type */
1856 /* :TODO: Check dimension sizes! */
1858 /* :TODO: Change to "symbol \"%s\" does not have a matching type */
1861 if (sym->tag != arg->tag)
1865 sym->usage |= uREAD;
1866 values[arg->index] = sym->addr;
1867 found[arg->index] = 1;
1868 refer_symbol(sym, mysym);
1877 error(1, "-identifier-", str);
1879 } while (matchtoken(','));
1881 matchtoken(';'); /* eat up optional semicolon */
1883 for (i=0; i<pstruct->argcount; i++)
1887 structarg_t *arg = pstruct->args[i];
1888 if (arg->ident == iREFARRAY)
1890 values[arg->index] = glb_declared * sizeof(cell);
1893 cur_litidx = litidx;
1894 } else if (arg->ident == iVARIABLE) {
1895 values[arg->index] = 0;
1897 /* :TODO: broken for iARRAY! (unused tho) */
1902 mysym->addr = glb_declared * sizeof(cell);
1903 glb_declared += pstruct->argcount;
1905 for (i=0; i<pstruct->argcount; i++)
1918 /* declglb - declare global symbols
1920 * Declare a static (global) variable. Global variables are stored in
1923 * global references: glb_declared (altered)
1925 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst)
1927 int ident,tag,ispublic;
1928 int idxtag[sDIMEN_MAX];
1929 char name[sNAMEMAX+1];
1934 int dim[sDIMEN_MAX];
1939 constvalue *enumroot;
1944 assert(!fpublic || !fstatic); /* may not both be set */
1945 insert_docstring_separator(); /* see comment in newfunc() */
1946 filenum=fcurrent; /* save file number at the start of the declaration */
1948 size=1; /* single size (no array) */
1949 numdim=0; /* no dimensions */
1951 if (firstname!=NULL) {
1952 assert(strlen(firstname)<=sNAMEMAX);
1953 strcpy(name,firstname); /* save symbol name */
1957 tag=pc_addtag(NULL);
1958 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
1959 error(20,str); /* invalid symbol name */
1960 assert(strlen(str)<=sNAMEMAX);
1961 strcpy(name,str); /* save symbol name */
1964 if (name[0]==PUBLIC_CHAR) {
1965 ispublic=TRUE; /* implicitly public variable */
1968 while (matchtoken('[')) {
1970 if (numdim == sDIMEN_MAX) {
1971 error(53); /* exceeding maximum number of dimensions */
1974 size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */
1975 #if INT_MAX < LONG_MAX
1977 error(105); /* overflow, exceeding capacity */
1979 #if 0 /* We don't actually care */
1981 error(56,name); /* arrays cannot be public */
1983 dim[numdim++]=(int)size;
1985 if (ident == iARRAY && tag == pc_tag_string && dim[numdim-1]) {
1986 slength=dim[numdim-1];
1987 dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
1989 assert(sc_curstates==0);
1990 sc_curstates=getstates(name);
1991 if (sc_curstates<0) {
1992 error(85,name); /* empty state list on declaration */
1994 } else if (sc_curstates>0 && ispublic) {
1995 error(88,name); /* public variables may not have states */
1998 sym=findconst(name,NULL);
2000 sym=findglb(name,sSTATEVAR);
2001 /* if a global variable without states is found and this declaration has
2002 * states, the declaration is okay
2004 if (sym!=NULL && sym->states==NULL && sc_curstates>0)
2005 sym=NULL; /* set to NULL, we found the global variable */
2006 if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL)
2007 error(233,name); /* state variable shadows a global variable */
2010 * a) not found a matching variable (or rejected it, because it was a shadow)
2011 * b) found a global variable and we were looking for that global variable
2012 * c) found a state variable in the automaton that we were looking for
2015 || sym->states==NULL && sc_curstates==0
2016 || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates);
2017 /* a state variable may only have a single id in its list (so either this
2018 * variable has no states, or it has a single list)
2020 assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL);
2021 /* it is okay for the (global) variable to exist, as long as it belongs to
2022 * a different automaton
2024 if (sym!=NULL && (sym->usage & uDEFINE)!=0)
2025 error(21,name); /* symbol already defined */
2026 /* if this variable is never used (which can be detected only in the
2027 * second stage), shut off code generation
2029 cidx=0; /* only to avoid a compiler warning */
2030 if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) {
2034 glbdecl=glb_declared;
2037 begdseg(); /* real (initialized) data in data segment */
2038 assert(litidx==0); /* literal queue should be empty */
2041 aligndata(sc_dataalign);
2042 dumplits(); /* dump the literal queue */
2044 litidx=0; /* global initial data is dumped, so restart at zero */
2046 assert(litidx==0); /* literal queue should be empty (again) */
2047 initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */
2048 if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
2049 slength = glbstringread;
2051 assert(size>=litidx);
2054 /* before dumping the initial values (or zeros) check whether this variable
2057 if (sc_curstates>0) {
2061 error(89,name); /* state variables may not be initialized */
2062 /* find an appropriate address for the state variable */
2063 /* assume that it cannot be found */
2064 address=sizeof(cell)*glb_declared;
2066 /* use a memory map in which every cell occupies one bit */
2067 if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) {
2068 int fsa=state_getfsa(sc_curstates);
2070 cell sweepsize,addr;
2071 memset(map,0,(glb_declared+7)/8);
2073 /* fill in all variables belonging to this automaton */
2074 for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
2075 if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
2076 continue; /* hierarchical type, or no states, or same as this variable */
2077 if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
2078 continue; /* a function or a constant */
2079 if ((sweep->usage & uDEFINE)==0)
2080 continue; /* undefined variable, ignore */
2081 if (fsa!=state_getfsa(sweep->states->next->index))
2082 continue; /* wrong automaton */
2083 /* when arrived here, this is a global variable, with states and
2084 * belonging to the same automaton as the variable we are declaring
2086 sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
2087 assert(sweep->addr % sizeof(cell) == 0);
2088 addr=sweep->addr/sizeof(cell);
2089 /* mark this address range */
2090 while (sweepsize-->0) {
2091 map[addr/8] |= (unsigned char)(1 << (addr % 8));
2095 /* go over it again, clearing any ranges that have conflicts */
2096 for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
2097 if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
2098 continue; /* hierarchical type, or no states, or same as this variable */
2099 if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
2100 continue; /* a function or a constant */
2101 if ((sweep->usage & uDEFINE)==0)
2102 continue; /* undefined variable, ignore */
2103 if (fsa!=state_getfsa(sweep->states->next->index))
2104 continue; /* wrong automaton */
2105 /* when arrived here, this is a global variable, with states and
2106 * belonging to the same automaton as the variable we are declaring
2108 /* if the lists of states of the existing variable and the new
2109 * variable have a non-empty intersection, this is not a suitable
2110 * overlap point -> wipe the address range
2112 if (state_conflict_id(sc_curstates,sweep->states->next->index)) {
2113 sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
2114 assert(sweep->addr % sizeof(cell) == 0);
2115 addr=sweep->addr/sizeof(cell);
2116 /* mark this address range */
2117 while (sweepsize-->0) {
2118 map[addr/8] &= (unsigned char)(~(1 << (addr % 8)));
2123 /* now walk through the map and find a starting point that is big enough */
2125 for (addr=0; addr<glb_declared; addr++) {
2126 if ((map[addr/8] & (1 << (addr % 8)))==0)
2128 for (sweepsize=addr+1; sweepsize<glb_declared; sweepsize++) {
2129 if ((map[sweepsize/8] & (1 << (sweepsize % 8)))==0)
2130 break; /* zero bit found, skip this range */
2131 if (sweepsize-addr>=size)
2132 break; /* fitting range found, no need to search further */
2134 if (sweepsize-addr>=size)
2135 break; /* fitting range found, no need to search further */
2139 if (sweepsize-addr>=size) {
2140 address=sizeof(cell)*addr; /* fitting range found, set it */
2145 address=sizeof(cell)*glb_declared;
2148 if (address==sizeof(cell)*glb_declared) {
2149 dumplits(); /* dump the literal queue */
2150 dumpzero((int)size-litidx);
2153 if (sym==NULL) { /* define only if not yet defined */
2154 sym=addvariable2(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag,slength);
2156 attachstatelist(sym,sc_curstates);
2157 } else { /* if declared but not yet defined, adjust the variable's address */
2158 assert(sym->states==NULL && sc_curstates==0
2159 || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL);
2161 sym->codeaddr=code_idx;
2162 sym->usage|=uDEFINE;
2167 sym->usage|=uPUBLIC|uREAD;
2173 sym->fnumber=filenum;
2174 sc_attachdocumentation(sym);/* attach any documenation to the variable */
2175 if (sc_status==statSKIP) {
2176 sc_status=statWRITE;
2178 assert(glb_declared==glbdecl);
2180 glb_declared+=glb_incr; /* add total number of cells (if added to the end) */
2182 } while (matchtoken(',')); /* enddo */ /* more? */
2183 needtoken(tTERM); /* if not comma, must be semicolumn */
2186 /* declloc - declare local symbols
2188 * Declare local (automatic) variables. Since these variables are relative
2189 * to the STACK, there is no switch to the DATA segment. These variables
2190 * cannot be initialized either.
2192 * global references: declared (altered)
2193 * funcstatus (referred to only)
2195 static int declloc(int fstatic)
2198 int idxtag[sDIMEN_MAX];
2199 char name[sNAMEMAX+1];
2201 constvalue *enumroot=NULL;
2206 int dim[sDIMEN_MAX];
2212 fconst=matchtoken(tCONST);
2217 numdim=0; /* no dimensions */
2218 tag=pc_addtag(NULL);
2219 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
2220 error(20,str); /* invalid symbol name */
2221 assert(strlen(str)<=sNAMEMAX);
2222 strcpy(name,str); /* save symbol name */
2223 if (name[0]==PUBLIC_CHAR)
2224 error(56,name); /* local variables cannot be public */
2225 /* Note: block locals may be named identical to locals at higher
2226 * compound blocks (as with standard C); so we must check (and add)
2227 * the "nesting level" of local variables to verify the
2228 * multi-definition of symbols.
2230 if ((sym=findloc(name))!=NULL && sym->compound==nestlevel)
2231 error(21,name); /* symbol already defined */
2232 /* Although valid, a local variable whose name is equal to that
2233 * of a global variable or to that of a local variable at a lower
2234 * level might indicate a bug.
2236 if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL)
2237 error(219,name); /* variable shadows another symbol */
2238 if (matchtoken('[')) {
2244 int all_constant = 1;
2245 int _staging = staging;
2249 stgget(&_index, &_code);
2252 if (numdim == sDIMEN_MAX) {
2253 error(53); /* exceeding maximum number of dimensions */
2254 return (all_constant ? iARRAY : iREFARRAY);
2255 } else if (numdim) { /* if */
2256 /* If we have a dimension on the stack, push it */
2259 if (matchtoken(']')) {
2265 dim_ident = doexpr2(TRUE,FALSE,FALSE,FALSE,&idxtag[numdim],&dim_sym,0,&dim_val);
2266 if (dim_ident == iVARIABLE || dim_ident == iEXPRESSION || dim_ident == iARRAYCELL) {
2268 } else if (dim_ident == iCONSTEXPR) {
2269 dim[numdim] = dim_val.constval;
2270 /* :TODO: :URGENT: Make sure this still works */
2271 if (dim_sym && dim_sym->usage & uENUMROOT)
2272 enumroot = dim_sym->dim.enumlist;
2273 idxtag[numdim] = dim_sym ? dim_sym->tag : 0;
2274 #if INT_MAX < LONG_MAX
2275 if (dim[numdim] > INT_MAX)
2276 error(105); /* overflow, exceeding capacity */
2279 error(29); /* invalid expression, assumed 0 */
2283 } while (matchtoken('['));
2285 /* Change the last dimension to be based on chars instead if we have a string */
2286 if (tag == pc_tag_string && numdim && dim[numdim-1]) {
2287 slength = dim[numdim-1];
2288 dim[numdim-1] = (dim[numdim-1] + sizeof(cell)-1) / sizeof(cell);
2290 /* Scrap the code generated */
2292 stgdel(_index, _code);
2294 if (tag == pc_tag_string && numdim && dim[numdim-1]) {
2298 memset(dim, 0, sizeof(int)*sDIMEN_MAX);
2300 genarray(numdim, autozero);
2306 if (getstates(name))
2307 error(88,name); /* local variables may not have states */
2308 if (ident==iARRAY || fstatic) {
2310 aligndata(sc_dataalign);
2313 cur_lit=litidx; /* save current index in the literal table */
2314 if (numdim && !dim[numdim-1])
2316 initials(ident,tag,&size,dim,numdim,enumroot);
2317 if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
2318 slength = glbstringread;
2321 return ident; /* error message already given */
2325 /* reserve memory (on the stack) for the variable */
2327 /* write zeros for uninitialized fields */
2328 while (litidx<cur_lit+size)
2330 sym=addvariable2(name,(cur_lit+glb_declared)*sizeof(cell),ident,sSTATIC,
2331 tag,dim,numdim,idxtag,slength);
2332 } else if (ident!=iREFARRAY) {
2333 declared+=(int)size; /* variables are put on stack, adjust "declared" */
2334 sym=addvariable2(name,-declared*sizeof(cell),ident,sLOCAL,tag,
2335 dim,numdim,idxtag,slength);
2336 if (ident==iVARIABLE) {
2338 stgset(TRUE); /* start stage-buffering */
2340 staging_start=stgidx;
2342 markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
2343 modstk(-(int)size*sizeof(cell));
2344 markstack(MEMUSE_STATIC, size);
2345 assert(curfunc!=NULL);
2346 assert((curfunc->usage & uNATIVE)==0);
2347 if (curfunc->x.stacksize<declared+1)
2348 curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
2349 } else if (ident==iREFARRAY) {
2350 declared+=1; /* one cell for address */
2351 sym=addvariable(name,-declared*sizeof(cell),ident,sLOCAL,tag,dim,numdim,idxtag);
2352 //markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
2353 /* genarray() pushes the address onto the stack, so we don't need to call modstk() here! */
2354 markheap(MEMUSE_DYNAMIC, 0);
2355 markstack(MEMUSE_STATIC, 1);
2356 assert(curfunc != NULL && ((curfunc->usage & uNATIVE) == 0));
2357 if (curfunc->x.stacksize<declared+1)
2358 curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
2360 /* now that we have reserved memory for the variable, we can proceed
2361 * to initialize it */
2362 assert(sym!=NULL); /* we declared it, it must be there */
2363 sym->compound=nestlevel; /* for multiple declaration/shadowing check */
2366 if (!fstatic) { /* static variables already initialized */
2367 if (ident==iVARIABLE) {
2368 /* simple variable, also supports initialization */
2369 int ctag = tag; /* set to "tag" by default */
2370 int explicit_init=FALSE;/* is the variable explicitly initialized? */
2372 if (matchtoken('=')) {
2375 cident=doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE);
2379 ldconst(0,sPRI); /* uninitialized variable, set to zero */
2382 /* now try to save the value (still in PRI) in the variable */
2384 lval.ident=iVARIABLE;
2387 check_userop(NULL,ctag,lval.tag,2,NULL,&ctag);
2389 markexpr(sEXPR,NULL,0); /* full expression ends after the store */
2391 assert(staging); /* end staging phase (optimize expression) */
2392 stgout(staging_start);
2394 if (!matchtag_string(cident, ctag) && !matchtag(tag,ctag,TRUE))
2395 error(213); /* tag mismatch */
2396 /* if the variable was not explicitly initialized, reset the
2397 * "uWRITTEN" flag that store() set */
2399 sym->usage &= ~uWRITTEN;
2400 } else if (ident!=iREFARRAY) {
2402 assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax);
2403 assert(size>0 && size>=sym->dim.array.length);
2404 assert(numdim>1 || size==sym->dim.array.length);
2406 /* final literal values that are zero make no sense to put in the literal
2407 * pool, because values get zero-initialized anyway; we check for this,
2408 * because users often explicitly initialize strings to ""
2410 while (litidx>cur_lit && litq[litidx-1]==0)
2412 /* if the array is not completely filled, set all values to zero first */
2413 if (litidx-cur_lit<size && (ucell)size<CELL_MAX)
2414 fillarray(sym,size*sizeof(cell),0);
2416 if (cur_lit<litidx) {
2417 /* check whether the complete array is set to a single value; if
2418 * it is, more compact code can be generated */
2419 cell first=litq[cur_lit];
2421 for (i=cur_lit; i<litidx && litq[i]==first; i++)
2424 /* all values are the same */
2425 fillarray(sym,(litidx-cur_lit)*sizeof(cell),first);
2426 litidx=cur_lit; /* reset literal table */
2428 /* copy the literals to the array */
2429 ldconst((cur_lit+glb_declared)*sizeof(cell),sPRI);
2430 copyarray(sym,(litidx-cur_lit)*sizeof(cell));
2435 } while (matchtoken(',')); /* enddo */ /* more? */
2436 needtoken(tTERM); /* if not comma, must be semicolumn */
2440 /* this function returns the maximum value for a cell in case of an error
2441 * (invalid dimension).
2443 static cell calc_arraysize(int dim[],int numdim,int cur)
2448 /* the return value is in cells, not bytes */
2449 assert(cur>=0 && cur<=numdim);
2452 subsize=calc_arraysize(dim,numdim,cur+1);
2453 newsize=dim[cur]+dim[cur]*subsize;
2454 if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize<(ucell)subsize
2455 || newsize*sizeof(cell)>=CELL_MAX)
2460 static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment,
2461 int startlit,constvalue *lastdim,int *skipdim)
2467 assert(cur>=0 && cur<numdim);
2468 assert(increment>=0);
2469 assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx);
2474 /* 2 or more dimensions left, fill in an indirection vector */
2477 for (d=0; d<dim[cur]; d++)
2478 litq[base++]=(dim[cur]+d*(dim[cur+1]-1)+increment) * sizeof(cell);
2479 accum=dim[cur]*(dim[cur+1]-1);
2481 /* final dimension is variable length */
2483 assert(dim[cur+1]==0);
2484 assert(lastdim!=NULL);
2485 assert(skipdim!=NULL);
2487 /* skip the final dimension sizes for all earlier major dimensions */
2488 for (d=0,ld=lastdim->next; d<*skipdim; d++,ld=ld->next) {
2491 for (d=0; d<dim[cur]; d++) {
2493 assert(strtol(ld->name,NULL,16)==d);
2494 litq[base++]=(dim[cur]+accum+increment) * sizeof(cell);
2500 /* create the indirection tables for the lower level */
2501 if (cur+2<numdim) { /* are there at least 2 dimensions below this one? */
2502 increment+=(dim[cur]-1)*dim[cur+1]; /* this many indirection tables follow */
2503 for (d=0; d<dim[cur]; d++)
2504 increment+=adjust_indirectiontables(dim,numdim,cur+1,increment,-1,lastdim,skipdim);
2511 * Initialize global objects and local arrays.
2512 * size==array cells (count), if 0 on input, the routine counts the number of elements
2513 * tag==required tagname id (not the returned tag)
2515 * Global references: litidx (altered)
2517 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
2518 constvalue *enumroot)
2525 if (!matchtoken('=')) {
2526 assert(ident!=iARRAY || numdim>0);
2527 if (ident==iARRAY && dim[numdim-1]==0) {
2528 /* declared as "myvar[];" which is senseless (note: this *does* make
2529 * sense in the case of a iREFARRAY, which is a function parameter)
2531 error(9); /* array has zero length -> invalid size */
2533 if (ident==iARRAY) {
2534 assert(numdim>0 && numdim<=sDIMEN_MAX);
2535 *size=calc_arraysize(dim,numdim,0);
2536 if (*size==(cell)CELL_MAX) {
2537 error(9); /* array is too big -> invalid size */
2540 /* first reserve space for the indirection vectors of the array, then
2541 * adjust it to contain the proper values
2542 * (do not use dumpzero(), as it bypasses the literal queue)
2544 for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
2546 if (dim[numdim-1]!=0) /* error 9 has already been given */
2547 adjust_indirectiontables(dim,numdim,0,0,curlit,NULL,NULL);
2552 if (ident==iVARIABLE) {
2554 init(ident,&ctag,NULL);
2555 if (!matchtag(tag,ctag,TRUE))
2556 error(213); /* tag mismatch */
2560 *size=initvector(ident,tag,dim[0],FALSE,enumroot,NULL);
2562 int errorfound=FALSE;
2563 int counteddim[sDIMEN_MAX];
2565 constvalue lastdim={NULL,"",0,0}; /* sizes of the final dimension */
2568 if (dim[numdim-1]!=0)
2569 *size=calc_arraysize(dim,numdim,0); /* calc. full size, if known */
2570 /* already reserve space for the indirection tables (for an array with
2572 * (do not use dumpzero(), as it bypasses the literal queue)
2574 for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
2576 /* now initialize the sub-arrays */
2577 memset(counteddim,0,sizeof counteddim);
2578 initarray(ident,tag,dim,numdim,0,curlit,counteddim,&lastdim,enumroot,&errorfound);
2579 /* check the specified array dimensions with the initializer counts */
2580 for (idx=0; idx<numdim-1; idx++) {
2582 dim[idx]=counteddim[idx];
2583 } else if (counteddim[idx]<dim[idx]) {
2584 error(52); /* array is not fully initialized */
2586 } else if (counteddim[idx]>dim[idx]) {
2587 error(18); /* initialization data exceeds declared size */
2591 if (numdim>1 && dim[numdim-1]==0) {
2592 /* also look whether, by any chance, all "counted" final dimensions are
2593 * the same value; if so, we can store this
2595 constvalue *ld=lastdim.next;
2597 for (d=0; d<dim[numdim-2]; d++) {
2599 assert(strtol(ld->name,NULL,16)==d);
2602 else if (match!=ld->value)
2606 if (d==dim[numdim-2])
2607 dim[numdim-1]=match;
2609 /* after all arrays have been initalized, we know the (major) dimensions
2610 * of the array and we can properly adjust the indirection vectors
2613 adjust_indirectiontables(dim,numdim,0,0,curlit,&lastdim,&skipdim);
2614 delete_consttable(&lastdim); /* clear list of minor dimension sizes */
2619 *size=litidx-curlit; /* number of elements defined */
2622 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
2623 int startlit,int counteddim[],constvalue *lastdim,
2624 constvalue *enumroot,int *errorfound)
2626 cell dsize,totalsize;
2629 assert(cur>=0 && cur<numdim);
2630 assert(startlit>=0);
2631 assert(cur+2<=numdim);/* there must be 2 dimensions or more to do */
2632 assert(errorfound!=NULL && *errorfound==FALSE);
2635 for (idx=0,abortparse=FALSE; !abortparse; idx++) {
2636 /* In case the major dimension is zero, we need to store the offset
2637 * to the newly detected sub-array into the indirection table; i.e.
2638 * this table needs to be expanded and updated.
2639 * In the current design, the indirection vectors for a multi-dimensional
2640 * array are adjusted after parsing all initializers. Hence, it is only
2641 * necessary at this point to reserve space for an extra cell in the
2642 * indirection vector.
2645 litinsert(0,startlit);
2646 } else if (idx>=dim[cur]) {
2647 error(18); /* initialization data exceeds array size */
2651 dsize=initarray(ident,tag,dim,numdim,cur+1,startlit,counteddim,
2652 lastdim,enumroot,errorfound);
2654 dsize=initvector(ident,tag,dim[cur+1],TRUE,enumroot,errorfound);
2655 /* The final dimension may be variable length. We need to keep the
2656 * lengths of the final dimensions in order to set the indirection
2657 * vectors for the next-to-last dimension.
2659 append_constval(lastdim,itoh(idx),dsize,0);
2662 if (*errorfound || !matchtoken(','))
2666 assert(counteddim!=NULL);
2667 if (counteddim[cur]>0) {
2668 if (idx<counteddim[cur])
2669 error(52); /* array is not fully initialized */
2670 else if (idx>counteddim[cur])
2671 error(18); /* initialization data exceeds declared size */
2673 counteddim[cur]=idx;
2675 return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */
2679 * Initialize a single dimensional array
2681 static cell initvector(int ident,int tag,cell size,int fillzero,
2682 constvalue *enumroot,int *errorfound)
2684 cell prev1=0,prev2=0;
2689 assert(ident==iARRAY || ident==iREFARRAY);
2690 if (matchtoken('{')) {
2691 constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL;
2693 int fieldlit=litidx;
2695 if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */
2699 if ((ellips=matchtoken(tELLIPS))!=0)
2701 /* for enumeration fields, allow another level of braces ("{...}") */
2702 matchbrace=0; /* preset */
2704 if (enumfield!=NULL)
2705 matchbrace=matchtoken('{');
2708 prev1=init(ident,&ctag,errorfound);
2711 if ((ellips=matchtoken(tELLIPS))!=0)
2713 if (!matchtoken(',')) {
2718 /* if this array is based on an enumeration, fill the "field" up with
2719 * zeros, and toggle the tag
2721 if (enumroot!=NULL && enumfield==NULL)
2722 error(227); /* more initializers than enum fields */
2723 rtag=tag; /* preset, may be overridden by enum field tag */
2724 if (enumfield!=NULL) {
2726 int cmptag=enumfield->index;
2727 symbol *symfield=findconst(enumfield->name,&cmptag);
2729 error(91,enumfield->name); /* ambiguous constant, needs tag override */
2730 assert(symfield!=NULL);
2731 assert(fieldlit<litidx);
2732 if (litidx-fieldlit>symfield->dim.array.length)
2733 error(228); /* length of initializer exceeds size of the enum field */
2740 for (i=litidx-fieldlit; i<symfield->dim.array.length; i++) {
2744 rtag=symfield->x.tags.index; /* set the expected tag to the index tag */
2745 enumfield=enumfield->next;
2747 if (!matchtag(rtag,ctag,TRUE))
2748 error(213); /* tag mismatch */
2749 } while (matchtoken(',')); /* do */
2752 init(ident,&ctag,errorfound);
2753 if (!matchtag(tag,ctag,TRUE))
2754 error(213); /* tagname mismatch */
2756 /* fill up the literal queue with a series */
2758 cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2;
2759 if (size==0 || (litidx-curlit)==0)
2760 error(41); /* invalid ellipsis, array size unknown */
2761 else if ((litidx-curlit)==(int)size)
2762 error(18); /* initialisation data exceeds declared size */
2763 while ((litidx-curlit)<(int)size) {
2768 if (fillzero && size>0) {
2769 while ((litidx-curlit)<(int)size)
2773 size=litidx-curlit; /* number of elements defined */
2774 } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */
2775 error(18); /* initialisation data exceeds declared size */
2776 litidx=(int)size+curlit; /* avoid overflow in memory moves */
2783 * Evaluate one initializer.
2785 static cell init(int ident,int *tag,int *errorfound)
2789 if (matchtoken(tSTRING)){
2790 /* lex() automatically stores strings in the literal table (and
2791 * increases "litidx") */
2792 if (ident==iVARIABLE) {
2793 error(6); /* must be assigned to an array */
2794 litidx=1; /* reset literal queue */
2797 } else if (constexpr(&i,tag,NULL)){
2798 litadd(i); /* store expression result in literal table */
2800 if (errorfound!=NULL)
2808 * Get required array size
2810 static cell needsub(int *tag,constvalue **enumroot)
2818 *enumroot=NULL; /* preset */
2819 if (matchtoken(']')) /* we have already seen "[" */
2820 return 0; /* zero size (like "char msg[]") */
2822 constexpr(&val,tag,&sym); /* get value (must be constant expression) */
2824 error(9); /* negative array size is invalid; assumed zero */
2829 if (enumroot!=NULL) {
2830 /* get the field list for an enumeration */
2831 assert(*enumroot==NULL);/* should have been preset */
2832 assert(sym==NULL || sym->ident==iCONSTEXPR);
2833 if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) {
2834 assert(sym->dim.enumlist!=NULL);
2835 *enumroot=sym->dim.enumlist;
2839 return val; /* return array size */
2842 /* decl_const - declare a single constant
2845 static void decl_const(int vclass)
2847 char constname[sNAMEMAX+1];
2854 insert_docstring_separator(); /* see comment in newfunc() */
2856 tag=pc_addtag(NULL);
2857 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
2858 error(20,str); /* invalid symbol name */
2859 symbolline=fline; /* save line where symbol was found */
2860 strcpy(constname,str); /* save symbol name */
2862 constexpr(&val,&exprtag,NULL); /* get value */
2863 /* add_constant() checks for duplicate definitions */
2864 if (!matchtag(tag,exprtag,FALSE)) {
2865 /* temporarily reset the line number to where the symbol was defined */
2868 error(213); /* tagname mismatch */
2871 sym=add_constant(constname,val,vclass,tag);
2873 sc_attachdocumentation(sym);/* attach any documenation to the constant */
2874 } while (matchtoken(',')); /* enddo */ /* more? */
2879 * declstruct - declare a struct type
2881 static void declstruct(void)
2889 /* get the explicit tag (required!) */
2890 tok = lex(&val,&str);
2896 if (pstructs_find(str) != NULL)
2898 /* :TODO: change to "struct requires unique struct name" */
2902 pstruct = pstructs_add(str);
2908 if (matchtoken('}'))
2914 memset(&arg, 0, sizeof(structarg_t));
2915 tok = lex(&val,&str);
2919 tok = lex(&val,&str);
2924 arg.ident = iREFERENCE;
2925 tok = lex(&val,&str);
2929 arg.tag = pc_addtag(str);
2930 tok = lex(&val,&str);
2934 error(1, "-identifier-", str);
2937 strcpy(arg.name, str);
2938 if (matchtoken('['))
2940 if (arg.ident == iREFERENCE)
2942 error(67, arg.name);
2947 constvalue *enumroot;
2949 if (arg.dimcount == sDIMEN_MAX)
2954 size = needsub(&ignore_tag, &enumroot);
2955 arg.dims[arg.dimcount++] = size;
2956 } while (matchtoken('['));
2957 /* Handle strings */
2958 if (arg.tag == pc_tag_string && arg.dims[arg.dimcount-1])
2960 arg.dims[arg.dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
2962 if (arg.dimcount == 1 && !arg.dims[arg.dimcount-1])
2964 arg.ident = iREFARRAY;
2966 } else if (!arg.ident) {
2967 arg.ident = iVARIABLE;
2969 if (pstructs_addarg(pstruct, &arg) == NULL)
2971 /* :TODO: change to "struct member name appears more than once" */
2974 } while (matchtoken(','));
2976 matchtoken(';'); /* eat up optional semicolon */
2981 * dofuncenum - declare function enumerations
2983 static void dofuncenum(int listmode)
2988 char tagname[sNAMEMAX+1];
2990 funcenum_t *fenum = NULL;
2993 /* get the explicit tag (required!) */
2994 int l = lex(&val,&str);
3000 /* This tag can't already exist! */
3001 cur=tagname_tab.next;
3004 if (strcmp(cur->name, str) == 0)
3006 /* Another bad one... */
3007 if (!(cur->value & FUNCTAG))
3015 strcpy(tagname, str);
3017 fenum = funcenums_add(tagname);
3026 if (listmode && matchtoken('}'))
3032 memset(&func, 0, sizeof(func));
3033 func.ret_tag = pc_addtag(NULL); /* Get the return tag */
3034 l = lex(&val, &str);
3036 * Right now, there is a problem. We can't pass non-public function pointer addresses around,
3037 * because the address isn't known until the final reparse. Unfortunately, you can write code
3038 * before the address is known, because Pawn's compiler isn't truly multipass.
3040 * When you call a function, there's no problem, because it does not write the address.
3041 * The assembly looks like this:
3043 * Then, only at assembly time (once all passes are done), does it know the address.
3044 * I do not see any solution to this because there is no way I know to inject the function name
3045 * rather than the constant value. And even if we could, we'd have to change the assembler recognize that.
3050 } else */if (l == tPUBLIC) {
3051 func.type = uPUBLIC;
3053 error(1, "-public-", str);
3058 funcarg_t *arg = &(func.args[func.argcount]);
3061 if (matchtoken(')'))
3066 l = lex(&val, &str);
3069 if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
3071 error(1, "-identifier-", "&");
3073 arg->ident = iREFERENCE;
3074 } else if (l == tCONST) {
3075 if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
3077 error(1, "-identifier-", "const");
3080 } else if (l == tLABEL) {
3081 if (arg->tagcount > 0)
3083 error(1, "-identifier-", "-tagname-");
3085 arg->tags[arg->tagcount++] = pc_addtag(str);
3087 while (arg->tagcount < sTAGS_MAX)
3089 if (!matchtoken('_') && !needtoken(tSYMBOL))
3093 tokeninfo(&val, &ptr);
3094 arg->tags[arg->tagcount++] = pc_addtag(ptr);
3095 if (matchtoken('}'))
3104 } else if (l == tSYMBOL) {
3105 if (func.argcount >= sARGS_MAX)
3109 if (str[0] == PUBLIC_CHAR)
3113 if (matchtoken('['))
3116 if (arg->ident == iREFERENCE)
3122 constvalue *enumroot;
3124 if (arg->dimcount == sDIMEN_MAX)
3129 size = needsub(&ignore_tag, &enumroot);
3130 arg->dims[arg->dimcount] = size;
3132 } while (matchtoken('['));
3133 /* Handle strings */
3134 if ((arg->tagcount == 1 && arg->tags[0] == pc_tag_string)
3135 && arg->dims[arg->dimcount-1])
3137 arg->dims[arg->dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
3139 arg->ident=iREFARRAY;
3140 } else if (arg->ident == 0) {
3141 arg->ident = iVARIABLE;
3144 if (matchtoken('='))
3147 arg->ommittable = TRUE;
3148 func.ommittable = TRUE;
3149 } else if (func.ommittable) {
3153 } else if (l == tELLIPS) {
3154 if (arg->ident == iVARIABLE)
3158 arg->ident = iVARARGS;
3163 } while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(','));
3165 for (i=0; i<func.argcount; i++)
3167 if (func.args[i].tagcount == 0)
3169 func.args[i].tags[0] = 0;
3170 func.args[i].tagcount = 1;
3173 functags_add(fenum, &func);
3178 } while (matchtoken(','));
3183 matchtoken(';'); /* eat an optional semicolon. nom nom nom */
3184 errorset(sRESET, 0);
3187 /* decl_enum - declare enumerated constants
3190 static void decl_enum(int vclass)
3192 char enumname[sNAMEMAX+1],constname[sNAMEMAX+1];
3193 cell val,value,size;
3195 int tag,explicittag;
3196 cell increment,multiplier;
3197 constvalue *enumroot;
3200 /* get an explicit tag, if any (we need to remember whether an explicit
3201 * tag was passed, even if that explicit tag was "_:", so we cannot call
3204 if (lex(&val,&str)==tLABEL) {
3213 /* get optional enum name (also serves as a tag if no explicit tag was set) */
3214 if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */
3215 strcpy(enumname,str); /* save enum name (last constant) */
3217 tag=pc_addtag(enumname);
3219 lexpush(); /* analyze again */
3223 /* get increment and multiplier */
3226 if (matchtoken('(')) {
3227 if (matchtoken(taADD)) {
3228 constexpr(&increment,NULL,NULL);
3229 } else if (matchtoken(taMULT)) {
3230 constexpr(&multiplier,NULL,NULL);
3231 } else if (matchtoken(taSHL)) {
3232 constexpr(&val,NULL,NULL);
3239 if (strlen(enumname)>0) {
3240 /* already create the root symbol, so the fields can have it as their "parent" */
3241 enumsym=add_constant(enumname,0,vclass,tag);
3243 enumsym->usage |= uENUMROOT;
3244 /* start a new list for the element names */
3245 if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL)
3246 error(103); /* insufficient memory (fatal error) */
3247 memset(enumroot,0,sizeof(constvalue));
3254 /* go through all constants */
3255 value=0; /* default starting value */
3257 int idxtag,fieldtag;
3259 if (matchtoken('}')) { /* quick exit if '}' follows ',' */
3263 idxtag=pc_addtag(NULL); /* optional explicit item tag */
3264 if (needtoken(tSYMBOL)) { /* read in (new) token */
3265 tokeninfo(&val,&str); /* get the information */
3266 strcpy(constname,str); /* save symbol name */
3270 size=increment; /* default increment of 'val' */
3271 fieldtag=0; /* default field tag */
3272 if (matchtoken('[')) {
3273 constexpr(&size,&fieldtag,NULL); /* get size */
3276 /* :TODO: do we need a size modifier here for pc_tag_string? */
3277 if (matchtoken('='))
3278 constexpr(&value,NULL,NULL); /* get value */
3279 /* add_constant() checks whether a variable (global or local) or
3280 * a constant with the same name already exists
3282 sym=add_constant(constname,value,vclass,tag);
3284 continue; /* error message already given */
3285 /* set the item tag and the item size, for use in indexing arrays */
3286 sym->x.tags.index=idxtag;
3287 sym->x.tags.field=fieldtag;
3288 sym->dim.array.length=size;
3289 sym->dim.array.level=0;
3290 sym->dim.array.slength=0;
3291 sym->parent=enumsym;
3292 /* add the constant to a separate list as well */
3293 if (enumroot!=NULL) {
3294 sym->usage |= uENUMFIELD;
3295 append_constval(enumroot,constname,value,tag);
3300 value*=size*multiplier;
3301 } while (matchtoken(','));
3302 needtoken('}'); /* terminates the constant list */
3303 matchtoken(';'); /* eat an optional ; */
3305 /* set the enum name to the "next" value (typically the last value plus one) */
3306 if (enumsym!=NULL) {
3307 assert((enumsym->usage & uENUMROOT)!=0);
3308 enumsym->addr=value;
3309 /* assign the constant list */
3310 assert(enumroot!=NULL);
3311 enumsym->dim.enumlist=enumroot;
3312 sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */
3316 static int getstates(const char *funcname)
3318 char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1];
3321 constvalue *automaton;
3325 int count,listsize,state_id;
3327 if (!matchtoken('<'))
3329 if (matchtoken('>'))
3330 return -1; /* special construct: all other states (fall-back) */
3338 if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
3340 tokeninfo(&val,&str);
3341 assert(strlen(str)<sizeof fsaname);
3342 strcpy(fsaname,str); /* assume this is the name of the automaton */
3343 if (islabel || matchtoken(':')) {
3344 /* token is an automaton name, add the name and get a new token */
3345 if (!needtoken(tSYMBOL))
3347 tokeninfo(&val,&str);
3348 assert(strlen(str)<sizeof statename);
3349 strcpy(statename,str);
3351 /* the token was the state name (part of an anynymous automaton) */
3352 assert(strlen(fsaname)<sizeof statename);
3353 strcpy(statename,fsaname);
3356 if (fsa<0 || fsaname[0]!='\0') {
3357 automaton=automaton_add(fsaname);
3358 assert(automaton!=NULL);
3359 if (fsa>=0 && automaton->index!=fsa)
3360 error(83,funcname); /* multiple automatons for a single function/variable */
3361 fsa=automaton->index;
3363 state=state_add(statename,fsa);
3364 /* add this state to the state combination list (it will be attached to the
3365 * automaton later) */
3366 state_buildlist(&list,&listsize,&count,(int)state->value);
3367 } while (matchtoken(','));
3371 assert(automaton!=NULL);
3373 state_id=state_addlist(list,count,fsa);
3376 /* error is already given */
3385 static void attachstatelist(symbol *sym, int state_id)
3388 if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0))
3389 error(21,sym->name); /* function already defined, either without states or the current definition has no states */
3392 /* add the state list id */
3393 constvalue *stateptr;
3394 if (sym->states==NULL) {
3395 if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL)
3396 error(103); /* insufficient memory (fatal error) */
3397 memset(sym->states,0,sizeof(constvalue));
3399 /* see whether the id already exists (add new state only if it does not
3402 assert(sym->states!=NULL);
3403 for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next)
3405 assert(state_id<=SHRT_MAX);
3407 append_constval(sym->states,"",code_idx,(short)state_id);
3408 else if (stateptr->value==0)
3409 stateptr->value=code_idx;
3411 error(84,sym->name);
3412 /* also check for another conflicting situation: a fallback function
3413 * without any states
3415 if (state_id==-1 && sc_status!=statFIRST) {
3416 /* in the second round, all states should have been accumulated */
3417 assert(sym->states!=NULL);
3418 for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next)
3421 error(85,sym->name); /* no states are defined for this function */
3427 * Finds a function in the global symbol table or creates a new entry.
3428 * It does some basic processing and error checking.
3430 SC_FUNC symbol *fetchfunc(char *name,int tag)
3434 if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */
3435 if (sym->ident!=iFUNCTN) {
3436 error(21,name); /* yes, but not as a function */
3437 return NULL; /* make sure the old symbol is not damaged */
3438 } else if ((sym->usage & uNATIVE)!=0) {
3439 error(21,name); /* yes, and it is a native */
3441 assert(sym->vclass==sGLOBAL);
3442 if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag)
3443 error(25); /* mismatch from earlier prototype */
3444 if ((sym->usage & uDEFINE)==0) {
3445 /* as long as the function stays undefined, update the address and the tag */
3446 if (sym->states==NULL)
3451 /* don't set the "uDEFINE" flag; it may be a prototype */
3452 sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0);
3453 assert(sym!=NULL); /* fatal error 103 must be given on error */
3454 /* assume no arguments */
3455 sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo));
3456 sym->dim.arglist[0].ident=0;
3457 /* set library ID to NULL (only for native functions) */
3459 /* set the required stack size to zero (only for non-native functions) */
3460 sym->x.stacksize=1; /* 1 for PROC opcode */
3462 if (pc_deprecate!=NULL) {
3464 sym->flags|=flgDEPRECATED;
3465 if (sc_status==statWRITE) {
3466 if (sym->documentation!=NULL) {
3467 free(sym->documentation);
3468 sym->documentation=NULL;
3470 sym->documentation=pc_deprecate;
3480 /* This routine adds symbolic information for each argument.
3482 static void define_args(void)
3486 /* At this point, no local variables have been declared. All
3487 * local symbols are function arguments.
3491 assert(sym->ident!=iLABEL);
3492 assert(sym->vclass==sLOCAL);
3493 markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */
3498 static int operatorname(char *name)
3506 /* check the operator */
3507 opertok=lex(&val,&str);
3519 name[0]=(char)opertok;
3542 error(7); /* operator cannot be redefined (or bad operator name) */
3549 static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag)
3554 char tmpname[sNAMEMAX+1];
3560 assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL);
3561 /* count arguments and save (first two) tags */
3562 while (arg=&sym->dim.arglist[count], arg->ident!=0) {
3565 error(65,count+1); /* function argument may only have a single tag */
3566 else if (arg->numtags==1)
3567 tags[count]=arg->tags[0];
3569 if (opertok=='~' && count==0) {
3570 if (arg->ident!=iREFARRAY)
3571 error(73,arg->name);/* must be an array argument */
3573 if (arg->ident!=iVARIABLE)
3574 error(66,arg->name);/* must be non-reference argument */
3576 if (arg->hasdefault)
3577 error(59,arg->name); /* arguments of an operator may not have a default value */
3581 /* for '!', '++' and '--', count must be 1
3582 * for '-', count may be 1 or 2
3583 * for '=', count must be 1, and the resulttag is also important
3584 * for all other (binary) operators and the special '~' operator, count must be 2
3592 error(62); /* number or placement of the operands does not fit the operator */
3595 if (count!=1 && count!=2)
3596 error(62); /* number or placement of the operands does not fit the operator */
3600 error(62); /* number or placement of the operands does not fit the operator */
3603 if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0))
3604 error(64); /* cannot change predefined operators */
3606 /* change the operator name */
3607 assert(strlen(opername)>0);
3608 operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag);
3609 if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) {
3611 if ((oldsym->usage & uDEFINE)!=0) {
3612 char errname[2*sNAMEMAX+16];
3613 funcdisplayname(errname,tmpname);
3614 error(21,errname); /* symbol already defined */
3616 sym->usage|=oldsym->usage; /* copy flags from the previous definition */
3617 for (i=0; i<oldsym->numrefers; i++)
3618 if (oldsym->refer[i]!=NULL)
3619 refer_symbol(sym,oldsym->refer[i]);
3620 delete_symbol(&glbtab,oldsym);
3622 strcpy(sym->name,tmpname);
3623 sym->hash=namehash(sym->name);/* calculate new hash */
3625 /* operators should return a value, except the '~' operator */
3627 sym->usage |= uRETVALUE;
3632 static int check_operatortag(int opertok,int resulttag,char *opername)
3634 assert(opername!=NULL && strlen(opername)>0);
3643 if (resulttag!=pc_addtag("bool")) {
3644 error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */
3650 error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */
3658 static char *tag2str(char *dest,int tag)
3662 sprintf(dest,"0%x",tag);
3663 return isdigit(dest[1]) ? &dest[1] : dest;
3666 SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag)
3668 char tagstr1[10], tagstr2[10];
3671 assert(numtags>=1 && numtags<=2);
3672 opertok= (opername[1]=='\0') ? opername[0] : 0;
3674 sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1));
3675 else if (numtags==1 || opertok=='~')
3676 sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1));
3678 sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2));
3682 static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname)
3687 /* tags are only positive, so if the function name starts with a '-',
3688 * the operator is an unary '-' or '--' operator.
3695 *tag1=(int)strtol(fname,&ptr,16);
3696 unary= ptr==fname; /* unary operator if it doesn't start with a tag name */
3698 assert(!unary || *tag1==0);
3700 for (name=opname; !isdigit(*ptr); )
3703 *tag2=(int)strtol(ptr,NULL,16);
3707 constvalue *find_tag_byval(int tag)
3710 tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG);
3712 tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);
3716 SC_FUNC char *funcdisplayname(char *dest,char *funcname)
3720 constvalue *tagsym[2];
3723 if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') {
3725 strcpy(dest,funcname);
3729 unary=parse_funcname(funcname,&tags[0],&tags[1],opname);
3730 tagsym[1]=find_tag_byval(tags[1]);
3731 assert(tagsym[1]!=NULL);
3733 sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name);
3735 tagsym[0]=find_tag_byval(tags[0]);
3736 assert(tagsym[0]!=NULL);
3737 /* special case: the assignment operator has the return value as the 2nd tag */
3738 if (opname[0]=='=' && opname[1]=='\0')
3739 sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name);
3741 sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name);
3746 static void funcstub(int fnative)
3748 int tok,tag,fpublic;
3751 char symbolname[sNAMEMAX+1];
3752 int idxtag[sDIMEN_MAX];
3753 int dim[sDIMEN_MAX];
3760 litidx=0; /* clear the literal pool */
3761 assert(loctab.next==NULL); /* local symbol table should be empty */
3763 tag=pc_addtag(NULL); /* get the tag of the return value */
3765 while (matchtoken('[')) {
3766 /* the function returns an array, get this tag for the index and the array
3769 if (numdim == sDIMEN_MAX) {
3770 error(53); /* exceeding maximum number of dimensions */
3773 size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
3775 error(9); /* invalid array size */
3776 #if INT_MAX < LONG_MAX
3778 error(105); /* overflow, exceeding capacity */
3780 dim[numdim++]=(int)size;
3783 if (tag == pc_tag_string && numdim && dim[numdim-1])
3784 dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
3787 fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR);
3789 if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR)
3790 error(42); /* invalid combination of class specifiers */
3792 if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC)
3796 if (tok==tOPERATOR) {
3797 opertok=operatorname(symbolname);
3799 return; /* error message already given */
3800 check_operatortag(opertok,tag,symbolname);
3802 if (tok!=tSYMBOL && freading) {
3803 error(10); /* illegal function or declaration */
3806 strcpy(symbolname,str);
3808 needtoken('('); /* only functions may be native/forward */
3810 sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
3814 sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
3815 sym->x.lib=curlibrary;
3816 } else if (fpublic) {
3817 sym->usage|=uPUBLIC;
3819 sym->usage|=uFORWARD;
3821 declargs(sym,FALSE);
3822 /* "declargs()" found the ")" */
3823 sc_attachdocumentation(sym); /* attach any documenation to the function */
3824 if (!operatoradjust(opertok,sym,symbolname,tag))
3825 sym->usage &= ~uDEFINE;
3827 if (getstates(symbolname)!=0) {
3828 if (fnative || opertok!=0)
3829 error(82); /* native functions and operators may not have states */
3831 error(231); /* ignoring state specifications on forward declarations */
3834 /* for a native operator, also need to specify an "exported" function name;
3835 * for a native function, this is optional
3840 lexpush(); /* push back, for matchtoken() to retrieve again */
3842 if (matchtoken('=')) {
3843 /* allow number or symbol */
3844 if (matchtoken(tSYMBOL)) {
3845 tokeninfo(&val,&str);
3846 insert_alias(sym->name,str);
3848 constexpr(&val,NULL,NULL);
3850 /* At the moment, I have assumed that this syntax is only valid if
3851 * val < 0. To properly mix "normal" native functions and indexed
3852 * native functions, one should use negative indices anyway.
3853 * Special code for a negative index in sym->addr exists in SC4.C
3854 * (ffcall()) and in SC6.C (the loops for counting the number of native
3855 * variables and for writing them).
3862 /* attach the array to the function symbol */
3865 sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag);
3869 litidx=0; /* clear the literal pool */
3870 delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
3873 /* newfunc - begin a function
3875 * This routine is called from "parse" and tries to make a function
3876 * out of the following text
3878 * Global references: funcstatus,lastst,litidx
3881 * declared (altered)
3882 * glb_declared (altered)
3883 * sc_alignnext (altered)
3885 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock)
3888 int argcnt,tok,tag,funcline;
3889 int opertok,opererror;
3890 char symbolname[sNAMEMAX+1];
3892 cell val,cidx,glbdecl;
3896 assert(litidx==0); /* literal queue should be empty */
3897 litidx=0; /* clear the literal pool (should already be empty) */
3899 lastst=0; /* no statement yet */
3900 cidx=0; /* just to avoid compiler warnings */
3902 assert(loctab.next==NULL); /* local symbol table should be empty */
3903 filenum=fcurrent; /* save file number at the start of the declaration */
3905 if (firstname!=NULL) {
3906 assert(strlen(firstname)<=sNAMEMAX);
3907 strcpy(symbolname,firstname); /* save symbol name */
3910 tag= (firsttag>=0) ? firsttag : pc_addtag(NULL);
3913 if (tok==tNATIVE || tok==tPUBLIC && stock)
3914 error(42); /* invalid combination of class specifiers */
3915 if (tok==tOPERATOR) {
3916 opertok=operatorname(symbolname);
3918 return TRUE; /* error message already given */
3919 check_operatortag(opertok,tag,symbolname);
3921 if (tok!=tSYMBOL && freading) {
3922 error(20,str); /* invalid symbol name */
3925 assert(strlen(str)<=sNAMEMAX);
3926 strcpy(symbolname,str);
3929 /* check whether this is a function or a variable declaration */
3930 if (!matchtoken('('))
3932 /* so it is a function, proceed */
3933 funcline=fline; /* save line at which the function is defined */
3934 if (symbolname[0]==PUBLIC_CHAR) {
3935 fpublic=TRUE; /* implicitly public function */
3937 error(42); /* invalid combination of class specifiers */
3939 sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
3940 if (sym==NULL || (sym->usage & uNATIVE)!=0)
3941 return TRUE; /* it was recognized as a function declaration, but not as a valid one */
3943 sym->usage|=uPUBLIC;
3945 sym->fnumber=filenum;
3946 /* if the function was used before being declared, and it has a tag for the
3947 * result, add a third pass (as second "skimming" parse) because the function
3948 * result may have been used with user-defined operators, which have now
3949 * been incorrectly flagged (as the return tag was unknown at the time of
3952 if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) {
3953 int curstatus=sc_status;
3954 sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */
3955 #if 0 /* SourceMod - silly, should be removed in first pass, so removed */
3958 sc_status=curstatus;
3959 sc_reparse=TRUE; /* must add another pass to "initial scan" phase */
3961 #if 0 /* Not used for SourceMod */
3962 /* we want public functions to be explicitly prototyped, as they are called
3965 if (fpublic && (sym->usage & uFORWARD)==0)
3966 error(235,symbolname);
3968 /* declare all arguments */
3969 argcnt=declargs(sym,TRUE);
3970 opererror=!operatoradjust(opertok,sym,symbolname,tag);
3971 if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) {
3973 error(5); /* "main()" and "entry()" functions may not have any arguments */
3974 sym->usage|=uREAD; /* "main()" is the program's entry point: always used */
3976 state_id=getstates(symbolname);
3977 if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0))
3978 error(82); /* operators may not have states, main() may neither */
3979 attachstatelist(sym,state_id);
3980 /* "declargs()" found the ")"; if a ";" appears after this, it was a
3982 if (matchtoken(';')) {
3983 sym->usage|=uFORWARD;
3984 if (!sc_needsemicolon)
3985 error(218); /* old style prototypes used with optional semicolumns */
3986 delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */
3989 /* so it is not a prototype, proceed */
3990 /* if this is a function that is not referred to (this can only be detected
3991 * in the second stage), shut code generation off */
3992 if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) {
3995 glbdecl=glb_declared;
3997 if ((sym->flags & flgDEPRECATED)!=0) {
3998 char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
3999 error(234,symbolname,ptr); /* deprecated (probably a public function) */
4002 sym->usage|=uDEFINE; /* set the definition flag */
4005 if (opertok!=0 && opererror)
4006 sym->usage &= ~uDEFINE;
4007 /* if the function has states, dump the label to the start of the function */
4009 constvalue *ptr=sym->states->next;
4011 assert(sc_status!=statWRITE || strlen(ptr->name)>0);
4012 if (ptr->index==state_id) {
4013 setlabel((int)strtol(ptr->name,NULL,16));
4019 startfunc(sym->name); /* creates stack frame */
4020 insert_dbgline(funcline);
4023 alignframe(sc_dataalign);
4026 declared=0; /* number of local cells */
4029 rettype=(sym->usage & uRETVALUE); /* set "return type" variable */
4031 define_args(); /* add the symbolic info for the function arguments */
4032 #if !defined SC_LIGHT
4033 if (matchtoken('{')) {
4036 /* Insert a separator so that comments following the statement will not
4037 * be attached to this function; they should be attached to the next
4038 * function. This is not a problem for functions having a compound block,
4039 * because the closing brace is an explicit "end token" for the function.
4040 * With single statement functions, the preprocessor may overread the
4041 * source code before the parser determines an "end of statement".
4043 insert_docstring_separator();
4046 sc_curstates=state_id;/* set state id, for accessing global state variables */
4047 statement(NULL,FALSE);
4049 if ((rettype & uRETVALUE)!=0)
4050 sym->usage|=uRETVALUE;
4052 /* This happens only in a very special (and useless) case, where a function
4053 * has only a single statement in its body (no compound block) and that
4054 * statement declares a new variable
4059 if ((lastst!=tRETURN) && (lastst!=tGOTO)){
4061 ffret(strcmp(sym->name,uENTRYFUNC)!=0);
4062 if ((sym->usage & uRETVALUE)!=0) {
4063 char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
4064 funcdisplayname(symname,sym->name);
4065 error(209,symname); /* function should return a value */
4069 sym->codeaddr=code_idx;
4070 sc_attachdocumentation(sym); /* attach collected documenation to the function */
4071 if (litidx) { /* if there are literals defined */
4072 glb_declared+=litidx;
4073 begdseg(); /* flip to DATA segment */
4074 dumplits(); /* dump literal strings */
4077 testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */
4078 delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */
4079 assert(loctab.next==NULL);
4081 if (sc_status==statSKIP) {
4082 sc_status=statWRITE;
4084 glb_declared=glbdecl;
4089 static int argcompare(arginfo *a1,arginfo *a2)
4093 #if 0 /* SourceMod uses case insensitive args for forwards */
4094 result= strcmp(a1->name,a2->name)==0; /* name */
4099 result= a1->ident==a2->ident; /* type/class */
4101 result= a1->usage==a2->usage; /* "const" flag */
4103 result= a1->numtags==a2->numtags; /* tags (number and names) */
4104 for (i=0; result && i<a1->numtags; i++)
4105 result= a1->tags[i]==a2->tags[i];
4107 result= a1->numdim==a2->numdim; /* array dimensions & index tags */
4108 for (level=0; result && level<a1->numdim; level++)
4109 result= a1->dim[level]==a2->dim[level];
4110 for (level=0; result && level<a1->numdim; level++)
4111 result= a1->idxtag[level]==a2->idxtag[level];
4113 result= a1->hasdefault==a2->hasdefault; /* availability of default value */
4114 if (a1->hasdefault) {
4115 if (a1->ident==iREFARRAY) {
4117 result= a1->defvalue.array.size==a2->defvalue.array.size;
4119 result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize;
4120 /* ??? should also check contents of the default array (these troubles
4121 * go away in a 2-pass compiler that forbids double declarations, but
4122 * Pawn currently does not forbid them) */
4125 if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0 || (a1->hasdefault & uCOUNTOF)!=0)
4126 result= a1->hasdefault==a2->hasdefault
4127 && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0
4128 && a1->defvalue.size.level==a2->defvalue.size.level;
4130 result= a1->defvalue.val==a2->defvalue.val;
4134 result= a1->defvalue_tag==a2->defvalue_tag;
4141 * This routine adds an entry in the local symbol table for each argument
4142 * found in the argument list. It returns the number of arguments.
4144 static int declargs(symbol *sym,int chkshadow)
4148 int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags;
4150 arginfo arg, *arglist;
4151 char name[sNAMEMAX+1];
4152 int ident,fpublic,fconst;
4155 /* if the function is already defined earlier, get the number of arguments
4156 * of the existing definition
4159 if ((sym->usage & uPROTOTYPED)!=0)
4160 while (sym->dim.arglist[oldargcnt].ident!=0)
4162 argcnt=0; /* zero aruments up to now */
4166 fpublic= (sym->usage & (uPUBLIC|uSTOCK))!=0;
4167 /* the '(' parantheses has already been parsed */
4168 if (!matchtoken(')')){
4169 do { /* there are arguments; process them */
4170 /* any legal name increases argument count (and stack offset) */
4177 if (ident!=iVARIABLE || numtags>0)
4178 error(1,"-identifier-","&");
4182 if (ident!=iVARIABLE || numtags>0)
4183 error(1,"-identifier-","const");
4188 error(1,"-identifier-","-tagname-");
4189 tags[0]=pc_addtag(ptr);
4194 error(1,"-identifier-","-tagname-");
4196 while (numtags<MAXTAGS) {
4197 if (!matchtoken('_') && !needtoken(tSYMBOL))
4199 tokeninfo(&val,&ptr);
4200 tags[numtags++]=pc_addtag(ptr);
4201 if (matchtoken('}'))
4206 tok=tLABEL; /* for outer loop: flag that we have seen a tagname */
4209 if (argcnt>=sMAXARGS)
4210 error(45); /* too many function arguments */
4211 strcpy(name,ptr); /* save symbol name */
4212 if (name[0]==PUBLIC_CHAR)
4213 error(56,name); /* function arguments cannot be public */
4215 tags[numtags++]=0; /* default tag */
4217 * base + 0*sizeof(cell) == previous "base"
4218 * base + 1*sizeof(cell) == function return address
4219 * base + 2*sizeof(cell) == number of arguments
4220 * base + 3*sizeof(cell) == first argument of the function
4221 * So the offset of each argument is "(argcnt+3) * sizeof(cell)".
4223 doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg);
4224 /* :TODO: fix this so stocks that are func pointers can't have default arguments? */
4225 if ((sym->usage & uPUBLIC) && arg.hasdefault)
4226 error(59,name); /* arguments of a public function may not have a default value */
4227 if ((sym->usage & uPROTOTYPED)==0) {
4228 /* redimension the argument list, add the entry */
4229 sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
4230 if (sym->dim.arglist==0)
4231 error(103); /* insufficient memory */
4232 memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
4233 sym->dim.arglist[argcnt]=arg;
4235 /* check the argument with the earlier definition */
4236 if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg))
4237 error(25); /* function definition does not match prototype */
4238 /* may need to free default array argument and the tag list */
4239 if (arg.ident==iREFARRAY && arg.hasdefault)
4240 free(arg.defvalue.array.data);
4241 else if (arg.ident==iVARIABLE
4242 && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0) || (arg.hasdefault & uCOUNTOF)!=0)
4243 free(arg.defvalue.size.symname);
4252 if (ident!=iVARIABLE)
4253 error(10); /* illegal function or declaration */
4255 tags[numtags++]=0; /* default tag */
4256 if ((sym->usage & uPROTOTYPED)==0) {
4257 /* redimension the argument list, add the entry iVARARGS */
4258 sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
4259 if (sym->dim.arglist==0)
4260 error(103); /* insufficient memory */
4261 memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
4262 sym->dim.arglist[argcnt].ident=iVARARGS;
4263 sym->dim.arglist[argcnt].hasdefault=FALSE;
4264 sym->dim.arglist[argcnt].defvalue.val=0;
4265 sym->dim.arglist[argcnt].defvalue_tag=0;
4266 sym->dim.arglist[argcnt].numtags=numtags;
4267 sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]);
4268 if (sym->dim.arglist[argcnt].tags==NULL)
4269 error(103); /* insufficient memory */
4270 memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]);
4272 if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS)
4273 error(25); /* function definition does not match prototype */
4278 error(10); /* illegal function or declaration */
4280 } while (tok=='&' || tok==tLABEL || tok==tCONST
4281 || tok!=tELLIPS && matchtoken(',')); /* more? */
4282 /* if the next token is not ",", it should be ")" */
4285 /* resolve any "sizeof" arguments (now that all arguments are known) */
4286 assert(sym->dim.arglist!=NULL);
4287 arglist=sym->dim.arglist;
4288 for (idx=0; idx<argcnt && arglist[idx].ident!=0; idx++) {
4289 if ((arglist[idx].hasdefault & uSIZEOF)!=0
4290 || (arglist[idx].hasdefault & uTAGOF)!=0
4291 || (arglist[idx].hasdefault & uCOUNTOF)!=0) {
4293 /* Find the argument with the name mentioned after the "sizeof". Note
4294 * that we cannot use findloc here because we need the arginfo struct,
4297 ptr=arglist[idx].defvalue.size.symname;
4299 for (altidx=0; altidx<argcnt && strcmp(ptr,arglist[altidx].name)!=0; altidx++)
4301 if (altidx>=argcnt) {
4302 error(17,ptr); /* undefined symbol */
4304 assert(arglist[idx].defvalue.size.symname!=NULL);
4305 /* check the level against the number of dimensions */
4306 if (arglist[idx].defvalue.size.level>0
4307 && arglist[idx].defvalue.size.level>=arglist[altidx].numdim)
4308 error(28,arglist[idx].name); /* invalid subscript */
4309 /* check the type of the argument whose size to take; for a iVARIABLE
4310 * or a iREFERENCE, this is always 1 (so the code is redundant)
4312 assert(arglist[altidx].ident!=iVARARGS);
4313 if (arglist[altidx].ident!=iREFARRAY
4314 && (((arglist[idx].hasdefault & uSIZEOF)!=0)
4315 || (arglist[idx].hasdefault & uCOUNTOF)!=0)) {
4316 if ((arglist[idx].hasdefault & uTAGOF)!=0) {
4317 error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */
4319 assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE);
4320 error(223,ptr); /* redundant sizeof */
4327 sym->usage|=uPROTOTYPED;
4328 errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
4332 /* doarg - declare one argument type
4334 * this routine is called from "declargs()" and adds an entry in the local
4335 * symbol table for one argument.
4337 * "fpublic" indicates whether the function for this argument list is public.
4338 * The arguments themselves are never public.
4340 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
4341 int fpublic,int fconst,int chkshadow,arginfo *arg)
4344 constvalue *enumroot;
4348 strcpy(arg->name,name);
4349 arg->hasdefault=FALSE; /* preset (most common case) */
4350 arg->defvalue.val=0; /* clear */
4351 arg->defvalue_tag=0;
4353 if (matchtoken('[')) {
4354 if (ident==iREFERENCE)
4355 error(67,name); /* illegal declaration ("&name[]" is unsupported) */
4357 if (arg->numdim == sDIMEN_MAX) {
4358 error(53); /* exceeding maximum number of dimensions */
4361 size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */
4362 #if INT_MAX < LONG_MAX
4364 error(105); /* overflow, exceeding capacity */
4366 arg->dim[arg->numdim]=(int)size;
4368 } while (matchtoken('['));
4369 ident=iREFARRAY; /* "reference to array" (is a pointer) */
4370 if (checktag(tags, numtags, pc_tag_string)) {
4371 slength = arg->dim[arg->numdim - 1];
4372 arg->dim[arg->numdim - 1] = (size + sizeof(cell) - 1) / sizeof(cell);
4374 if (matchtoken('=')) {
4375 lexpush(); /* initials() needs the "=" token again */
4376 assert(litidx==0); /* at the start of a function, this is reset */
4378 initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot);
4379 assert(size>=litidx);
4380 /* allocate memory to hold the initial values */
4381 arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell));
4382 if (arg->defvalue.array.data!=NULL) {
4384 memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell));
4385 arg->hasdefault=TRUE; /* argument has default value */
4386 arg->defvalue.array.size=litidx;
4387 arg->defvalue.array.addr=-1;
4388 /* calulate size to reserve on the heap */
4389 arg->defvalue.array.arraysize=1;
4390 for (i=0; i<arg->numdim; i++)
4391 arg->defvalue.array.arraysize*=arg->dim[i];
4392 if (arg->defvalue.array.arraysize < arg->defvalue.array.size)
4393 arg->defvalue.array.arraysize = arg->defvalue.array.size;
4395 litidx=0; /* reset */
4398 if (matchtoken('=')) {
4399 unsigned char size_tag_token;
4400 assert(ident==iVARIABLE || ident==iREFERENCE);
4401 arg->hasdefault=TRUE; /* argument has a default value */
4402 size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0);
4403 if (size_tag_token==0)
4404 size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0);
4405 if (size_tag_token==0)
4406 size_tag_token=(unsigned char)(matchtoken(tCELLSOF) ? uCOUNTOF : 0);
4407 if (size_tag_token!=0) {
4409 if (ident==iREFERENCE)
4410 error(66,name); /* argument may not be a reference */
4412 while (matchtoken('('))
4414 if (needtoken(tSYMBOL)) {
4415 /* save the name of the argument whose size id to take */
4418 tokeninfo(&val,&name);
4419 if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL)
4420 error(103); /* insufficient memory */
4421 arg->defvalue.size.level=0;
4422 if (size_tag_token==uSIZEOF || size_tag_token==uCOUNTOF) {
4423 while (matchtoken('[')) {
4424 arg->defvalue.size.level+=(short)1;
4428 if (ident==iVARIABLE) /* make sure we set this only if not a reference */
4429 arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */
4431 while (paranthese--)
4434 constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL);
4436 if (!matchtag(tags[0],arg->defvalue_tag,TRUE))
4437 error(213); /* tagname mismatch */
4441 arg->ident=(char)ident;
4442 arg->usage=(char)(fconst ? uCONST : 0);
4443 arg->numtags=numtags;
4444 arg->tags=(int*)malloc(numtags*sizeof tags[0]);
4445 if (arg->tags==NULL)
4446 error(103); /* insufficient memory */
4447 memcpy(arg->tags,tags,numtags*sizeof tags[0]);
4448 argsym=findloc(name);
4450 error(21,name); /* symbol already defined */
4452 if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN)
4453 error(219,name); /* variable shadows another symbol */
4454 /* add details of type and address */
4456 argsym=addvariable2(name,offset,ident,sLOCAL,tags[0],
4457 arg->dim,arg->numdim,arg->idxtag,slength);
4459 if (ident==iREFERENCE)
4460 argsym->usage|=uREAD; /* because references are passed back */
4462 argsym->usage|=uREAD; /* arguments of public functions are always "used" */
4464 argsym->usage|=uCONST;
4468 static int count_referrers(symbol *entry)
4473 for (i=0; i<entry->numrefers; i++)
4474 if (entry->refer[i]!=NULL)
4479 #if !defined SC_LIGHT
4480 static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue,
4481 char **outer_start,int *outer_length,
4482 char **inner_start,int *inner_length)
4484 char *ptr,*inner_end;
4485 int xmltag_len,xmlparam_len,xmlvalue_len;
4488 assert(source!=NULL);
4489 assert(xmltag!=NULL);
4490 assert(outer_start!=NULL);
4491 assert(outer_length!=NULL);
4492 assert(inner_start!=NULL);
4493 assert(inner_length!=NULL);
4495 /* both NULL or both non-NULL */
4496 assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL);
4498 xmltag_len=strlen(xmltag);
4499 xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0;
4500 xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0;
4502 /* find an opening '<' */
4503 while ((ptr=strchr(ptr,'<'))!=NULL) {
4504 *outer_start=ptr; /* be optimistic... */
4505 match=FALSE; /* ...and pessimistic at the same time */
4506 ptr++; /* skip '<' */
4507 while (*ptr!='\0' && *ptr<=' ')
4508 ptr++; /* skip white space */
4509 if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
4510 /* xml tag found, optionally check the parameter */
4512 while (*ptr!='\0' && *ptr<=' ')
4513 ptr++; /* skip white space */
4514 if (xmlparam!=NULL) {
4515 if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) {
4517 while (*ptr!='\0' && *ptr<=' ')
4518 ptr++; /* skip white space */
4520 ptr++; /* skip '=' */
4521 while (*ptr!='\0' && *ptr<=' ')
4522 ptr++; /* skip white space */
4523 if (*ptr=='"' || *ptr=='\'')
4524 ptr++; /* skip " or ' */
4525 assert(xmlvalue!=NULL);
4526 if (strncmp(ptr,xmlvalue,xmlvalue_len)==0
4527 && (*(ptr+xmlvalue_len)<=' '
4528 || *(ptr+xmlvalue_len)=='>'
4529 || *(ptr+xmlvalue_len)=='"'
4530 || *(ptr+xmlvalue_len)=='\''))
4531 match=TRUE; /* found it */
4535 match=TRUE; /* don't check the parameter */
4539 /* now find the end of the opening tag */
4540 while (*ptr!='\0' && *ptr!='>')
4544 while (*ptr!='\0' && *ptr<=' ')
4545 ptr++; /* skip white space */
4547 /* find the start of the closing tag (assume no nesting) */
4548 while ((ptr=strchr(ptr,'<'))!=NULL) {
4550 ptr++; /* skip '<' */
4551 while (*ptr!='\0' && *ptr<=' ')
4552 ptr++; /* skip white space */
4555 while (*ptr!='\0' && *ptr<=' ')
4556 ptr++; /* skip white space */
4557 if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
4558 /* find the end of the closing tag */
4559 while (*ptr!='\0' && *ptr!='>')
4563 /* set the lengths of the inner and outer segment */
4564 assert(*inner_start!=NULL);
4565 *inner_length=(int)(inner_end-*inner_start);
4566 assert(*outer_start!=NULL);
4567 *outer_length=(int)(ptr-*outer_start);
4568 break; /* break out of the loop */
4575 return FALSE; /* not found */
4578 static char *xmlencode(char *dest,char *source)
4580 char temp[2*sNAMEMAX+20],*ptr;
4582 /* replace < by < and such; normally, such a symbol occurs at most once in
4583 * a symbol name (e.g. "operator<")
4586 while (*source!='\0') {
4597 strcpy(ptr,"&");
4610 static void make_report(symbol *root,FILE *log,char *sourcefile)
4612 char symname[_MAX_PATH];
4616 constvalue *enumroot;
4619 /* adapt the installation directory */
4620 strcpy(symname,sc_rootpath);
4621 #if DIRSEP_CHAR=='\\'
4622 while ((ptr=strchr(symname,':'))!=NULL)
4624 while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL)
4628 /* the XML header */
4629 fprintf(log,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
4630 fprintf(log,"<?xml-stylesheet href=\"file:///%s/xml/pawndoc.xsl\" type=\"text/xsl\"?>\n",symname);
4631 fprintf(log,"<doc source=\"%s\">\n",sourcefile);
4632 ptr=strrchr(sourcefile,DIRSEP_CHAR);
4637 fprintf(log,"\t<assembly>\n\t\t<name>%s</name>\n\t</assembly>\n",ptr);
4639 /* attach the global documentation, if any */
4640 if (sc_documentation!=NULL) {
4641 fprintf(log,"\n\t<!-- general -->\n");
4642 fprintf(log,"\t<general>\n\t\t");
4643 fputs(sc_documentation,log);
4644 fprintf(log,"\n\t</general>\n\n");
4647 /* use multiple passes to print constants variables and functions in
4650 fprintf(log,"\t<members>\n");
4652 fprintf(log,"\n\t\t<!-- enumerations -->\n");
4653 for (sym=root->next; sym!=NULL; sym=sym->next) {
4654 if (sym->parent!=NULL)
4655 continue; /* hierarchical data type */
4656 assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
4657 || sym->ident==iARRAY || sym->ident==iFUNCTN);
4658 if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0)
4660 if ((sym->usage & uREAD)==0)
4662 fprintf(log,"\t\t<member name=\"T:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
4664 tagsym=find_tag_byval(sym->tag);
4665 assert(tagsym!=NULL);
4666 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4668 /* browse through all fields */
4669 if ((enumroot=sym->dim.enumlist)!=NULL) {
4670 enumroot=enumroot->next; /* skip root */
4671 while (enumroot!=NULL) {
4672 fprintf(log,"\t\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,enumroot->name),enumroot->value);
4673 /* find the constant with this name and get the tag */
4674 ref=findglb(enumroot->name,sGLOBAL);
4676 if (ref->x.tags.index!=0) {
4677 tagsym=find_tag_byval(ref->x.tags.index);
4678 assert(tagsym!=NULL);
4679 fprintf(log,"\t\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4681 if (ref->dim.array.length!=1)
4682 fprintf(log,"\t\t\t\t<size value=\"%ld\"/>\n",(long)ref->dim.array.length);
4684 fprintf(log,"\t\t\t</member>\n");
4685 enumroot=enumroot->next;
4688 assert(sym->refer!=NULL);
4689 for (i=0; i<sym->numrefers; i++) {
4690 if ((ref=sym->refer[i])!=NULL)
4691 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4693 if (sym->documentation!=NULL)
4694 fprintf(log,"\t\t\t%s\n",sym->documentation);
4695 fprintf(log,"\t\t</member>\n");
4698 fprintf(log,"\n\t\t<!-- constants -->\n");
4699 for (sym=root->next; sym!=NULL; sym=sym->next) {
4700 if (sym->parent!=NULL)
4701 continue; /* hierarchical data type */
4702 assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
4703 || sym->ident==iARRAY || sym->ident==iFUNCTN);
4704 if (sym->ident!=iCONSTEXPR)
4706 if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0)
4708 fprintf(log,"\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
4710 tagsym=find_tag_byval(sym->tag);
4711 assert(tagsym!=NULL);
4712 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4714 assert(sym->refer!=NULL);
4715 for (i=0; i<sym->numrefers; i++) {
4716 if ((ref=sym->refer[i])!=NULL)
4717 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4719 if (sym->documentation!=NULL)
4720 fprintf(log,"\t\t\t%s\n",sym->documentation);
4721 fprintf(log,"\t\t</member>\n");
4724 fprintf(log,"\n\t\t<!-- variables -->\n");
4725 for (sym=root->next; sym!=NULL; sym=sym->next) {
4726 if (sym->parent!=NULL)
4727 continue; /* hierarchical data type */
4728 if (sym->ident!=iVARIABLE && sym->ident!=iARRAY)
4730 fprintf(log,"\t\t<member name=\"F:%s\">\n",funcdisplayname(symname,sym->name));
4732 tagsym=find_tag_byval(sym->tag);
4733 assert(tagsym!=NULL);
4734 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4736 assert(sym->refer!=NULL);
4737 if ((sym->usage & uPUBLIC)!=0)
4738 fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
4739 for (i=0; i<sym->numrefers; i++) {
4740 if ((ref=sym->refer[i])!=NULL)
4741 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4743 if (sym->documentation!=NULL)
4744 fprintf(log,"\t\t\t%s\n",sym->documentation);
4745 fprintf(log,"\t\t</member>\n");
4748 fprintf(log,"\n\t\t<!-- functions -->\n");
4749 for (sym=root->next; sym!=NULL; sym=sym->next) {
4750 if (sym->parent!=NULL)
4751 continue; /* hierarchical data type */
4752 if (sym->ident!=iFUNCTN)
4754 if ((sym->usage & (uREAD | uNATIVE))==uNATIVE)
4755 continue; /* unused native function */
4756 funcdisplayname(symname,sym->name);
4757 xmlencode(symname,symname);
4758 fprintf(log,"\t\t<member name=\"M:%s\" syntax=\"%s(",symname,symname);
4759 /* print only the names of the parameters between the parentheses */
4760 assert(sym->dim.arglist!=NULL);
4761 for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
4765 switch (sym->dim.arglist[arg].ident) {
4767 fprintf(log,"%s",sym->dim.arglist[arg].name);
4770 fprintf(log,"&%s",sym->dim.arglist[arg].name);
4773 fprintf(log,"%s",sym->dim.arglist[arg].name);
4774 for (dim=0; dim<sym->dim.arglist[arg].numdim;dim++)
4782 /* ??? should also print an "array return" size */
4783 fprintf(log,")\">\n");
4785 tagsym=find_tag_byval(sym->tag);
4786 assert(tagsym!=NULL);
4787 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4789 /* check whether this function is called from the outside */
4790 if ((sym->usage & uNATIVE)!=0)
4791 fprintf(log,"\t\t\t<attribute name=\"native\"/>\n");
4792 if ((sym->usage & uPUBLIC)!=0)
4793 fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
4794 if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0)
4795 fprintf(log,"\t\t\t<attribute name=\"entry\"/>\n");
4796 if ((sym->usage & uNATIVE)==0)
4797 fprintf(log,"\t\t\t<stacksize value=\"%ld\"/>\n",(long)sym->x.stacksize);
4798 if (sym->states!=NULL) {
4799 constvalue *stlist=sym->states->next;
4800 assert(stlist!=NULL); /* there should be at least one state item */
4801 while (stlist!=NULL && stlist->index==-1)
4802 stlist=stlist->next;
4803 assert(stlist!=NULL); /* state id should be found */
4804 i=state_getfsa(stlist->index);
4805 assert(i>=0); /* automaton 0 exists */
4806 stlist=automaton_findid(i);
4807 assert(stlist!=NULL); /* automaton should be found */
4808 fprintf(log,"\t\t\t<automaton name=\"%s\"/>\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)");
4809 //??? dump state decision table
4811 assert(sym->refer!=NULL);
4812 for (i=0; i<sym->numrefers; i++)
4813 if ((ref=sym->refer[i])!=NULL)
4814 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4815 /* print all symbols that are required for this function to compile */
4816 for (ref=root->next; ref!=NULL; ref=ref->next) {
4819 for (i=0; i<ref->numrefers; i++)
4820 if (ref->refer[i]==sym)
4821 fprintf(log,"\t\t\t<dependency name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4823 /* print parameter list, with tag & const information, plus descriptions */
4824 assert(sym->dim.arglist!=NULL);
4825 for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
4827 char *outer_start,*inner_start;
4828 int outer_length,inner_length;
4829 if (sym->dim.arglist[arg].ident==iVARARGS)
4830 fprintf(log,"\t\t\t<param name=\"...\">\n");
4832 fprintf(log,"\t\t\t<param name=\"%s\">\n",sym->dim.arglist[arg].name);
4833 /* print the tag name(s) for each parameter */
4834 assert(sym->dim.arglist[arg].numtags>0);
4835 assert(sym->dim.arglist[arg].tags!=NULL);
4836 paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0)
4837 || sym->dim.arglist[arg].ident==iREFERENCE
4838 || sym->dim.arglist[arg].ident==iREFARRAY;
4840 fprintf(log,"\t\t\t\t<paraminfo>");
4841 if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) {
4843 if (sym->dim.arglist[arg].numtags>1)
4845 for (i=0; i<sym->dim.arglist[arg].numtags; i++) {
4848 tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]);
4849 assert(tagsym!=NULL);
4850 fprintf(log,"%s",tagsym->name);
4852 if (sym->dim.arglist[arg].numtags>1)
4855 switch (sym->dim.arglist[arg].ident) {
4857 fprintf(log," &");
4861 for (dim=0; dim<sym->dim.arglist[arg].numdim; dim++) {
4862 if (sym->dim.arglist[arg].dim[dim]==0) {
4865 //??? find index tag
4866 fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]);
4872 fprintf(log," </paraminfo>\n");
4873 /* print the user description of the parameter (parse through
4874 * sym->documentation)
4876 if (sym->documentation!=NULL
4877 && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name,
4878 &outer_start, &outer_length, &inner_start, &inner_length))
4881 fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start);
4882 /* delete from documentation string */
4883 tail=outer_start+outer_length;
4884 memmove(outer_start,tail,strlen(tail)+1);
4886 fprintf(log,"\t\t\t</param>\n");
4888 if (sym->documentation!=NULL)
4889 fprintf(log,"\t\t\t%s\n",sym->documentation);
4890 fprintf(log,"\t\t</member>\n");
4893 fprintf(log,"\n\t</members>\n");
4894 fprintf(log,"</doc>\n");
4898 /* Every symbol has a referrer list, that contains the functions that use
4899 * the symbol. Now, if function "apple" is accessed by functions "banana" and
4900 * "citron", but neither function "banana" nor "citron" are used by anyone
4901 * else, then, by inference, function "apple" is not used either.
4903 static void reduce_referrers(symbol *root)
4910 for (sym=root->next; sym!=NULL; sym=sym->next) {
4911 if (sym->parent!=NULL)
4912 continue; /* hierarchical data type */
4913 if (sym->ident==iFUNCTN
4914 && (sym->usage & uNATIVE)==0
4915 && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0
4916 && count_referrers(sym)==0)
4918 sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
4919 /* find all symbols that are referred by this symbol */
4920 for (ref=root->next; ref!=NULL; ref=ref->next) {
4921 if (ref->parent!=NULL)
4922 continue; /* hierarchical data type */
4923 assert(ref->refer!=NULL);
4924 for (i=0; i<ref->numrefers && ref->refer[i]!=sym; i++)
4926 if (i<ref->numrefers) {
4927 assert(ref->refer[i]==sym);
4932 } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY)
4933 && (sym->usage & uPUBLIC)==0
4934 && sym->parent==NULL
4935 && count_referrers(sym)==0)
4937 sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
4940 /* after removing a symbol, check whether more can be removed */
4941 } while (restart>0);
4944 #if !defined SC_LIGHT
4945 static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion)
4951 assert(sym->ident==iFUNCTN);
4952 assert((sym->usage & uNATIVE)==0);
4953 assert(recursion!=NULL);
4955 maxsize=sym->x.stacksize;
4956 for (i=0; i<sym->numrefers; i++) {
4957 if (sym->refer[i]!=NULL) {
4958 assert(sym->refer[i]->ident==iFUNCTN);
4959 assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */
4960 if (sym->refer[i]==sourcesym) { /* recursion detection */
4962 break; /* recursion was detected, quit loop */
4964 size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion);
4970 if ((sym->usage & uPUBLIC)!=0) {
4971 /* Find out how many parameters a public function has, then see if this
4972 * is bigger than some maximum
4974 arginfo *arg=sym->dim.arglist;
4977 while (arg->ident!=0) {
4981 assert(pubfuncparams!=0);
4982 if (count>*pubfuncparams)
4983 *pubfuncparams=count;
4986 return maxsize+basesize;
4989 static long max_stacksize(symbol *root,int *recursion)
4991 /* Loop over all non-native functions. For each function, loop
4992 * over all of its referrers, accumulating the stack requirements.
4993 * Detect (indirect) recursion with a "mark-and-sweep" algorithm.
4994 * I (mis-)use the "compound" field of the symbol structure for
4995 * the marker, as this field is unused for functions.
4997 * Note that the stack is shared with the heap. A host application
4998 * may "eat" cells from the heap as well, through amx_Allot(). The
4999 * stack requirements are thus only an estimate.
5006 assert(recursion!=NULL);
5008 for (sym=root->next; sym!=NULL; sym=sym->next)
5009 if (sym->ident==iFUNCTN)
5010 assert(sym->compound==0);
5015 *recursion=0; /* assume no recursion */
5016 for (sym=root->next; sym!=NULL; sym=sym->next) {
5017 /* drop out if this is not a user-implemented function */
5018 if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0)
5020 /* accumulate stack size for this symbol */
5021 size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion);
5027 maxsize++; /* +1 because a zero cell is always pushed on top
5028 * of the stack to catch stack overwrites */
5029 return maxsize+(maxparams+1);/* +1 because # of parameters is always pushed on entry */
5033 /* testsymbols - test for unused local or global variables
5035 * "Public" functions are excluded from the check, since these
5036 * may be exported to other object modules.
5037 * Labels are excluded from the check if the argument 'testlabs'
5038 * is 0. Thus, labels are not tested until the end of the function.
5039 * Constants may also be excluded (convenient for global constants).
5041 * When the nesting level drops below "level", the check stops.
5043 * The function returns whether there is an "entry" point for the file.
5044 * This flag will only be 1 when browsing the global symbol table.
5046 static int testsymbols(symbol *root,int level,int testlabs,int testconst)
5048 char symname[2*sNAMEMAX+16];
5051 symbol *sym=root->next;
5052 while (sym!=NULL && sym->compound>=level) {
5053 switch (sym->ident) {
5056 if ((sym->usage & uDEFINE)==0) {
5057 error(19,sym->name); /* not a label: ... */
5058 } else if ((sym->usage & uREAD)==0) {
5059 errorset(sSETPOS,sym->lnumber);
5060 error(203,sym->name); /* symbol isn't used: ... */
5065 if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) {
5066 funcdisplayname(symname,sym->name);
5067 if (strlen(symname)>0)
5068 error(203,symname); /* symbol isn't used ... (and not public/native/stock) */
5070 if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0)
5071 entry=TRUE; /* there is an entry point */
5072 /* also mark the function to the debug information */
5073 if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0)
5074 insert_dbgsymbol(sym);
5077 if (testconst && (sym->usage & uREAD)==0) {
5078 errorset(sSETPOS,sym->lnumber);
5079 error(203,sym->name); /* symbol isn't used: ... */
5084 if (sym->parent!=NULL)
5085 break; /* hierarchical data type */
5086 if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) {
5088 errorset(sSETPOS,sym->lnumber);
5089 error(203,sym->name,sym->lnumber); /* symbol isn't used (and not stock) */
5090 } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) {
5091 errorset(sSETPOS,sym->lnumber);
5092 error(204,sym->name); /* value assigned to symbol is never used */
5093 #if 0 // ??? not sure whether it is a good idea to force people use "const"
5094 } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) {
5095 errorset(sSETPOS,sym->lnumber);
5096 error(214,sym->name); /* make array argument "const" */
5099 /* also mark the variable (local or global) to the debug information */
5100 if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0)
5101 insert_dbgsymbol(sym);
5109 static cell calc_array_datasize(symbol *sym, cell *offset)
5114 assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
5115 length=sym->dim.array.length;
5116 if (sym->dim.array.level > 0) {
5117 cell sublength=calc_array_datasize(finddepend(sym),offset);
5119 *offset=length*(*offset+sizeof(cell));
5121 length*=length*sublength;
5131 static void destructsymbols(symbol *root,int level)
5135 symbol *sym=root->next;
5136 while (sym!=NULL && sym->compound>=level) {
5137 if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
5138 char symbolname[16];
5141 /* check that the '~' operator is defined for this tag */
5142 operator_symname(symbolname,"~",sym->tag,0,1,0);
5143 if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) {
5144 /* save PRI, in case of a return statment */
5146 pushreg(sPRI); /* right-hand operand is in PRI */
5149 /* if the variable is an array, get the number of elements */
5150 if (sym->ident==iARRAY) {
5151 elements=calc_array_datasize(sym,&offset);
5152 /* "elements" can be zero when the variable is declared like
5153 * new mytag: myvar[2][] = { {1, 2}, {3, 4} }
5154 * one should declare all dimensions!
5157 error(46,sym->name); /* array size is unknown */
5163 /* call the '~' operator */
5165 addconst(offset); /* add offset to array data to the address */
5167 pushval(2 /* *sizeof(cell)*/ );/* 2 parameters */
5168 assert(opsym->ident==iFUNCTN);
5169 ffcall(opsym,NULL,1);
5170 if (sc_status!=statSKIP)
5171 markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */
5172 if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL)
5173 opsym->x.lib->value += 1; /* increment "usage count" of the library */
5178 /* restore PRI, if it was saved */
5183 static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,
5188 if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL)
5189 error(103); /* insufficient memory (fatal error) */
5190 memset(cur,0,sizeof(constvalue));
5192 assert(strlen(name)<sNAMEMAX);
5193 strcpy(cur->name,name);
5202 SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index)
5204 constvalue *cur,*prev;
5206 /* find the end of the constant table */
5207 for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next)
5209 return insert_constval(prev,NULL,name,val,index);
5212 SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index)
5214 constvalue *ptr = table->next;
5217 if (strcmp(name,ptr->name)==0 && ptr->index==index)
5224 static constvalue *find_constval_byval(constvalue *table,cell val)
5226 constvalue *ptr = table->next;
5229 if (ptr->value==val)
5236 #if 0 /* never used */
5237 static int delete_constval(constvalue *table,char *name)
5239 constvalue *prev = table;
5240 constvalue *cur = prev->next;
5243 if (strcmp(name,cur->name)==0) {
5244 prev->next=cur->next;
5255 SC_FUNC void delete_consttable(constvalue *table)
5257 constvalue *cur=table->next, *next;
5264 memset(table,0,sizeof(constvalue));
5269 * Adds a symbol to the symbol table. Returns NULL on failure.
5271 SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag)
5275 /* Test whether a global or local symbol with the same name exists. Since
5276 * constants are stored in the symbols table, this also finds previously
5277 * defind constants. */
5278 sym=findglb(name,sSTATEVAR);
5283 if (sym->ident!=iCONSTEXPR)
5284 redef=1; /* redefinition a function/variable to a constant is not allowed */
5285 if ((sym->usage & uENUMFIELD)!=0) {
5286 /* enum field, special case if it has a different tag and the new symbol is also an enum field */
5290 redef=1; /* enumeration field is redefined (same tag) */
5291 tagid=find_tag_byval(tag);
5293 redef=1; /* new constant does not have a tag */
5295 tagsym=findconst(tagid->name,NULL);
5296 if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0)
5297 redef=1; /* new constant is not an enumeration field */
5299 /* in this particular case (enumeration field that is part of a different
5300 * enum, and non-conflicting with plain constants) we want to be able to
5304 goto redef_enumfield;
5305 } else if (sym->tag!=tag) {
5306 redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */
5309 error(21,name); /* symbol already defined */
5311 } else if (sym->addr!=val) {
5312 error(201,name); /* redefinition of constant (different value) */
5313 sym->addr=val; /* set new value */
5315 /* silently ignore redefinitions of constants with the same value & tag */
5319 /* constant doesn't exist yet (or is allowed to be redefined) */
5321 sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE);
5322 assert(sym!=NULL); /* fatal error 103 must be given on error */
5323 if (sc_status == statIDLE)
5324 sym->usage |= uPREDEF;
5328 /* statement - The Statement Parser
5330 * This routine is called whenever the parser needs to know what statement
5331 * it encounters (i.e. whenever program syntax requires a statement).
5333 static void statement(int *lastindent,int allow_decl)
5340 error(36); /* empty statement */
5347 insert_dbgline(fline);
5350 /* lex() has set stmtindent */
5351 if (lastindent!=NULL && tok!=tLABEL) {
5352 if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0)
5353 error(217); /* loose indentation */
5354 *lastindent=stmtindent;
5355 indent_nowarn=FALSE; /* if warning was blocked, re-enable it */
5367 error(3); /* declaration only valid in a block */
5376 error(3); /* declaration only valid in a block */
5384 error(3); /* declaration only valid in a block */
5390 if (!matchtoken('}')) /* {} is the empty statement */
5391 compound(save==fline,tok);
5392 /* lastst (for "last statement") does not change */
5395 error(36); /* empty statement */
5415 error(14); /* not in switch */
5459 default: /* non-empty expression */
5460 sc_allowproccall=optproccall;
5461 lexpush(); /* analyze token later */
5462 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);
5465 sc_allowproccall=FALSE;
5469 static void compound(int stmt_sameline,int starttok)
5472 cell save_decl=declared;
5474 int block_start=fline; /* save line where the compound block started */
5479 /* if there is more text on this line, we should adjust the statement indent */
5480 if (stmt_sameline) {
5482 const unsigned char *p=lptr;
5483 /* go back to the opening brace */
5484 while (*p!=starttok) {
5488 assert(*p==starttok); /* it should be found */
5489 /* go forward, skipping white-space */
5491 while (*p<=' ' && *p!='\0')
5493 assert(*p!='\0'); /* a token should be found */
5495 for (i=0; i<(int)(p-pline); i++)
5496 if (pline[i]=='\t' && sc_tabsize>0)
5497 stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
5502 endtok=(starttok=='{') ? '}' : tEND;
5503 nestlevel+=1; /* increase compound statement level */
5504 while (matchtoken(endtok)==0){/* repeat until compound statement is closed */
5506 error(30,block_start); /* compound block not closed at end of file */
5509 if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE || lastst==tENDLESS))
5510 error(225); /* unreachable code */
5511 statement(&indent,TRUE); /* do a statement */
5515 if (lastst!=tRETURN)
5516 destructsymbols(&loctab,nestlevel);
5517 if (lastst!=tRETURN && lastst!=tGOTO) {
5524 testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
5526 delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but
5527 * retain block local labels
5528 * (within the function) */
5529 nestlevel-=1; /* decrease compound statement level */
5534 * Global references: stgidx (referred to only)
5536 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
5537 int *tag,symbol **symptr,int chkfuncresult)
5539 return doexpr2(comma,chkeffect,allowarray,mark_endexpr,tag,symptr,chkfuncresult,NULL);
5544 * Global references: stgidx (referred to only)
5546 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
5547 int *tag,symbol **symptr,int chkfuncresult,value *lval)
5550 int localstaging=FALSE;
5554 stgset(TRUE); /* start stage-buffering */
5559 errorset(sEXPRMARK,0);
5561 /* on second round through, mark the end of the previous expression */
5563 markexpr(sEXPR,NULL,0);
5565 ident=expression(&val,tag,symptr,chkfuncresult,lval);
5566 if (!allowarray && (ident==iARRAY || ident==iREFARRAY))
5567 error(33,"-unknown-"); /* array must be indexed */
5568 if (chkeffect && !sideeffect)
5569 error(215); /* expression has no effect */
5570 sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */
5571 } while (comma && matchtoken(',')); /* more? */
5573 markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */
5574 errorset(sEXPRRELEASE,0);
5577 stgset(FALSE); /* stop staging */
5584 SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr)
5589 stgset(TRUE); /* start stage-buffering */
5590 stgget(&index,&cidx); /* mark position in code generator */
5591 errorset(sEXPRMARK,0);
5592 ident=expression(val,tag,symptr,FALSE,NULL);
5593 stgdel(index,cidx); /* scratch generated code */
5594 stgset(FALSE); /* stop stage-buffering */
5595 if (ident!=iCONSTEXPR) {
5596 error(8); /* must be constant expression */
5604 errorset(sEXPRRELEASE,0);
5605 return (ident==iCONSTEXPR);
5610 * In the case a "simple assignment" operator ("=") is used within a test,
5611 * the warning "possibly unintended assignment" is displayed. This routine
5612 * sets the global variable "sc_intest" to true, it is restored upon termination.
5613 * In the case the assignment was intended, use parantheses around the
5614 * expression to avoid the warning; primary() sets "sc_intest" to 0.
5616 * Global references: sc_intest (altered, but restored upon termination)
5618 static int test(int label,int parens,int invert)
5626 int localstaging=FALSE;
5629 stgset(TRUE); /* start staging */
5632 stgget(&index,&cidx); /* should start at zero if started locally */
5637 PUSHSTK_I(sc_intest);
5640 if (parens!=TEST_PLAIN) {
5641 if (matchtoken('('))
5643 else if (parens==TEST_THEN)
5645 else if (parens==TEST_DO)
5649 stgget(&index,&cidx); /* mark position (of last expression) in
5651 ident=expression(&constval,&tag,&sym,TRUE,NULL);
5652 tok=matchtoken(',');
5654 markexpr(sEXPR,NULL,0);
5655 } while (tok); /* do */
5658 if (ident==iARRAY || ident==iREFARRAY) {
5659 char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-";
5660 error(33,ptr); /* array must be indexed */
5662 if (ident==iCONSTEXPR) { /* constant expression */
5664 sc_intest=(short)POPSTK_I();/* restore stack */
5666 if (constval) { /* code always executed */
5667 error(206); /* redundant test: always non-zero */
5670 error(205); /* redundant code: never executed */
5674 stgout(0); /* write "jumplabel" code */
5675 stgset(FALSE); /* stop staging */
5679 if (tag!=0 && tag!=pc_addtag("bool"))
5680 if (check_userop(lneg,tag,0,1,NULL,&tag))
5681 invert= !invert; /* user-defined ! operator inverted result */
5683 jmp_ne0(label); /* jump to label if true (different from 0) */
5685 jmp_eq0(label); /* jump to label if false (equal to 0) */
5686 markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */
5687 sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */
5689 stgout(0); /* output queue from the very beginning (see
5690 * assert() when localstaging is set to TRUE) */
5691 stgset(FALSE); /* stop staging */
5696 static int doif(void)
5702 ifindent=stmtindent; /* save the indent of the "if" instruction */
5703 flab1=getlabel(); /* get label number for false branch */
5704 test(flab1,TEST_THEN,FALSE); /* get expression, branch to flab1 if false */
5705 statement(NULL,FALSE); /* if true, do a statement */
5706 if (!matchtoken(tELSE)) { /* if...else ? */
5707 setlabel(flab1); /* no, simple if..., print false label */
5709 lastst_true=lastst; /* save last statement of the "true" branch */
5710 /* to avoid the "dangling else" error, we want a warning if the "else"
5711 * has a lower indent than the matching "if" */
5712 if (stmtindent<ifindent && sc_tabsize>0)
5713 error(217); /* loose indentation */
5715 if ((lastst!=tRETURN) && (lastst!=tGOTO))
5716 jumplabel(flab2); /* "true" branch jumps around "else" clause, unless the "true" branch statement already jumped */
5717 setlabel(flab1); /* print false label */
5718 statement(NULL,FALSE); /* do "else" clause */
5719 setlabel(flab2); /* print true label */
5720 /* if both the "true" branch and the "false" branch ended with the same
5721 * kind of statement, set the last statement id to that kind, rather than
5722 * to the generic tIF; this allows for better "unreachable code" checking
5724 if (lastst==lastst_true)
5730 static int dowhile(void)
5732 int wq[wqSIZE]; /* allocate local queue */
5733 int save_endlessloop,retcode;
5735 save_endlessloop=endlessloop;
5736 addwhile(wq); /* add entry to queue for "break" */
5737 setlabel(wq[wqLOOP]); /* loop label */
5738 /* The debugger uses the "break" opcode to be able to "break" out of
5739 * a loop. To make sure that each loop has a break opcode, even for the
5740 * tiniest loop, set it below the top of the loop
5743 endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
5744 statement(NULL,FALSE); /* if so, do a statement */
5745 jumplabel(wq[wqLOOP]); /* and loop to "while" start */
5746 setlabel(wq[wqEXIT]); /* exit label */
5747 delwhile(); /* delete queue entry */
5749 retcode=endlessloop ? tENDLESS : tWHILE;
5750 endlessloop=save_endlessloop;
5755 * Note that "continue" will in this case not jump to the top of the loop, but
5756 * to the end: just before the TRUE-or-FALSE testing code.
5758 static int dodo(void)
5761 int save_endlessloop,retcode;
5763 save_endlessloop=endlessloop;
5764 addwhile(wq); /* see "dowhile" for more info */
5765 top=getlabel(); /* make a label first */
5766 setlabel(top); /* loop label */
5767 statement(NULL,FALSE);
5769 setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */
5771 endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
5773 setlabel(wq[wqEXIT]);
5777 retcode=endlessloop ? tENDLESS : tDO;
5778 endlessloop=save_endlessloop;
5782 static int dofor(void)
5784 int wq[wqSIZE],skiplab;
5786 int save_nestlevel,save_endlessloop;
5791 save_nestlevel=nestlevel;
5792 save_endlessloop=endlessloop;
5797 endtok= matchtoken('(') ? ')' : tDO;
5798 if (matchtoken(';')==0) {
5799 /* new variable declarations are allowed here */
5800 if (matchtoken(tNEW)) {
5801 /* The variable in expr1 of the for loop is at a
5802 * 'compound statement' level of it own.
5806 declloc(FALSE); /* declare local variable */
5808 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */
5812 /* Adjust the "declared" field in the "while queue", in case that
5813 * local variables were declared in the first expression of the
5814 * "for" loop. These are deleted in separately, so a "break" or a "continue"
5815 * must ignore these fields.
5819 /*ptr[wqBRK]=(int)declared;
5820 *ptr[wqCONT]=(int)declared;
5822 ptr[wqBRK] = stackusage->list_id;
5823 ptr[wqCONT] = stackusage->list_id;
5824 jumplabel(skiplab); /* skip expression 3 1st time */
5825 setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */
5827 /* Expressions 2 and 3 are reversed in the generated code: expression 3
5828 * precedes expression 2. When parsing, the code is buffered and marks for
5829 * the start of each expression are insterted in the buffer.
5832 stgset(TRUE); /* start staging */
5835 stgmark(sSTARTREORDER);
5836 stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */
5837 setlabel(skiplab); /* jump to this point after 1st expression */
5838 if (matchtoken(';')) {
5841 endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
5844 stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */
5845 if (!matchtoken(endtok)) {
5846 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */
5849 stgmark(sENDREORDER); /* mark end of reversed evaluation */
5851 stgset(FALSE); /* stop staging */
5852 statement(NULL,FALSE);
5853 jumplabel(wq[wqLOOP]);
5854 setlabel(wq[wqEXIT]);
5857 assert(nestlevel>=save_nestlevel);
5858 if (nestlevel>save_nestlevel) {
5859 /* Clean up the space and the symbol table for the local
5860 * variable in "expr1".
5862 destructsymbols(&loctab,nestlevel);
5864 testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
5866 delete_symbols(&loctab,nestlevel,FALSE,TRUE);
5867 nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */
5870 index=endlessloop ? tENDLESS : tFOR;
5871 endlessloop=save_endlessloop;
5875 /* The switch statement is incompatible with its C sibling:
5876 * 1. the cases are not drop through
5877 * 2. only one instruction may appear below each case, use a compound
5878 * instruction to execute multiple instructions
5879 * 3. the "case" keyword accepts a comma separated list of values to
5883 * PRI = expression result
5884 * param = table offset (code segment)
5887 static void doswitch(void)
5889 int lbl_table,lbl_exit,lbl_case;
5890 int swdefault,casecount;
5894 constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */
5895 constvalue *cse,*csp;
5896 char labelname[sNAMEMAX+1];
5898 endtok= matchtoken('(') ? ')' : tDO;
5899 doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */
5901 /* generate the code for the switch statement, the label is the address
5902 * of the case table (to be generated later).
5904 lbl_table=getlabel();
5905 lbl_case=0; /* just to avoid a compiler warning */
5906 ffswitch(lbl_table);
5908 if (matchtoken(tBEGIN)) {
5914 lbl_exit=getlabel(); /* get label number for jumping out of switch */
5918 tok=lex(&val,&str); /* read in (new) token */
5921 if (swdefault!=FALSE)
5922 error(15); /* "default" case must be last in switch statement */
5923 lbl_case=getlabel();
5924 PUSHSTK_I(sc_allowtags);
5925 sc_allowtags=FALSE; /* do not allow tagnames here */
5929 /* ??? enforce/document that, in a switch, a statement cannot start
5930 * with a label. Then, you can search for:
5931 * * the first semicolon (marks the end of a statement)
5932 * * an opening brace (marks the start of a compound statement)
5933 * and search for the right-most colon before that statement
5934 * Now, by replacing the ':' by a special COLON token, you can
5935 * parse all expressions until that special token.
5938 constexpr(&val,NULL,NULL);
5939 /* Search the insertion point (the table is kept in sorted order, so
5940 * that advanced abstract machines can sift the case table with a
5941 * binary search). Check for duplicate case values at the same time.
5943 for (csp=&caselist, cse=caselist.next;
5944 cse!=NULL && cse->value<val;
5945 csp=cse, cse=cse->next)
5947 if (cse!=NULL && cse->value==val)
5948 error(40,val); /* duplicate "case" label */
5949 /* Since the label is stored as a string in the "constvalue", the
5950 * size of an identifier must be at least 8, as there are 8
5951 * hexadecimal digits in a 32-bit number.
5954 #error Length of identifier (sNAMEMAX) too small.
5957 assert(csp->next==cse);
5958 insert_constval(csp,cse,itoh(lbl_case),val,0);
5959 if (matchtoken(tDBLDOT)) {
5960 error(1, ":", "..");
5962 } while (matchtoken(','));
5963 needtoken(':'); /* ':' ends the case */
5964 sc_allowtags=(short)POPSTK_I(); /* reset */
5966 statement(NULL,FALSE);
5967 jumplabel(lbl_exit);
5970 if (swdefault!=FALSE)
5971 error(16); /* multiple defaults in switch */
5972 lbl_case=getlabel();
5976 statement(NULL,FALSE);
5977 /* Jump to lbl_exit, even thouh this is the last clause in the
5978 * switch, because the jump table is generated between the last
5979 * clause of the switch and the exit label.
5981 jumplabel(lbl_exit);
5986 indent_nowarn=TRUE; /* disable this check */
5987 tok=endtok; /* break out of the loop after an error */
5990 } while (tok!=endtok);
5993 /* verify that the case table is sorted (unfortunatly, duplicates can
5994 * occur; there really shouldn't be duplicate cases, but the compiler
5995 * may not crash or drop into an assertion for a user error). */
5996 for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next)
5997 assert(cse->value <= cse->next->value);
5999 /* generate the table here, before lbl_exit (general jump target) */
6000 setlabel(lbl_table);
6001 assert(swdefault==FALSE || swdefault==TRUE);
6002 if (swdefault==FALSE) {
6003 /* store lbl_exit as the "none-matched" label in the switch table */
6004 strcpy(labelname,itoh(lbl_exit));
6006 /* lbl_case holds the label of the "default" clause */
6007 strcpy(labelname,itoh(lbl_case));
6009 ffcase(casecount,labelname,TRUE);
6010 /* generate the rest of the table */
6011 for (cse=caselist.next; cse!=NULL; cse=cse->next)
6012 ffcase(cse->value,cse->name,FALSE);
6015 delete_consttable(&caselist); /* clear list of case labels */
6018 static void doassert(void)
6023 if ((sc_debug & sCHKBOUNDS)!=0) {
6024 flab1=getlabel(); /* get label number for "OK" branch */
6025 test(flab1,TEST_PLAIN,TRUE);/* get expression and branch to flab1 if true */
6026 insert_dbgline(fline); /* make sure we can find the correct line number */
6027 ffabort(xASSERTION);
6030 stgset(TRUE); /* start staging */
6031 stgget(&index,&cidx); /* mark position in code generator */
6033 expression(NULL,NULL,NULL,FALSE,NULL);
6034 stgdel(index,cidx); /* just scrap the code */
6035 } while (matchtoken(','));
6036 stgset(FALSE); /* stop staging */
6041 static void dogoto(void)
6047 /* if we were inside an endless loop, assume that we jump out of it */
6050 if (lex(&val,&st)==tSYMBOL) {
6052 jumplabel((int)sym->addr);
6053 sym->usage|=uREAD; /* set "uREAD" bit */
6054 // ??? if the label is defined (check sym->usage & uDEFINE), check
6055 // sym->compound (nesting level of the label) against nestlevel;
6056 // if sym->compound < nestlevel, call the destructor operator
6058 error(20,st); /* illegal symbol name */
6063 static void dolabel(void)
6069 tokeninfo(&val,&st); /* retrieve label name again */
6070 if (find_constval(&tagname_tab,st,0)!=NULL)
6071 error(221,st); /* label name shadows tagname */
6073 setlabel((int)sym->addr);
6074 /* since one can jump around variable declarations or out of compound
6075 * blocks, the stack must be manually adjusted
6077 //:TODO: This is actually generated, egads!
6078 //We have to support this and LCTRL/SCTRL
6079 setstk(-declared*sizeof(cell));
6080 sym->usage|=uDEFINE; /* label is now defined */
6085 * Finds a label from the (local) symbol table or adds one to it.
6086 * Labels are local in scope.
6088 * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()"
6089 * must set this bit accordingly.
6091 static symbol *fetchlab(char *name)
6095 sym=findloc(name); /* labels are local in scope */
6097 if (sym->ident!=iLABEL)
6098 error(19,sym->name); /* not a label: ... */
6100 sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0);
6101 assert(sym!=NULL); /* fatal error 103 must be given on error */
6102 sym->x.declared=(int)declared;
6103 sym->compound=nestlevel;
6110 * Global references: rettype (altered)
6112 static void doreturn(void)
6118 if (!matchtoken(tTERM)) {
6119 /* "return <value>" */
6120 if ((rettype & uRETNONE)!=0)
6121 error(78); /* mix "return;" and "return value;" */
6122 ident=doexpr(TRUE,FALSE,TRUE,FALSE,&tag,&sym,TRUE);
6124 /* see if this function already has a sub type (an array attached) */
6125 sub=finddepend(curfunc);
6126 assert(sub==NULL || sub->ident==iREFARRAY);
6127 if ((rettype & uRETVALUE)!=0) {
6128 int retarray=(ident==iARRAY || ident==iREFARRAY);
6129 /* there was an earlier "return" statement in this function */
6130 if (sub==NULL && retarray || sub!=NULL && !retarray)
6131 error(79); /* mixing "return array;" and "return value;" */
6132 if (retarray && (curfunc->usage & uPUBLIC)!=0)
6133 error(90,curfunc->name); /* public function may not return array */
6135 rettype|=uRETVALUE; /* function returns a value */
6136 /* check tagname with function tagname */
6137 assert(curfunc!=NULL);
6138 if (!matchtag_string(ident, tag) && !matchtag(curfunc->tag,tag,TRUE))
6139 error(213); /* tagname mismatch */
6140 if (ident==iARRAY || ident==iREFARRAY) {
6141 int dim[sDIMEN_MAX],numdim;
6145 assert(sub->ident==iREFARRAY);
6146 /* this function has an array attached already; check that the current
6147 * "return" statement returns exactly the same array
6149 level=sym->dim.array.level;
6150 if (sub->dim.array.level!=level) {
6151 error(48); /* array dimensions must match */
6153 for (numdim=0; numdim<=level; numdim++) {
6154 dim[numdim]=(int)sub->dim.array.length;
6155 if (sym->dim.array.length!=dim[numdim])
6156 error(47); /* array sizes must match */
6158 sym=finddepend(sym);
6159 sub=finddepend(sub);
6160 assert(sym!=NULL && sub!=NULL);
6161 /* ^^^ both arrays have the same dimensions (this was checked
6162 * earlier) so the dependend should always be found
6168 int idxtag[sDIMEN_MAX];
6170 /* this function does not yet have an array attached; clone the
6171 * returned symbol beneath the current function
6175 level=sub->dim.array.level;
6176 for (numdim=0; numdim<=level; numdim++) {
6177 dim[numdim]=(int)sub->dim.array.length;
6178 idxtag[numdim]=sub->x.tags.index;
6180 sub=finddepend(sub);
6183 /* check that all dimensions are known */
6185 error(46,sym->name);
6187 /* the address of the array is stored in a hidden parameter; the address
6188 * of this parameter is 1 + the number of parameters (times the size of
6189 * a cell) + the size of the stack frame and the return address
6190 * base + 0*sizeof(cell) == previous "base"
6191 * base + 1*sizeof(cell) == function return address
6192 * base + 2*sizeof(cell) == number of arguments
6193 * base + 3*sizeof(cell) == first argument of the function
6195 * base + ((n-1)+3)*sizeof(cell) == last argument of the function
6196 * base + (n+3)*sizeof(cell) == hidden parameter with array address
6198 assert(curfunc!=NULL);
6199 assert(curfunc->dim.arglist!=NULL);
6200 for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++)
6202 sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag);
6203 sub->parent=curfunc;
6205 /* get the hidden parameter, copy the array (the array is on the heap;
6206 * it stays on the heap for the moment, and it is removed -usually- at
6207 * the end of the expression/statement, see expression() in SC3.C)
6209 address(sub,sALT); /* ALT = destination */
6210 arraysize=calc_arraysize(dim,numdim,0);
6211 memcopy(arraysize*sizeof(cell)); /* source already in PRI */
6212 /* moveto1(); is not necessary, callfunction() does a popreg() */
6215 /* this return statement contains no expression */
6217 if ((rettype & uRETVALUE)!=0) {
6218 char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
6219 assert(curfunc!=NULL);
6220 funcdisplayname(symname,curfunc->name);
6221 error(209,symname); /* function should return a value */
6223 rettype|=uRETNONE; /* function does not return anything */
6225 destructsymbols(&loctab,0); /* call destructor for *all* locals */
6227 genstackfree(-1); /* free everything on the stack */
6228 ffret(strcmp(curfunc->name,uENTRYFUNC)!=0);
6231 static void dobreak(void)
6235 endlessloop=0; /* if we were inside an endless loop, we just jumped out */
6236 ptr=readwhile(); /* readwhile() gives an error if not in loop */
6240 destructsymbols(&loctab,nestlevel);
6241 genstackfree(ptr[wqBRK]);
6242 jumplabel(ptr[wqEXIT]);
6245 static void docont(void)
6249 ptr=readwhile(); /* readwhile() gives an error if not in loop */
6253 destructsymbols(&loctab,nestlevel);
6254 genstackfree(ptr[wqCONT]);
6255 genheapfree(ptr[wqCONT]);
6256 jumplabel(ptr[wqLOOP]);
6259 SC_FUNC void exporttag(int tag)
6261 /* find the tag by value in the table, then set the top bit to mark it
6264 if (tag!=0 && (tag & PUBLICTAG)==0) {
6266 for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next)
6269 ptr->value |= PUBLICTAG;
6273 static void doexit(void)
6277 if (matchtoken(tTERM)==0){
6278 doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
6285 destructsymbols(&loctab,0); /* call destructor for *all* locals */
6289 static void dosleep(void)
6293 if (matchtoken(tTERM)==0){
6294 doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
6303 /* for stack usage checking, mark the use of the sleep instruction */
6304 pc_memflags |= suSLEEP_INSTR;
6307 static void dostate(void)
6309 char name[sNAMEMAX+1];
6310 constvalue *automaton;
6315 #if !defined SC_LIGHT
6316 int length,index,listid,listindex,stateindex;
6320 /* check for an optional condition */
6321 if (matchtoken('(')) {
6322 flabel=getlabel(); /* get label number for "false" branch */
6323 pc_docexpr=TRUE; /* attach expression as a documentation string */
6324 test(flabel,TEST_PLAIN,FALSE);/* get expression, branch to flabel if false */
6331 if (!sc_getstateid(&automaton,&state)) {
6332 delete_autolisttable();
6337 /* store the new state id */
6338 assert(state!=NULL);
6339 ldconst(state->value,sPRI);
6340 assert(automaton!=NULL);
6341 assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
6342 storereg(automaton->value,sPRI);
6344 /* find the optional entry() function for the state */
6345 sym=findglb(uENTRYFUNC,sGLOBAL);
6346 if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) {
6347 for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) {
6348 assert(strlen(stlist->name)!=0);
6349 if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value))
6352 assert(stlist==NULL || state_inlist(stlist->index,state->value));
6354 /* the label to jump to is in stlist->name */
6355 ffcall(sym,stlist->name,0);
6360 setlabel(flabel); /* condition was false, jump around the state switch */
6362 #if !defined SC_LIGHT
6363 /* mark for documentation */
6364 if (sc_status==statFIRST) {
6366 /* get the last list id attached to the function, this contains the source states */
6367 assert(curfunc!=NULL);
6368 if (curfunc->states!=NULL) {
6369 stlist=curfunc->states->next;
6370 assert(stlist!=NULL);
6371 while (stlist->next!=NULL)
6372 stlist=stlist->next;
6373 listid=stlist->index;
6378 length=strlen(name)+70; /* +70 for the fixed part "<transition ... />\n" */
6379 /* see if there are any condition strings to attach */
6380 for (index=0; (str=get_autolist(index))!=NULL; index++)
6381 length+=strlen(str);
6382 if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) {
6384 sprintf(doc,"<transition target=\"%s\"",name);
6386 /* get the source state */
6387 stateindex=state_listitem(listid,listindex);
6388 state=state_findid(stateindex);
6389 assert(state!=NULL);
6390 sprintf(doc+strlen(doc)," source=\"%s\"",state->name);
6392 if (get_autolist(0)!=NULL) {
6393 /* add the condition */
6394 strcat(doc," condition=\"");
6395 for (index=0; (str=get_autolist(index))!=NULL; index++) {
6396 /* remove the ')' token that may be appended before detecting that the expression has ended */
6397 if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL)
6403 insert_docstring(doc);
6404 } while (listid>=0 && ++listindex<state_count(listid));
6409 delete_autolisttable();
6413 static void addwhile(int *ptr)
6417 ptr[wqBRK]=(int)declared; /* stack pointer (for "break") */
6418 ptr[wqCONT]=(int)declared; /* for "continue", possibly adjusted later */
6419 ptr[wqLOOP]=getlabel();
6420 ptr[wqEXIT]=getlabel();
6421 if (wqptr>=(wq+wqTABSZ-wqSIZE))
6422 error(102,"loop table"); /* loop table overflow (too many active loops)*/
6424 while (k<wqSIZE){ /* copy "ptr" to while queue table */
6432 static void delwhile(void)
6438 static int *readwhile(void)
6441 error(24); /* out of context */
6444 return (wqptr-wqSIZE);