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
75 int pc_tag_string = 0;
77 static void resetglobals(void);
78 static void initglobals(void);
79 static char *get_extension(char *filename);
80 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
81 char *rname,char *codepage);
82 static void setconfig(char *root);
83 static void setcaption(void);
84 static void about(void);
85 static void setconstants(void);
86 static void parse(void);
87 static void dumplits(void);
88 static void dumpzero(int count);
89 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst);
90 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
91 int stock,int fconst);
92 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
93 static int declloc(int fstatic);
94 static void decl_const(int table);
95 static void declstruct();
96 static void decl_enum(int table);
97 static cell needsub(int *tag,constvalue **enumroot);
98 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
99 constvalue *enumroot);
100 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
101 int startlit,int counteddim[],constvalue *lastdim,
102 constvalue *enumroot,int *errorfound);
103 static cell initvector(int ident,int tag,cell size,int fillzero,
104 constvalue *enumroot,int *errorfound);
105 static cell init(int ident,int *tag,int *errorfound);
106 static int getstates(const char *funcname);
107 static void attachstatelist(symbol *sym, int state_id);
108 static void funcstub(int fnative);
109 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
110 static int declargs(symbol *sym,int chkshadow);
111 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
112 int fpublic,int fconst,int chkshadow,arginfo *arg);
113 static void make_report(symbol *root,FILE *log,char *sourcefile);
114 static void reduce_referrers(symbol *root);
115 static long max_stacksize(symbol *root,int *recursion);
116 static int testsymbols(symbol *root,int level,int testlabs,int testconst);
117 static void destructsymbols(symbol *root,int level);
118 static constvalue *find_constval_byval(constvalue *table,cell val);
119 static symbol *fetchlab(char *name);
120 static void statement(int *lastindent,int allow_decl);
121 static void compound(int stmt_sameline,int starttok);
122 static int test(int label,int parens,int invert);
123 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
124 int *tag,symbol **symptr,int chkfuncresult);
125 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
126 int *tag,symbol **symptr,int chkfuncresult,value *lval);
127 static void doassert(void);
128 static void doexit(void);
129 static int doif(void);
130 static int dowhile(void);
131 static int dodo(void);
132 static int dofor(void);
133 static void doswitch(void);
134 static void dogoto(void);
135 static void dolabel(void);
136 static void doreturn(void);
137 static void dofuncenum(int listmode);
138 static void dobreak(void);
139 static void docont(void);
140 static void dosleep(void);
141 static void dostate(void);
142 static void addwhile(int *ptr);
143 static void delwhile(void);
144 static int *readwhile(void);
145 static void inst_datetime_defines(void);
148 TEST_PLAIN, /* no parentheses */
149 TEST_THEN, /* '(' <expr> ')' or <expr> 'then' */
150 TEST_DO, /* '(' <expr> ')' or <expr> 'do' */
151 TEST_OPT, /* '(' <expr> ')' or <expr> */
153 static int norun = 0; /* the compiler never ran */
154 static int autozero = 1; /* if 1 will zero out the variable, if 0 omit the zeroing */
155 static int lastst = 0; /* last executed statement type */
156 static int nestlevel = 0; /* number of active (open) compound statements */
157 static int endlessloop= 0; /* nesting level of endless loop */
158 static int rettype = 0; /* the type that a "return" expression should have */
159 static int skipinput = 0; /* number of lines to skip from the first input file */
160 static int optproccall = TRUE; /* support "procedure call" */
161 static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */
162 static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */
163 static int sc_parsenum = 0; /* number of the extra parses */
164 static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */
165 static int *wqptr; /* pointer to next entry */
166 #if !defined SC_LIGHT
167 static char sc_rootpath[_MAX_PATH];
168 static char *sc_documentation=NULL;/* main documentation */
170 #if defined __WIN32__ || defined _WIN32 || defined _Windows
171 static HWND hwndFinish = 0;
174 SC_VDECL int glbstringread = 0;
175 char g_tmpfile[_MAX_PATH] = {0};
177 /* "main" of the compiler
179 #if defined __cplusplus
182 int pc_compile(int argc, char *argv[])
186 char incfname[_MAX_PATH];
187 char reportname[_MAX_PATH];
188 char codepage[MAXCODEPAGE+1];
191 int lcl_packstr,lcl_needsemicolon,lcl_tabsize;
192 #if !defined SC_LIGHT
197 /* set global variables to their initial value */
201 errorset(sEXPRRELEASE,0);
204 /* make sure that we clean up on a fatal error; do this before the first
205 * call to error(). */
206 if ((jmpcode=setjmp(errbuf))!=0)
209 /* allocate memory for fixed tables */
210 inpfname=(char*)malloc(_MAX_PATH);
212 error(123); /* insufficient memory */
213 litq=(cell*)malloc(litmax*sizeof(cell));
215 error(123); /* insufficient memory */
217 error(123); /* insufficient memory */
219 setopt(argc,argv,outfname,errfname,incfname,reportname,codepage);
220 strcpy(binfname,outfname);
221 ptr=get_extension(binfname);
222 if (ptr!=NULL && stricmp(ptr,".asm")==0)
223 set_extension(binfname,".smx",TRUE);
225 set_extension(binfname,".smx",FALSE);
226 /* set output names that depend on the input name */
228 set_extension(outfname,".lst",TRUE);
230 set_extension(outfname,".asm",TRUE);
231 if (strlen(errfname)!=0)
232 remove(errfname); /* delete file on startup */
233 else if (verbosity>0)
235 setconfig(argv[0]); /* the path to the include and codepage files */
236 sc_ctrlchar_org=sc_ctrlchar;
237 lcl_packstr=sc_packstr;
238 lcl_needsemicolon=sc_needsemicolon;
239 lcl_tabsize=sc_tabsize;
240 #if !defined NO_CODEPAGE
241 if (!cp_set(codepage)) /* set codepage */
242 error(128); /* codepage mapping file not found */
244 /* optionally create a temporary input file that is a collection of all
247 assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */
248 if (get_sourcefile(1)!=NULL) {
249 /* there are at least two or more source files */
253 #if defined __WIN32__ || defined _WIN32
254 tname=_tempnam(NULL,"pawn");
255 #elif defined __MSDOS__ || defined _Windows
256 tname=tempnam(NULL,"pawn");
257 #elif defined(MACOS) && !defined(__MACH__)
258 /* tempnam is not supported for the Macintosh CFM build. */
259 error(124,get_sourcefile(1));
263 tname=tempnam(NULL,"pawn");
265 ftmp=(FILE*)pc_createsrc(tname);
266 for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) {
267 unsigned char tstring[128];
268 fsrc=(FILE*)pc_opensrc(sname);
272 strcpy(inpfname,sname); /* avoid invalid filename */
275 pc_writesrc(ftmp,(unsigned char*)"#file \"");
276 pc_writesrc(ftmp,(unsigned char*)sname);
277 pc_writesrc(ftmp,(unsigned char*)"\"\n");
278 while (!pc_eofsrc(fsrc) && pc_readsrc(fsrc,tstring,sizeof tstring)) {
279 pc_writesrc(ftmp,tstring);
284 strcpy(inpfname,tname);
285 strcpy(g_tmpfile,tname);
288 strcpy(inpfname,get_sourcefile(0));
290 inpf_org=(FILE*)pc_opensrc(inpfname);
294 outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */
297 /* immediately open the binary file, for other programs to check */
298 if (sc_asmfile || sc_listing) {
301 binf=(FILE*)pc_openbin(binfname);
305 setconstants(); /* set predefined constants and tagnames */
306 for (i=0; i<skipinput; i++) /* skip lines in the input file */
307 if (pc_readsrc(inpf_org,pline,sLINEMAX)!=NULL)
308 fline++; /* keep line number up to date */
311 /* write starting options (from the command line or the configuration file) */
314 sprintf(string,"#pragma ctrlchar 0x%02x\n"
316 "#pragma semicolon %s\n"
317 "#pragma tabsize %d\n",
319 sc_packstr ? "true" : "false",
320 sc_needsemicolon ? "true" : "false",
322 pc_writeasm(outf,string);
323 setfiledirect(inpfname);
325 /* do the first pass through the file (or possibly two or more "first passes") */
327 inpfmark=pc_getpossrc(inpf_org);
329 /* reset "defined" flag of all functions and global variables */
330 reduce_referrers(&glbtab);
331 delete_symbols(&glbtab,0,TRUE,FALSE);
332 #if !defined NO_DEFINE
334 inst_datetime_defines();
339 sc_ctrlchar=sc_ctrlchar_org;
340 sc_packstr=lcl_packstr;
341 sc_needsemicolon=lcl_needsemicolon;
342 sc_tabsize=lcl_tabsize;
344 /* reset the source file */
347 pc_resetsrc(inpf,inpfmark); /* reset file position */
348 fline=skipinput; /* reset line number */
349 sc_reparse=FALSE; /* assume no extra passes */
350 sc_status=statFIRST; /* resetglobals() resets it to IDLE */
352 if (strlen(incfname)>0) {
353 if (strcmp(incfname,sDEF_PREFIX)==0) {
354 plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */
356 if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */
357 error(120,incfname); /* cannot read from ... (fatal error) */
360 preprocess(); /* fetch first line */
361 parse(); /* process all input */
363 } while (sc_reparse);
365 /* second (or third) pass */
366 sc_status=statWRITE; /* set, to enable warnings */
367 state_conflict(&glbtab);
369 /* write a report, if requested */
370 #if !defined SC_LIGHT
373 if (strlen(reportname)>0)
374 frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */
376 make_report(&glbtab,frep,get_sourcefile(0));
377 if (strlen(reportname)>0)
380 if (sc_documentation!=NULL) {
381 free(sc_documentation);
382 sc_documentation=NULL;
389 /* ??? for re-parsing the listing file instead of the original source
390 * file (and doing preprocessing twice):
391 * - close input file, close listing file
392 * - re-open listing file for reading (inpf)
393 * - open assembler file (outf)
396 /* reset "defined" flag of all functions and global variables */
397 reduce_referrers(&glbtab);
398 delete_symbols(&glbtab,0,TRUE,FALSE);
401 #if !defined NO_DEFINE
403 inst_datetime_defines();
406 sc_ctrlchar=sc_ctrlchar_org;
407 sc_packstr=lcl_packstr;
408 sc_needsemicolon=lcl_needsemicolon;
409 sc_tabsize=lcl_tabsize;
411 /* reset the source file */
414 pc_resetsrc(inpf,inpfmark); /* reset file position */
415 fline=skipinput; /* reset line number */
416 lexinit(); /* clear internal flags of lex() */
417 sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */
418 writeleader(&glbtab);
419 insert_dbgfile(inpfname);
420 if (strlen(incfname)>0) {
421 if (strcmp(incfname,sDEF_PREFIX)==0)
422 plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */
424 plungequalifiedfile(incfname); /* parse implicit include file (again) */
426 preprocess(); /* fetch first line */
427 parse(); /* process all input */
428 /* inpf is already closed when readline() attempts to pop of a file */
429 writetrailer(); /* write remaining stuff */
431 entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined
432 * functions and variables */
434 error(13); /* no entry point (no public functions) */
437 if (inpf!=NULL) /* main source file is not closed, do it now */
439 /* write the binary file (the file is already open) */
440 if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) {
442 pc_resetasm(outf); /* flush and loop back, for reading */
443 #if !defined SC_LIGHT
446 assemble(binf,outf); /* assembler file is now input */
449 pc_closeasm(outf,!(sc_asmfile || sc_listing));
453 pc_closebin(binf,errnum!=0);
457 #if !defined SC_LIGHT
458 if (errnum==0 && strlen(errfname)==0) {
460 long stacksize=max_stacksize(&glbtab,&recursion);
463 long totalsize=hdrsize+code_idx;
465 totalsize+=(glb_declared+pc_stksize)*sizeof(cell);
466 if (totalsize>=pc_amxlimit)
469 if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram)
471 if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) {
472 pc_printf("Header size: %8ld bytes\n", (long)hdrsize);
473 pc_printf("Code size: %8ld bytes\n", (long)code_idx);
474 pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell));
475 pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell));
476 pc_printf("estimated max. usage");
478 pc_printf(": unknown, due to recursion\n");
479 else if ((pc_memflags & suSLEEP_INSTR)!=0)
480 pc_printf(": unknown, due to the \"sleep\" instruction\n");
482 pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell));
483 pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell));
486 error(126,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */
490 if (g_tmpfile[0] != '\0') {
493 if (inpfname!=NULL) {
501 assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols
502 * should already have been deleted */
503 delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet
504 * done (i.e. on a fatal error) */
505 delete_symbols(&glbtab,0,TRUE,TRUE);
506 delete_consttable(&tagname_tab);
507 delete_consttable(&libname_tab);
508 delete_consttable(&sc_automaton_tab);
509 delete_consttable(&sc_state_tab);
513 delete_sourcefiletable();
514 delete_dbgstringtable();
517 #if !defined NO_DEFINE
520 #if !defined SC_LIGHT
521 delete_docstringtable();
522 if (sc_documentation!=NULL)
523 free(sc_documentation);
525 delete_autolisttable();
527 if (strlen(errfname)==0)
528 pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : "");
530 } else if (warnnum!=0){
531 if (strlen(errfname)==0)
532 pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : "");
533 retcode=0; /* use "0", so that MAKE and similar tools continue */
536 if (retcode==0 && verbosity>=2)
537 pc_printf("\nDone.\n");
539 #if defined __WIN32__ || defined _WIN32 || defined _Windows
540 if (IsWindow(hwndFinish))
541 PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L);
544 Fortify_ListAllMemory();
549 #if defined __cplusplus
552 int pc_addconstant(char *name,cell value,int tag)
554 errorset(sFORCESET,0); /* make sure error engine is silenced */
556 add_constant(name,value,sGLOBAL,tag);
560 static void inst_datetime_defines(void)
568 curtime = localtime(&td);
569 strftime(date, 31, "\"%m/%d/%Y\"", curtime);
570 strftime(ltime, 31, "\"%H:%M:%S\"", curtime);
572 insert_subst("__DATE__", date, 8);
573 insert_subst("__TIME__", ltime, 8);
576 #if defined __cplusplus
579 int pc_addtag(char *name)
586 /* no tagname was given, check for one */
587 if (lex(&val,&name)!=tLABEL) {
589 return 0; /* untagged */
593 assert(strchr(name,':')==NULL); /* colon should already have been stripped */
595 ptr=tagname_tab.next;
597 tag=(int)(ptr->value & TAGMASK);
598 if (strcmp(name,ptr->name)==0)
599 return tag; /* tagname is known, return its sequence number */
600 tag &= (int)~FIXEDTAG;
601 tag &= (int)~FUNCTAG;
607 /* tagname currently unknown, add it */
608 tag=last+1; /* guaranteed not to exist already */
610 tag |= (int)FIXEDTAG;
611 append_constval(&tagname_tab,name,(cell)tag,0);
615 int pc_addfunctag(char *name)
622 /* no tagname was given, check for one */
623 if (lex(&val,&name)!=tLABEL) {
625 return 0; /* untagged */
629 assert(strchr(name,':')==NULL); /* colon should already have been stripped */
631 ptr=tagname_tab.next;
633 tag=(int)(ptr->value & TAGMASK);
634 if (strcmp(name,ptr->name)==0)
635 return tag; /* tagname is known, return its sequence number */
636 tag &= (int)~FIXEDTAG;
637 tag &= (int)~FUNCTAG;
643 /* tagname currently unknown, add it */
644 tag=last+1; /* guaranteed not to exist already */
646 tag |= (int)FIXEDTAG;
648 append_constval(&tagname_tab,name,(cell)tag,0);
652 static void resetglobals(void)
654 /* reset the subset of global variables that is modified by the first pass */
655 curfunc=NULL; /* pointer to current function */
656 lastst=0; /* last executed statement type */
657 nestlevel=0; /* number of active (open) compound statements */
658 rettype=0; /* the type that a "return" expression should have */
659 litidx=0; /* index to literal table */
660 stgidx=0; /* index to the staging buffer */
661 sc_labnum=0; /* top value of (internal) labels */
662 staging=0; /* true if staging output */
663 declared=0; /* number of local cells declared */
664 glb_declared=0; /* number of global cells declared */
665 code_idx=0; /* number of bytes with generated code */
666 ntv_funcid=0; /* incremental number of native function */
667 curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */
668 freading=FALSE; /* no input file ready yet */
669 fline=0; /* the line number in the current file */
670 fnumber=0; /* the file number in the file table (debugging) */
671 fcurrent=0; /* current file being processed (debugging) */
672 sc_intest=FALSE; /* true if inside a test */
673 sideeffect=0; /* true if an expression causes a side-effect */
674 stmtindent=0; /* current indent of the statement */
675 indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */
676 sc_allowtags=TRUE; /* allow/detect tagnames */
678 sc_allowproccall=FALSE;
679 pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */
687 static void initglobals(void)
691 sc_asmfile=FALSE; /* do not create .ASM file */
692 sc_listing=FALSE; /* do not create .LST file */
693 skipinput=0; /* number of lines to skip from the first input file */
694 sc_ctrlchar=CTRL_CHAR;/* the escape character */
695 litmax=sDEF_LITMAX; /* current size of the literal table */
696 errnum=0; /* number of errors */
697 warnnum=0; /* number of warnings */
698 optproccall=FALSE; /* sourcemod: do not support "procedure call" */
699 verbosity=1; /* verbosity level, no copyright banner */
700 sc_debug=sCHKBOUNDS|sSYMBOLIC; /* sourcemod: full debug stuff */
701 pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */
702 sc_packstr=TRUE; /* strings are packed by default */
703 sc_compress=FALSE; /* always disable compact encoding! */
704 sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */
705 sc_dataalign=sizeof(cell);
706 pc_stksize=sDEF_AMXSTACK;/* default stack size */
707 pc_amxlimit=0; /* no limit on size of the abstract machine */
708 pc_amxram=0; /* no limit on data size of the abstract machine */
709 sc_tabsize=8; /* assume a TAB is 8 spaces */
710 sc_rationaltag=0; /* assume no support for rational numbers */
711 rational_digits=0; /* number of fractional digits */
713 outfname[0]='\0'; /* output file name */
714 errfname[0]='\0'; /* error file name */
715 inpf=NULL; /* file read from */
716 inpfname=NULL; /* pointer to name of the file currently read from */
717 outf=NULL; /* file written to */
718 litq=NULL; /* the literal queue */
719 glbtab.next=NULL; /* clear global variables/constants table */
720 loctab.next=NULL; /* " local " / " " */
721 tagname_tab.next=NULL;/* tagname table */
722 libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */
724 pline[0]='\0'; /* the line read from the input file */
725 lptr=NULL; /* points to the current position in "pline" */
726 curlibrary=NULL; /* current library */
727 inpf_org=NULL; /* main source file */
729 wqptr=wq; /* initialize while queue pointer */
731 #if !defined SC_LIGHT
732 sc_documentation=NULL;
733 sc_makereport=FALSE; /* do not generate a cross-reference report */
737 static char *get_extension(char *filename)
741 assert(filename!=NULL);
742 ptr=strrchr(filename,'.');
744 /* ignore extension on a directory or at the start of the filename */
745 if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR)
752 * Set the default extension, or force an extension. To erase the
753 * extension of a filename, set "extension" to an empty string.
755 SC_FUNC void set_extension(char *filename,char *extension,int force)
759 assert(extension!=NULL && (*extension=='\0' || *extension=='.'));
760 assert(filename!=NULL);
761 ptr=get_extension(filename);
762 if (force && ptr!=NULL)
763 *ptr='\0'; /* set zero terminator at the position of the period */
764 if (force || ptr==NULL)
765 strcat(filename,extension);
768 static const char *option_value(const char *optptr)
770 return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1;
773 static int toggle_option(const char *optptr, int option)
775 switch (*option_value(optptr)) {
791 /* Parsing command line options is indirectly recursive: parseoptions()
792 * calls parserespf() to handle options in a a response file and
793 * parserespf() calls parseoptions() at its turn after having created
794 * an "option list" from the contents of the file.
796 static void parserespf(char *filename,char *oname,char *ename,char *pname,
797 char *rname, char *codepage);
799 static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname,
800 char *rname, char *codepage)
802 char str[_MAX_PATH],*name;
806 for (arg=1; arg<argc; arg++) {
808 isoption= argv[arg][0]=='-';
810 isoption= argv[arg][0]=='/' || argv[arg][0]=='-';
816 i=atoi(option_value(ptr));
817 if ((i % sizeof(cell))==0)
825 sc_asmfile=TRUE; /* skip last pass of making binary file */
829 #if 0 /* not allowed in SourceMod */
831 #if AMX_COMPACTMARGIN > 2
832 sc_compress=toggle_option(ptr,sc_compress);
839 strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */
841 #if defined dos_setdrive
842 case 'D': /* set active directory */
843 ptr=option_value(ptr);
845 dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */
849 #if 0 /* not allowed to change for SourceMod */
851 switch (*option_value(ptr)) {
856 sc_debug=sCHKBOUNDS; /* assertions and bounds checking */
859 sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */
862 sc_debug=sCHKBOUNDS | sSYMBOLIC;
863 pc_optimize=sOPTIMIZE_NONE;
864 /* also avoid peephole optimization */
872 strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */
874 #if defined __WIN32__ || defined _WIN32 || defined _Windows
876 hwndFinish=(HWND)atoi(option_value(ptr));
877 if (!IsWindow(hwndFinish))
882 strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */
885 if (str[i-1]!=DIRSEP_CHAR) {
895 sc_listing=TRUE; /* skip second pass & code generation */
898 strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */
901 pc_optimize=*option_value(ptr) - '0';
902 if (pc_optimize<sOPTIMIZE_NONE || pc_optimize>=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO)
906 strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */
908 #if !defined SC_LIGHT
910 strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */
912 if (strlen(rname)>0) {
913 set_extension(rname,".xml",FALSE);
914 } else if ((name=get_sourcefile(0))!=NULL) {
915 assert(strlen(rname)==0);
916 assert(strlen(name)<_MAX_PATH);
917 if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
918 ptr++; /* strip path */
921 assert(strlen(ptr)<_MAX_PATH);
923 set_extension(rname,".xml",TRUE);
928 i=atoi(option_value(ptr));
930 pc_stksize=(cell)i; /* stack size has minimum size */
935 skipinput=atoi(option_value(ptr));
938 sc_tabsize=atoi(option_value(ptr));
941 verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2;
942 if (sc_asmfile && verbosity>1)
946 i=(int)strtol(option_value(ptr),(char **)&ptr,10);
948 pc_enablewarning(i,0);
950 pc_enablewarning(i,1);
952 pc_enablewarning(i,2);
956 i=atoi(option_value(ptr+1));
958 pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */
962 i=atoi(option_value(ptr));
964 pc_amxlimit=(cell)i;/* abstract machine has minimum size */
969 case '\\': /* use \ instead for escape characters */
972 case '^': /* use ^ instead for escape characters */
976 sc_needsemicolon=toggle_option(ptr,sc_needsemicolon);
978 #if 0 /* not allowed to change in SourceMod */
980 optproccall=!toggle_option(ptr,!optproccall);
983 default: /* wrong option */
986 } else if (argv[arg][0]=='@') {
987 #if !defined SC_LIGHT
988 parserespf(&argv[arg][1],oname,ename,pname,rname,codepage);
990 } else if ((ptr=strchr(argv[arg],'='))!=NULL) {
991 i=(int)(ptr-argv[arg]);
994 error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */
996 strlcpy(str,argv[arg],i+1); /* str holds symbol name */
998 add_constant(str,i,sGLOBAL,0);
1000 strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".sp" */
1001 set_extension(str,".sp",FALSE);
1002 insert_sourcefile(str);
1003 /* The output name is the first input name with a different extension,
1004 * but it is stored in a different directory
1006 if (strlen(oname)==0) {
1007 if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1008 ptr++; /* strip path */
1011 assert(strlen(ptr)<_MAX_PATH);
1014 set_extension(oname,".asm",TRUE);
1015 #if !defined SC_LIGHT
1016 if (sc_makereport && strlen(rname)==0) {
1017 if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
1018 ptr++; /* strip path */
1021 assert(strlen(ptr)<_MAX_PATH);
1023 set_extension(rname,".xml",TRUE);
1030 #if !defined SC_LIGHT
1031 static void parserespf(char *filename,char *oname,char *ename,char *pname,
1032 char *rname,char *codepage)
1034 #define MAX_OPTIONS 100
1036 char *string, *ptr, **argv;
1040 if ((fp=fopen(filename,"r"))==NULL)
1041 error(120,filename); /* error reading input file */
1042 /* load the complete file into memory */
1043 fseek(fp,0L,SEEK_END);
1045 fseek(fp,0L,SEEK_SET);
1046 assert(size<INT_MAX);
1047 if ((string=(char *)malloc((int)size+1))==NULL)
1048 error(123); /* insufficient memory */
1049 /* fill with zeros; in MS-DOS, fread() may collapse CR/LF pairs to
1050 * a single '\n', so the string size may be smaller than the file
1052 memset(string,0,(int)size+1);
1053 fread(string,1,(int)size,fp);
1055 /* allocate table for option pointers */
1056 if ((argv=(char **)malloc(MAX_OPTIONS*sizeof(char*)))==NULL)
1057 error(123); /* insufficient memory */
1058 /* fill the options table */
1059 ptr=strtok(string," \t\r\n");
1060 for (argc=1; argc<MAX_OPTIONS && ptr!=NULL; argc++) {
1061 /* note: the routine skips argv[0], for compatibility with main() */
1063 ptr=strtok(NULL," \t\r\n");
1066 error(122,"option table"); /* table overflow */
1067 /* parse the option table */
1068 parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1069 /* free allocated memory */
1075 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
1076 char *rname,char *codepage)
1078 delete_sourcefiletable(); /* make sure it is empty */
1084 strcpy(pname,sDEF_PREFIX);
1086 #if 0 /* needed to test with BoundsChecker for DOS (it does not pass
1087 * through arguments) */
1088 insert_sourcefile("test.p");
1089 strcpy(oname,"test.asm");
1092 #if !defined SC_LIGHT
1093 /* first parse a "config" file with default options */
1094 if (argv[0]!=NULL) {
1095 char cfgfile[_MAX_PATH];
1097 strcpy(cfgfile,argv[0]);
1098 if ((ext=strrchr(cfgfile,DIRSEP_CHAR))!=NULL) {
1099 *(ext+1)='\0'; /* strip the program filename */
1100 strcat(cfgfile,"pawn.cfg");
1102 strcpy(cfgfile,"pawn.cfg");
1104 if (access(cfgfile,4)==0)
1105 parserespf(cfgfile,oname,ename,pname,rname,codepage);
1108 parseoptions(argc,argv,oname,ename,pname,rname,codepage);
1109 if (get_sourcefile(0)==NULL)
1113 #if defined __BORLANDC__ || defined __WATCOMC__
1116 static void setconfig(char *root)
1118 #if defined macintosh
1119 insert_path(":include:");
1121 char path[_MAX_PATH];
1125 /* add the default "include" directory */
1126 #if defined __WIN32__ || defined _WIN32
1127 GetModuleFileNameA(NULL,path,_MAX_PATH);
1130 strlcpy(path,root,sizeof path); /* path + filename (hopefully) */
1132 #if defined __MSDOS__
1133 /* strip the options (appended to the path + filename) */
1134 if ((ptr=strpbrk(path," \t/"))!=NULL)
1137 /* terminate just behind last \ or : */
1138 if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL || (ptr=strchr(path,':'))!=NULL) {
1139 /* If there is no "\" or ":", the string probably does not contain the
1140 * path; so we just don't add it to the list in that case
1144 strcat(path,"include");
1146 path[len]=DIRSEP_CHAR;
1148 /* see if it exists */
1149 if (access(path,0)!=0 && *base==DIRSEP_CHAR) {
1150 /* There is no "include" directory below the directory where the compiler
1151 * is found. This typically means that the compiler is in a "bin" sub-directory
1152 * and the "include" is below the *parent*. So find the parent...
1155 if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL) {
1157 strcat(path,"include");
1159 path[len]=DIRSEP_CHAR;
1166 /* same for the codepage root */
1167 #if !defined NO_CODEPAGE
1169 if (!cp_path(path,"codepage"))
1170 error(129,path); /* codepage path */
1172 /* also copy the root path (for the XML documentation) */
1173 #if !defined SC_LIGHT
1175 strcpy(sc_rootpath,path);
1178 #endif /* macintosh */
1181 static void setcaption(void)
1183 pc_printf("SourcePawn Compiler " SVN_FULL_VERSION "\n");
1184 pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2007 AlliedModders, LLC\n\n");
1187 static void about(void)
1189 if (strlen(errfname)==0) {
1191 pc_printf("Usage: pawncc <filename> [filename...] [options]\n\n");
1192 pc_printf("Options:\n");
1193 pc_printf(" -A<num> alignment in bytes of the data segment and the stack\n");
1194 pc_printf(" -a output assembler code\n");
1195 #if 0 /* not toggleable in SourceMod */
1196 pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-');
1198 pc_printf(" -c<name> codepage name or number; e.g. 1252 for Windows Latin-1\n");
1199 #if defined dos_setdrive
1200 pc_printf(" -Dpath active directory path\n");
1202 #if 0 /* not used for SourceMod */
1203 pc_printf(" -d<num> debugging level (default=-d%d)\n",sc_debug);
1204 pc_printf(" 0 no symbolic information, no run-time checks\n");
1205 pc_printf(" 1 run-time checks, no symbolic information\n");
1206 pc_printf(" 2 full debug information and dynamic checking\n");
1207 pc_printf(" 3 same as -d2, but implies -O0\n");
1209 pc_printf(" -e<name> set name of error file (quiet compile)\n");
1210 #if defined __WIN32__ || defined _WIN32 || defined _Windows
1211 pc_printf(" -H<hwnd> window handle to send a notification message on finish\n");
1213 pc_printf(" -i<name> path for include files\n");
1214 pc_printf(" -l create list file (preprocess only)\n");
1215 pc_printf(" -o<name> set base name of (P-code) output file\n");
1216 pc_printf(" -O<num> optimization level (default=-O%d)\n",pc_optimize);
1217 pc_printf(" 0 no optimization\n");
1218 #if 0 /* not used for SourceMod */
1219 pc_printf(" 1 JIT-compatible optimizations only\n");
1221 pc_printf(" 2 full optimizations\n");
1222 pc_printf(" -p<name> set name of \"prefix\" file\n");
1223 #if !defined SC_LIGHT
1224 pc_printf(" -r[name] write cross reference report to console or to specified file\n");
1226 pc_printf(" -S<num> stack/heap size in cells (default=%d)\n",(int)pc_stksize);
1227 pc_printf(" -s<num> skip lines from the input file\n");
1228 pc_printf(" -t<num> TAB indent size (in character positions, default=%d)\n",sc_tabsize);
1229 pc_printf(" -v<num> verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity);
1230 pc_printf(" -w<num> disable a specific warning by its number\n");
1231 pc_printf(" -X<num> abstract machine size limit in bytes\n");
1232 pc_printf(" -XD<num> abstract machine data/stack size limit in bytes\n");
1233 pc_printf(" -\\ use '\\' for escape characters\n");
1234 pc_printf(" -^ use '^' for escape characters\n");
1235 pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-');
1236 #if 0 /* not allowed in SourceMod */
1237 pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+');
1239 pc_printf(" sym=val define constant \"sym\" with value \"val\"\n");
1240 pc_printf(" sym= define constant \"sym\" with value 0\n");
1241 #if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__
1242 pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n");
1243 pc_printf("equivalent.\n");
1245 pc_printf("\nOptions with a value may optionally separate the value from the option letter\n");
1246 pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n");
1247 pc_printf("and \"-d:0\" are all equivalent.\n");
1250 longjmp(errbuf,3); /* user abort */
1253 static void setconstants(void)
1257 assert(sc_status==statIDLE);
1258 append_constval(&tagname_tab,"_",0,0);/* "untagged" */
1259 append_constval(&tagname_tab,"bool",1,0);
1261 pc_anytag = pc_addtag("any");
1262 pc_functag = pc_addfunctag("Function");
1263 pc_tag_string = pc_addtag("String");
1265 add_constant("true",1,sGLOBAL,1); /* boolean flags */
1266 add_constant("false",0,sGLOBAL,1);
1267 add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */
1268 #if PAWN_CELL_SIZE==16
1269 add_constant("cellbits",16,sGLOBAL,0);
1270 #if defined _I16_MAX
1271 add_constant("cellmax",_I16_MAX,sGLOBAL,0);
1272 add_constant("cellmin",_I16_MIN,sGLOBAL,0);
1274 add_constant("cellmax",SHRT_MAX,sGLOBAL,0);
1275 add_constant("cellmin",SHRT_MIN,sGLOBAL,0);
1277 #elif PAWN_CELL_SIZE==32
1278 add_constant("cellbits",32,sGLOBAL,0);
1279 #if defined _I32_MAX
1280 add_constant("cellmax",_I32_MAX,sGLOBAL,0);
1281 add_constant("cellmin",_I32_MIN,sGLOBAL,0);
1283 add_constant("cellmax",LONG_MAX,sGLOBAL,0);
1284 add_constant("cellmin",LONG_MIN,sGLOBAL,0);
1286 #elif PAWN_CELL_SIZE==64
1287 #if !defined _I64_MIN
1288 #define _I64_MIN (-9223372036854775807ULL - 1)
1289 #define _I64_MAX 9223372036854775807ULL
1291 add_constant("cellbits",64,sGLOBAL,0);
1292 add_constant("cellmax",_I64_MAX,sGLOBAL,0);
1293 add_constant("cellmin",_I64_MIN,sGLOBAL,0);
1295 #error Unsupported cell size
1297 add_constant("charbits",sCHARBITS,sGLOBAL,0);
1298 add_constant("charmin",0,sGLOBAL,0);
1299 add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0);
1300 add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0);
1302 add_constant("__Pawn",VERSION_INT,sGLOBAL,0);
1305 if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC))
1307 else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS)
1309 add_constant("debug",debug,sGLOBAL,0);
1311 append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */
1314 static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst)
1320 assert(fconst!=NULL);
1321 assert(fstock!=NULL);
1322 assert(fstatic!=NULL);
1323 assert(fpublic!=NULL);
1328 switch (initialtok) {
1345 tok=lex(&val,&str); /* read in (new) token */
1349 err=42; /* invalid combination of class specifiers */
1354 err=42; /* invalid combination of class specifiers */
1359 err=42; /* invalid combination of class specifiers */
1364 err=42; /* invalid combination of class specifiers */
1369 tok=0; /* force break out of loop */
1371 } while (tok && err==0);
1374 if (*fstatic && *fpublic) {
1375 err=42; /* invalid combination of class specifiers */
1376 *fstatic=*fpublic=FALSE;
1384 /* parse - process all input text
1386 * At this level, only static declarations and function definitions are legal.
1388 static void parse(void)
1390 int tok,fconst,fstock,fstatic,fpublic;
1395 /* first try whether a declaration possibly is native or public */
1396 tok=lex(&val,&str); /* read in (new) token */
1402 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst))
1403 declglb(NULL,0,fpublic,fstatic,fstock,fconst);
1406 /* This can be a static function or a static global variable; we know
1407 * which of the two as soon as we have parsed up to the point where an
1408 * opening paranthesis of a function would be expected. To back out after
1409 * deciding it was a declaration of a static variable after all, we have
1410 * to store the symbol name and tag.
1412 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1414 declfuncvar(fpublic,fstatic,fstock,fconst);
1418 decl_const(sGLOBAL);
1433 /* This can be a public function or a public variable; see the comment
1434 * above (for static functions/variables) for details.
1436 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1438 declfuncvar(fpublic,fstatic,fstock,fconst);
1442 /* This can be a stock function or a stock *global*) variable; see the
1443 * comment above (for static functions/variables) for details.
1445 if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
1447 declfuncvar(fpublic,fstatic,fstock,fconst);
1454 if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) {
1455 error(10); /* illegal function or declaration */
1456 lexclr(TRUE); /* drop the rest of the line */
1457 litidx=0; /* drop the literal queue too */
1461 funcstub(TRUE); /* create a dummy function */
1467 error(54); /* unmatched closing brace */
1470 error(55); /* start of function body without function header */
1474 error(10); /* illegal function or declaration */
1475 lexclr(TRUE); /* drop the rest of the line */
1476 litidx=0; /* drop any literal arrays (strings) */
1484 * Dump the literal pool (strings etc.)
1486 * Global references: litidx (referred to only)
1488 static void dumplits(void)
1492 if (sc_status==statSKIP)
1497 /* should be in the data segment */
1500 j=16; /* 16 values per line */
1501 while (j && k<litidx){
1502 outval(litq[k], FALSE);
1506 if (j==0 || k>=litidx)
1507 stgwrite("\n"); /* force a newline after 10 dumps */
1508 /* Note: stgwrite() buffers a line until it is complete. It recognizes
1509 * the end of line as a sequence of "\n\0", so something like "\n\t"
1510 * so should not be passed to stgwrite().
1518 * Dump zero's for default initial values
1520 static void dumpzero(int count)
1524 if (sc_status==statSKIP || count<=0)
1529 while (count-- > 0) {
1532 stgwrite((i==0 || count==0) ? "\n" : " ");
1533 if (i==0 && count>0)
1538 static void aligndata(int numbytes)
1540 assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of
1542 assert(numbytes!=0);
1544 if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) {
1545 while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0)
1551 #if !defined SC_LIGHT
1552 /* sc_attachdocumentation()
1553 * appends documentation comments to the passed-in symbol, or to a global
1554 * string if "sym" is NULL.
1556 void sc_attachdocumentation(symbol *sym)
1562 if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) {
1563 /* just clear the entire table */
1564 delete_docstringtable();
1567 /* in the case of state functions, multiple documentation sections may
1568 * appear; we should concatenate these
1569 * (with forward declarations, this is also already the case, so the assertion
1572 // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL);
1574 /* first check the size */
1576 for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
1578 length++; /* count 1 extra for a separating space */
1579 length+=strlen(str);
1581 if (sym==NULL && sc_documentation!=NULL) {
1582 length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
1583 assert(length>strlen(sc_documentation));
1587 /* allocate memory for the documentation */
1588 if (sym!=NULL && sym->documentation!=NULL)
1589 length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
1590 doc=(char*)malloc((length+1)*sizeof(char));
1592 /* initialize string or concatenate */
1593 if (sym==NULL && sc_documentation!=NULL) {
1594 strcpy(doc,sc_documentation);
1596 } else if (sym!=NULL && sym->documentation!=NULL) {
1597 strcpy(doc,sym->documentation);
1599 free(sym->documentation);
1600 sym->documentation=NULL;
1604 /* collect all documentation */
1605 while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
1609 delete_docstring(0);
1612 /* also delete the separator */
1613 assert(*str==sDOCSEP);
1614 delete_docstring(0);
1617 assert(sym->documentation==NULL);
1618 sym->documentation=doc;
1620 if (sc_documentation!=NULL)
1621 free(sc_documentation);
1622 sc_documentation=doc;
1626 /* delete an empty separator, if present */
1627 if ((str=get_docstring(0))!=NULL && *str==sDOCSEP)
1628 delete_docstring(0);
1632 static void insert_docstring_separator(void)
1634 char sep[2]={sDOCSEP,'\0'};
1635 insert_docstring(sep);
1638 #define sc_attachdocumentation(s) (void)(s)
1639 #define insert_docstring_separator()
1642 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst)
1644 char name[sNAMEMAX+11];
1649 pstruct_t *pstruct = NULL;
1652 if (tok == tLABEL) {
1653 pstruct=pstructs_find(str);
1659 /* if we arrived here, this may not be a declaration of a native function
1663 error(42); /* invalid combination of class specifiers */
1667 if (tok!=tSYMBOL && tok!=tOPERATOR) {
1670 lexclr(TRUE); /* drop the rest of the line */
1671 litidx=0; /* drop the literal queue too */
1674 if (tok==tOPERATOR) {
1675 lexpush(); /* push "operator" keyword back (for later analysis) */
1676 if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) {
1677 error(10); /* illegal function or declaration */
1678 lexclr(TRUE); /* drop the rest of the line */
1679 litidx=0; /* drop the literal queue too */
1682 /* so tok is tSYMBOL */
1683 assert(strlen(str)<=sNAMEMAX);
1685 /* only variables can be "const" or both "public" and "stock" */
1686 invalidfunc= fconst || (fpublic && fstock);
1687 if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) {
1688 /* if not a function, try a global variable */
1690 declstructvar(name,fpublic,pstruct);
1692 declglb(name,tag,fpublic,fstatic,fstock,fconst);
1698 /* declstruct - declare global struct symbols
1700 * global references: glb_declared (altered)
1702 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
1704 char name[sNAMEMAX+1];
1709 cell *values, *found;
1713 strcpy(name, firstname);
1715 values = (cell *)malloc(pstruct->argcount * sizeof(cell));
1716 found = (cell *)malloc(pstruct->argcount * sizeof(cell));
1718 memset(found, 0, sizeof(cell) * pstruct->argcount);
1720 //:TODO: Make this work with stock
1723 * Lastly, very lastly, we will insert a copy of this variable.
1724 * This is soley to expose the pubvar.
1726 usage = uREAD|uCONST|uSTRUCT;
1732 for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1734 if (strcmp(name, sym->name) == 0)
1736 if ((sym->usage & uSTRUCT) && sym->vclass == sGLOBAL)
1738 if (sym->usage & uDEFINE)
1742 if (sym->usage & uPUBLIC && !fpublic)
1756 mysym=addsym(name, 0, iVARIABLE, sGLOBAL, pc_addtag(pstruct->name), usage);
1759 if (!matchtoken('='))
1762 /* Mark it as undefined instead */
1763 mysym->usage = uSTOCK|uSTRUCT;
1766 mysym->usage = usage;
1772 /* Detect early exit */
1773 if (matchtoken('}'))
1781 error(1, "-identifier-", str);
1784 arg=pstructs_getarg(pstruct,str);
1787 error(96, str, sym->name);
1790 cur_litidx = litidx;
1798 assert(litidx != 0);
1799 if (arg->dimcount != 1)
1802 } else if (arg->tag != pc_tag_string) {
1805 values[arg->index] = glb_declared * sizeof(cell);
1806 glb_declared += (litidx-cur_litidx);
1807 found[arg->index] = 1;
1808 } else if (tok == tNUMBER || tok == tRATIONAL) {
1809 /* eat optional 'f' */
1811 if (arg->ident != iVARIABLE && arg->ident != iREFERENCE)
1815 if (arg->tag == pc_addtag("Float") && tok == tNUMBER ||
1816 arg->tag == 0 && tok == tRATIONAL)
1820 if (arg->ident == iVARIABLE)
1822 values[arg->index] = val;
1823 } else if (arg->ident == iREFERENCE) {
1824 values[arg->index] = glb_declared * sizeof(cell);
1827 cur_litidx = litidx;
1829 found[arg->index] = 1;
1831 } else if (tok == tSYMBOL) {
1832 for (sym=glbtab.next; sym!=NULL; sym=sym->next)
1834 if (sym->vclass != sGLOBAL)
1838 if (strcmp(sym->name, str) == 0)
1840 if (arg->ident == iREFERENCE && sym->ident != iVARIABLE)
1843 } else if (arg->ident == iARRAY) {
1844 if (sym->ident != iARRAY)
1848 /* :TODO: We should check dimension sizes here... */
1850 } else if (arg->ident == iREFARRAY) {
1851 if (sym->ident != iARRAY)
1855 /* :TODO: Check dimension sizes! */
1859 if (sym->tag != arg->tag)
1863 sym->usage |= uREAD;
1864 values[arg->index] = sym->addr;
1865 found[arg->index] = 1;
1866 refer_symbol(sym, mysym);
1875 error(1, "-identifier-", str);
1877 } while (matchtoken(','));
1879 matchtoken(';'); /* eat up optional semicolon */
1881 for (i=0; i<pstruct->argcount; i++)
1885 structarg_t *arg = pstruct->args[i];
1886 if (arg->ident == iREFARRAY)
1888 values[arg->index] = glb_declared * sizeof(cell);
1891 cur_litidx = litidx;
1892 } else if (arg->ident == iVARIABLE) {
1893 values[arg->index] = 0;
1895 /* :TODO: broken for iARRAY! (unused tho) */
1900 mysym->addr = glb_declared * sizeof(cell);
1901 glb_declared += pstruct->argcount;
1903 for (i=0; i<pstruct->argcount; i++)
1916 /* declglb - declare global symbols
1918 * Declare a static (global) variable. Global variables are stored in
1921 * global references: glb_declared (altered)
1923 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst)
1925 int ident,tag,ispublic;
1926 int idxtag[sDIMEN_MAX];
1927 char name[sNAMEMAX+1];
1932 int dim[sDIMEN_MAX];
1937 constvalue *enumroot;
1942 assert(!fpublic || !fstatic); /* may not both be set */
1943 insert_docstring_separator(); /* see comment in newfunc() */
1944 filenum=fcurrent; /* save file number at the start of the declaration */
1946 size=1; /* single size (no array) */
1947 numdim=0; /* no dimensions */
1949 if (firstname!=NULL) {
1950 assert(strlen(firstname)<=sNAMEMAX);
1951 strcpy(name,firstname); /* save symbol name */
1955 tag=pc_addtag(NULL);
1956 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
1957 error(20,str); /* invalid symbol name */
1958 assert(strlen(str)<=sNAMEMAX);
1959 strcpy(name,str); /* save symbol name */
1962 if (name[0]==PUBLIC_CHAR) {
1963 ispublic=TRUE; /* implicitly public variable */
1966 while (matchtoken('[')) {
1968 if (numdim == sDIMEN_MAX) {
1969 error(53); /* exceeding maximum number of dimensions */
1972 size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */
1973 #if INT_MAX < LONG_MAX
1975 error(125); /* overflow, exceeding capacity */
1977 #if 0 /* We don't actually care */
1979 error(56,name); /* arrays cannot be public */
1981 dim[numdim++]=(int)size;
1983 if (ident == iARRAY && tag == pc_tag_string && dim[numdim-1]) {
1984 slength=dim[numdim-1];
1985 dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
1987 assert(sc_curstates==0);
1988 sc_curstates=getstates(name);
1989 if (sc_curstates<0) {
1990 error(85,name); /* empty state list on declaration */
1992 } else if (sc_curstates>0 && ispublic) {
1993 error(88,name); /* public variables may not have states */
1996 sym=findconst(name,NULL);
1998 sym=findglb(name,sSTATEVAR);
1999 /* if a global variable without states is found and this declaration has
2000 * states, the declaration is okay
2002 if (sym!=NULL && sym->states==NULL && sc_curstates>0)
2003 sym=NULL; /* set to NULL, we found the global variable */
2004 if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL)
2005 error(233,name); /* state variable shadows a global variable */
2008 * a) not found a matching variable (or rejected it, because it was a shadow)
2009 * b) found a global variable and we were looking for that global variable
2010 * c) found a state variable in the automaton that we were looking for
2013 || sym->states==NULL && sc_curstates==0
2014 || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates);
2015 /* a state variable may only have a single id in its list (so either this
2016 * variable has no states, or it has a single list)
2018 assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL);
2019 /* it is okay for the (global) variable to exist, as long as it belongs to
2020 * a different automaton
2022 if (sym!=NULL && (sym->usage & uDEFINE)!=0)
2023 error(21,name); /* symbol already defined */
2024 /* if this variable is never used (which can be detected only in the
2025 * second stage), shut off code generation
2027 cidx=0; /* only to avoid a compiler warning */
2028 if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) {
2032 glbdecl=glb_declared;
2035 begdseg(); /* real (initialized) data in data segment */
2036 assert(litidx==0); /* literal queue should be empty */
2039 aligndata(sc_dataalign);
2040 dumplits(); /* dump the literal queue */
2042 litidx=0; /* global initial data is dumped, so restart at zero */
2044 assert(litidx==0); /* literal queue should be empty (again) */
2045 initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */
2046 if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
2047 slength = glbstringread;
2049 assert(size>=litidx);
2052 /* before dumping the initial values (or zeros) check whether this variable
2055 if (sc_curstates>0) {
2059 error(89,name); /* state variables may not be initialized */
2060 /* find an appropriate address for the state variable */
2061 /* assume that it cannot be found */
2062 address=sizeof(cell)*glb_declared;
2064 /* use a memory map in which every cell occupies one bit */
2065 if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) {
2066 int fsa=state_getfsa(sc_curstates);
2068 cell sweepsize,addr;
2069 memset(map,0,(glb_declared+7)/8);
2071 /* fill in all variables belonging to this automaton */
2072 for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
2073 if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
2074 continue; /* hierarchical type, or no states, or same as this variable */
2075 if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
2076 continue; /* a function or a constant */
2077 if ((sweep->usage & uDEFINE)==0)
2078 continue; /* undefined variable, ignore */
2079 if (fsa!=state_getfsa(sweep->states->next->index))
2080 continue; /* wrong automaton */
2081 /* when arrived here, this is a global variable, with states and
2082 * belonging to the same automaton as the variable we are declaring
2084 sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
2085 assert(sweep->addr % sizeof(cell) == 0);
2086 addr=sweep->addr/sizeof(cell);
2087 /* mark this address range */
2088 while (sweepsize-->0) {
2089 map[addr/8] |= (unsigned char)(1 << (addr % 8));
2093 /* go over it again, clearing any ranges that have conflicts */
2094 for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
2095 if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
2096 continue; /* hierarchical type, or no states, or same as this variable */
2097 if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
2098 continue; /* a function or a constant */
2099 if ((sweep->usage & uDEFINE)==0)
2100 continue; /* undefined variable, ignore */
2101 if (fsa!=state_getfsa(sweep->states->next->index))
2102 continue; /* wrong automaton */
2103 /* when arrived here, this is a global variable, with states and
2104 * belonging to the same automaton as the variable we are declaring
2106 /* if the lists of states of the existing variable and the new
2107 * variable have a non-empty intersection, this is not a suitable
2108 * overlap point -> wipe the address range
2110 if (state_conflict_id(sc_curstates,sweep->states->next->index)) {
2111 sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
2112 assert(sweep->addr % sizeof(cell) == 0);
2113 addr=sweep->addr/sizeof(cell);
2114 /* mark this address range */
2115 while (sweepsize-->0) {
2116 map[addr/8] &= (unsigned char)(~(1 << (addr % 8)));
2121 /* now walk through the map and find a starting point that is big enough */
2123 for (addr=0; addr<glb_declared; addr++) {
2124 if ((map[addr/8] & (1 << (addr % 8)))==0)
2126 for (sweepsize=addr+1; sweepsize<glb_declared; sweepsize++) {
2127 if ((map[sweepsize/8] & (1 << (sweepsize % 8)))==0)
2128 break; /* zero bit found, skip this range */
2129 if (sweepsize-addr>=size)
2130 break; /* fitting range found, no need to search further */
2132 if (sweepsize-addr>=size)
2133 break; /* fitting range found, no need to search further */
2137 if (sweepsize-addr>=size) {
2138 address=sizeof(cell)*addr; /* fitting range found, set it */
2143 address=sizeof(cell)*glb_declared;
2146 if (address==sizeof(cell)*glb_declared) {
2147 dumplits(); /* dump the literal queue */
2148 dumpzero((int)size-litidx);
2151 if (sym==NULL) { /* define only if not yet defined */
2152 sym=addvariable2(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag,slength);
2154 attachstatelist(sym,sc_curstates);
2155 } else { /* if declared but not yet defined, adjust the variable's address */
2156 assert(sym->states==NULL && sc_curstates==0
2157 || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL);
2159 sym->codeaddr=code_idx;
2160 sym->usage|=uDEFINE;
2165 sym->usage|=uPUBLIC|uREAD;
2171 sym->fnumber=filenum;
2172 sc_attachdocumentation(sym);/* attach any documenation to the variable */
2173 if (sc_status==statSKIP) {
2174 sc_status=statWRITE;
2176 assert(glb_declared==glbdecl);
2178 glb_declared+=glb_incr; /* add total number of cells (if added to the end) */
2180 } while (matchtoken(',')); /* enddo */ /* more? */
2181 needtoken(tTERM); /* if not comma, must be semicolumn */
2184 /* declloc - declare local symbols
2186 * Declare local (automatic) variables. Since these variables are relative
2187 * to the STACK, there is no switch to the DATA segment. These variables
2188 * cannot be initialized either.
2190 * global references: declared (altered)
2191 * funcstatus (referred to only)
2193 static int declloc(int fstatic)
2196 int idxtag[sDIMEN_MAX];
2197 char name[sNAMEMAX+1];
2199 constvalue *enumroot=NULL;
2204 int dim[sDIMEN_MAX];
2210 fconst=matchtoken(tCONST);
2215 numdim=0; /* no dimensions */
2216 tag=pc_addtag(NULL);
2217 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
2218 error(20,str); /* invalid symbol name */
2219 assert(strlen(str)<=sNAMEMAX);
2220 strcpy(name,str); /* save symbol name */
2221 if (name[0]==PUBLIC_CHAR)
2222 error(56,name); /* local variables cannot be public */
2223 /* Note: block locals may be named identical to locals at higher
2224 * compound blocks (as with standard C); so we must check (and add)
2225 * the "nesting level" of local variables to verify the
2226 * multi-definition of symbols.
2228 if ((sym=findloc(name))!=NULL && sym->compound==nestlevel)
2229 error(21,name); /* symbol already defined */
2230 /* Although valid, a local variable whose name is equal to that
2231 * of a global variable or to that of a local variable at a lower
2232 * level might indicate a bug.
2234 if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL)
2235 error(219,name); /* variable shadows another symbol */
2236 if (matchtoken('[')) {
2242 int all_constant = 1;
2243 int _staging = staging;
2247 stgget(&_index, &_code);
2250 if (numdim == sDIMEN_MAX) {
2251 error(53); /* exceeding maximum number of dimensions */
2252 return (all_constant ? iARRAY : iREFARRAY);
2253 } else if (numdim) { /* if */
2254 /* If we have a dimension on the stack, push it */
2257 if (matchtoken(']')) {
2263 dim_ident = doexpr2(TRUE,FALSE,FALSE,FALSE,&idxtag[numdim],&dim_sym,0,&dim_val);
2264 if (dim_ident == iVARIABLE || dim_ident == iEXPRESSION || dim_ident == iARRAYCELL) {
2266 } else if (dim_ident == iCONSTEXPR) {
2267 dim[numdim] = dim_val.constval;
2268 /* :TODO: :URGENT: Make sure this still works */
2269 if (dim_sym && dim_sym->usage & uENUMROOT)
2270 enumroot = dim_sym->dim.enumlist;
2271 idxtag[numdim] = dim_sym ? dim_sym->tag : 0;
2272 #if INT_MAX < LONG_MAX
2273 if (dim[numdim] > INT_MAX)
2274 error(125); /* overflow, exceeding capacity */
2277 error(29); /* invalid expression, assumed 0 */
2281 } while (matchtoken('['));
2283 /* Change the last dimension to be based on chars instead if we have a string */
2284 if (tag == pc_tag_string && numdim && dim[numdim-1]) {
2285 slength = dim[numdim-1];
2286 dim[numdim-1] = (dim[numdim-1] + sizeof(cell)-1) / sizeof(cell);
2288 /* Scrap the code generated */
2290 stgdel(_index, _code);
2292 if (tag == pc_tag_string && numdim && dim[numdim-1]) {
2296 memset(dim, 0, sizeof(int)*sDIMEN_MAX);
2298 genarray(numdim, autozero);
2304 if (getstates(name))
2305 error(88,name); /* local variables may not have states */
2306 if (ident==iARRAY || fstatic) {
2308 aligndata(sc_dataalign);
2311 cur_lit=litidx; /* save current index in the literal table */
2312 if (numdim && !dim[numdim-1])
2314 initials(ident,tag,&size,dim,numdim,enumroot);
2315 if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
2316 slength = glbstringread;
2319 return ident; /* error message already given */
2323 /* reserve memory (on the stack) for the variable */
2325 /* write zeros for uninitialized fields */
2326 while (litidx<cur_lit+size)
2328 sym=addvariable2(name,(cur_lit+glb_declared)*sizeof(cell),ident,sSTATIC,
2329 tag,dim,numdim,idxtag,slength);
2330 } else if (ident!=iREFARRAY) {
2331 declared+=(int)size; /* variables are put on stack, adjust "declared" */
2332 sym=addvariable2(name,-declared*sizeof(cell),ident,sLOCAL,tag,
2333 dim,numdim,idxtag,slength);
2334 if (ident==iVARIABLE) {
2336 stgset(TRUE); /* start stage-buffering */
2338 staging_start=stgidx;
2340 markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
2341 modstk(-(int)size*sizeof(cell));
2342 markstack(MEMUSE_STATIC, size);
2343 assert(curfunc!=NULL);
2344 assert((curfunc->usage & uNATIVE)==0);
2345 if (curfunc->x.stacksize<declared+1)
2346 curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
2347 } else if (ident==iREFARRAY) {
2348 declared+=1; /* one cell for address */
2349 sym=addvariable(name,-declared*sizeof(cell),ident,sLOCAL,tag,dim,numdim,idxtag);
2350 //markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
2351 /* genarray() pushes the address onto the stack, so we don't need to call modstk() here! */
2352 markheap(MEMUSE_DYNAMIC, 0);
2353 markstack(MEMUSE_STATIC, 1);
2354 assert(curfunc != NULL && ((curfunc->usage & uNATIVE) == 0));
2355 if (curfunc->x.stacksize<declared+1)
2356 curfunc->x.stacksize=declared+1; /* +1 for PROC opcode */
2358 /* now that we have reserved memory for the variable, we can proceed
2359 * to initialize it */
2360 assert(sym!=NULL); /* we declared it, it must be there */
2361 sym->compound=nestlevel; /* for multiple declaration/shadowing check */
2364 if (!fstatic) { /* static variables already initialized */
2365 if (ident==iVARIABLE) {
2366 /* simple variable, also supports initialization */
2367 int ctag = tag; /* set to "tag" by default */
2368 int explicit_init=FALSE;/* is the variable explicitly initialized? */
2370 if (matchtoken('=')) {
2373 cident=doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE);
2377 ldconst(0,sPRI); /* uninitialized variable, set to zero */
2380 /* now try to save the value (still in PRI) in the variable */
2382 lval.ident=iVARIABLE;
2385 check_userop(NULL,ctag,lval.tag,2,NULL,&ctag);
2387 markexpr(sEXPR,NULL,0); /* full expression ends after the store */
2389 assert(staging); /* end staging phase (optimize expression) */
2390 stgout(staging_start);
2392 if (!matchtag_string(cident, ctag) && !matchtag(tag,ctag,TRUE))
2395 error(100); /* error - function prototypes do not match */
2397 error(213); /* warning - tag mismatch */
2399 /* if the variable was not explicitly initialized, reset the
2400 * "uWRITTEN" flag that store() set */
2402 sym->usage &= ~uWRITTEN;
2403 } else if (ident!=iREFARRAY) {
2405 assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax);
2406 assert(size>0 && size>=sym->dim.array.length);
2407 assert(numdim>1 || size==sym->dim.array.length);
2409 /* final literal values that are zero make no sense to put in the literal
2410 * pool, because values get zero-initialized anyway; we check for this,
2411 * because users often explicitly initialize strings to ""
2413 while (litidx>cur_lit && litq[litidx-1]==0)
2415 /* if the array is not completely filled, set all values to zero first */
2416 if (litidx-cur_lit<size && (ucell)size<CELL_MAX)
2417 fillarray(sym,size*sizeof(cell),0);
2419 if (cur_lit<litidx) {
2420 /* check whether the complete array is set to a single value; if
2421 * it is, more compact code can be generated */
2422 cell first=litq[cur_lit];
2424 for (i=cur_lit; i<litidx && litq[i]==first; i++)
2427 /* all values are the same */
2428 fillarray(sym,(litidx-cur_lit)*sizeof(cell),first);
2429 litidx=cur_lit; /* reset literal table */
2431 /* copy the literals to the array */
2432 ldconst((cur_lit+glb_declared)*sizeof(cell),sPRI);
2433 copyarray(sym,(litidx-cur_lit)*sizeof(cell));
2438 } while (matchtoken(',')); /* enddo */ /* more? */
2439 needtoken(tTERM); /* if not comma, must be semicolumn */
2443 /* this function returns the maximum value for a cell in case of an error
2444 * (invalid dimension).
2446 static cell calc_arraysize(int dim[],int numdim,int cur)
2451 /* the return value is in cells, not bytes */
2452 assert(cur>=0 && cur<=numdim);
2455 subsize=calc_arraysize(dim,numdim,cur+1);
2456 newsize=dim[cur]+dim[cur]*subsize;
2457 if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize*sizeof(cell)>=CELL_MAX)
2462 static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment,
2463 int startlit,constvalue *lastdim,int *skipdim)
2469 assert(cur>=0 && cur<numdim);
2470 assert(increment>=0);
2471 assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx);
2476 /* 2 or more dimensions left, fill in an indirection vector */
2479 for (d=0; d<dim[cur]; d++)
2480 litq[base++]=(dim[cur]+d*(dim[cur+1]-1)+increment) * sizeof(cell);
2481 accum=dim[cur]*(dim[cur+1]-1);
2483 /* final dimension is variable length */
2485 assert(dim[cur+1]==0);
2486 assert(lastdim!=NULL);
2487 assert(skipdim!=NULL);
2489 /* skip the final dimension sizes for all earlier major dimensions */
2490 for (d=0,ld=lastdim->next; d<*skipdim; d++,ld=ld->next) {
2493 for (d=0; d<dim[cur]; d++) {
2495 assert(strtol(ld->name,NULL,16)==d);
2496 litq[base++]=(dim[cur]+accum+increment) * sizeof(cell);
2502 /* create the indirection tables for the lower level */
2503 if (cur+2<numdim) { /* are there at least 2 dimensions below this one? */
2504 increment+=(dim[cur]-1)*dim[cur+1]; /* this many indirection tables follow */
2505 for (d=0; d<dim[cur]; d++)
2506 increment+=adjust_indirectiontables(dim,numdim,cur+1,increment,-1,lastdim,skipdim);
2513 * Initialize global objects and local arrays.
2514 * size==array cells (count), if 0 on input, the routine counts the number of elements
2515 * tag==required tagname id (not the returned tag)
2517 * Global references: litidx (altered)
2519 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
2520 constvalue *enumroot)
2527 if (!matchtoken('=')) {
2528 assert(ident!=iARRAY || numdim>0);
2529 if (ident==iARRAY && dim[numdim-1]==0) {
2530 /* declared as "myvar[];" which is senseless (note: this *does* make
2531 * sense in the case of a iREFARRAY, which is a function parameter)
2533 error(9); /* array has zero length -> invalid size */
2535 if (ident==iARRAY) {
2536 assert(numdim>0 && numdim<=sDIMEN_MAX);
2537 *size=calc_arraysize(dim,numdim,0);
2538 if (*size==(cell)CELL_MAX) {
2539 error(9); /* array is too big -> invalid size */
2542 /* first reserve space for the indirection vectors of the array, then
2543 * adjust it to contain the proper values
2544 * (do not use dumpzero(), as it bypasses the literal queue)
2546 for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
2548 if (dim[numdim-1]!=0) /* error 9 has already been given */
2549 adjust_indirectiontables(dim,numdim,0,0,curlit,NULL,NULL);
2554 if (ident==iVARIABLE) {
2556 init(ident,&ctag,NULL);
2557 if (!matchtag(tag,ctag,TRUE))
2560 error(100); /* error - function prototypes do not match */
2562 error(213); /* warning - tag mismatch */
2567 *size=initvector(ident,tag,dim[0],FALSE,enumroot,NULL);
2569 int errorfound=FALSE;
2570 int counteddim[sDIMEN_MAX];
2572 constvalue lastdim={NULL,"",0,0}; /* sizes of the final dimension */
2575 if (dim[numdim-1]!=0)
2576 *size=calc_arraysize(dim,numdim,0); /* calc. full size, if known */
2577 /* already reserve space for the indirection tables (for an array with
2579 * (do not use dumpzero(), as it bypasses the literal queue)
2581 for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
2583 /* now initialize the sub-arrays */
2584 memset(counteddim,0,sizeof counteddim);
2585 initarray(ident,tag,dim,numdim,0,curlit,counteddim,&lastdim,enumroot,&errorfound);
2586 /* check the specified array dimensions with the initializer counts */
2587 for (idx=0; idx<numdim-1; idx++) {
2589 dim[idx]=counteddim[idx];
2590 } else if (counteddim[idx]<dim[idx]) {
2591 error(52); /* array is not fully initialized */
2593 } else if (counteddim[idx]>dim[idx]) {
2594 error(18); /* initialization data exceeds declared size */
2598 if (numdim>1 && dim[numdim-1]==0) {
2599 /* also look whether, by any chance, all "counted" final dimensions are
2600 * the same value; if so, we can store this
2602 constvalue *ld=lastdim.next;
2604 for (d=0; d<dim[numdim-2]; d++) {
2606 assert(strtol(ld->name,NULL,16)==d);
2609 else if (match!=ld->value)
2613 if (d==dim[numdim-2])
2614 dim[numdim-1]=match;
2616 /* after all arrays have been initalized, we know the (major) dimensions
2617 * of the array and we can properly adjust the indirection vectors
2620 adjust_indirectiontables(dim,numdim,0,0,curlit,&lastdim,&skipdim);
2621 delete_consttable(&lastdim); /* clear list of minor dimension sizes */
2626 *size=litidx-curlit; /* number of elements defined */
2629 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
2630 int startlit,int counteddim[],constvalue *lastdim,
2631 constvalue *enumroot,int *errorfound)
2633 cell dsize,totalsize;
2636 assert(cur>=0 && cur<numdim);
2637 assert(startlit>=0);
2638 assert(cur+2<=numdim);/* there must be 2 dimensions or more to do */
2639 assert(errorfound!=NULL && *errorfound==FALSE);
2642 for (idx=0,abortparse=FALSE; !abortparse; idx++) {
2643 /* In case the major dimension is zero, we need to store the offset
2644 * to the newly detected sub-array into the indirection table; i.e.
2645 * this table needs to be expanded and updated.
2646 * In the current design, the indirection vectors for a multi-dimensional
2647 * array are adjusted after parsing all initializers. Hence, it is only
2648 * necessary at this point to reserve space for an extra cell in the
2649 * indirection vector.
2652 litinsert(0,startlit);
2653 } else if (idx>=dim[cur]) {
2654 error(18); /* initialization data exceeds array size */
2658 dsize=initarray(ident,tag,dim,numdim,cur+1,startlit,counteddim,
2659 lastdim,enumroot,errorfound);
2661 dsize=initvector(ident,tag,dim[cur+1],TRUE,enumroot,errorfound);
2662 /* The final dimension may be variable length. We need to keep the
2663 * lengths of the final dimensions in order to set the indirection
2664 * vectors for the next-to-last dimension.
2666 append_constval(lastdim,itoh(idx),dsize,0);
2669 if (*errorfound || !matchtoken(','))
2673 assert(counteddim!=NULL);
2674 if (counteddim[cur]>0) {
2675 if (idx<counteddim[cur])
2676 error(52); /* array is not fully initialized */
2677 else if (idx>counteddim[cur])
2678 error(18); /* initialization data exceeds declared size */
2680 counteddim[cur]=idx;
2682 return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */
2686 * Initialize a single dimensional array
2688 static cell initvector(int ident,int tag,cell size,int fillzero,
2689 constvalue *enumroot,int *errorfound)
2691 cell prev1=0,prev2=0;
2696 assert(ident==iARRAY || ident==iREFARRAY);
2697 if (matchtoken('{')) {
2698 constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL;
2700 int fieldlit=litidx;
2702 if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */
2706 if ((ellips=matchtoken(tELLIPS))!=0)
2708 /* for enumeration fields, allow another level of braces ("{...}") */
2709 matchbrace=0; /* preset */
2711 if (enumfield!=NULL)
2712 matchbrace=matchtoken('{');
2715 prev1=init(ident,&ctag,errorfound);
2718 if ((ellips=matchtoken(tELLIPS))!=0)
2720 if (!matchtoken(',')) {
2725 /* if this array is based on an enumeration, fill the "field" up with
2726 * zeros, and toggle the tag
2728 if (enumroot!=NULL && enumfield==NULL)
2729 error(227); /* more initializers than enum fields */
2730 rtag=tag; /* preset, may be overridden by enum field tag */
2731 if (enumfield!=NULL) {
2733 int cmptag=enumfield->index;
2734 symbol *symfield=findconst(enumfield->name,&cmptag);
2736 error(91,enumfield->name); /* ambiguous constant, needs tag override */
2737 assert(symfield!=NULL);
2738 assert(fieldlit<litidx);
2739 if (litidx-fieldlit>symfield->dim.array.length)
2740 error(228); /* length of initializer exceeds size of the enum field */
2747 for (i=litidx-fieldlit; i<symfield->dim.array.length; i++) {
2751 rtag=symfield->x.tags.index; /* set the expected tag to the index tag */
2752 enumfield=enumfield->next;
2754 if (!matchtag(rtag,ctag,TRUE))
2757 error(100); /* error - function prototypes do not match */
2759 error(213); /* warning - tag mismatch */
2761 } while (matchtoken(',')); /* do */
2764 init(ident,&ctag,errorfound);
2765 if (!matchtag(tag,ctag,TRUE))
2766 error(213); /* tagname mismatch */
2768 /* fill up the literal queue with a series */
2770 cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2;
2771 if (size==0 || (litidx-curlit)==0)
2772 error(41); /* invalid ellipsis, array size unknown */
2773 else if ((litidx-curlit)==(int)size)
2774 error(18); /* initialisation data exceeds declared size */
2775 while ((litidx-curlit)<(int)size) {
2780 if (fillzero && size>0) {
2781 while ((litidx-curlit)<(int)size)
2785 size=litidx-curlit; /* number of elements defined */
2786 } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */
2787 error(18); /* initialisation data exceeds declared size */
2788 litidx=(int)size+curlit; /* avoid overflow in memory moves */
2795 * Evaluate one initializer.
2797 static cell init(int ident,int *tag,int *errorfound)
2801 if (matchtoken(tSTRING)){
2802 /* lex() automatically stores strings in the literal table (and
2803 * increases "litidx") */
2804 if (ident==iVARIABLE) {
2805 error(6); /* must be assigned to an array */
2806 litidx=1; /* reset literal queue */
2809 } else if (constexpr(&i,tag,NULL)){
2810 litadd(i); /* store expression result in literal table */
2812 if (errorfound!=NULL)
2820 * Get required array size
2822 static cell needsub(int *tag,constvalue **enumroot)
2830 *enumroot=NULL; /* preset */
2831 if (matchtoken(']')) /* we have already seen "[" */
2832 return 0; /* zero size (like "char msg[]") */
2834 constexpr(&val,tag,&sym); /* get value (must be constant expression) */
2836 error(9); /* negative array size is invalid; assumed zero */
2841 if (enumroot!=NULL) {
2842 /* get the field list for an enumeration */
2843 assert(*enumroot==NULL);/* should have been preset */
2844 assert(sym==NULL || sym->ident==iCONSTEXPR);
2845 if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) {
2846 assert(sym->dim.enumlist!=NULL);
2847 *enumroot=sym->dim.enumlist;
2851 return val; /* return array size */
2854 /* decl_const - declare a single constant
2857 static void decl_const(int vclass)
2859 char constname[sNAMEMAX+1];
2866 insert_docstring_separator(); /* see comment in newfunc() */
2868 tag=pc_addtag(NULL);
2869 if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */
2870 error(20,str); /* invalid symbol name */
2871 symbolline=fline; /* save line where symbol was found */
2872 strcpy(constname,str); /* save symbol name */
2874 constexpr(&val,&exprtag,NULL); /* get value */
2875 /* add_constant() checks for duplicate definitions */
2876 if (!matchtag(tag,exprtag,FALSE)) {
2877 /* temporarily reset the line number to where the symbol was defined */
2880 error(213); /* tagname mismatch */
2883 sym=add_constant(constname,val,vclass,tag);
2885 sc_attachdocumentation(sym);/* attach any documenation to the constant */
2886 } while (matchtoken(',')); /* enddo */ /* more? */
2891 * declstruct - declare a struct type
2893 static void declstruct(void)
2901 /* get the explicit tag (required!) */
2902 tok = lex(&val,&str);
2908 if (pstructs_find(str) != NULL)
2913 pstruct = pstructs_add(str);
2919 if (matchtoken('}'))
2925 memset(&arg, 0, sizeof(structarg_t));
2926 tok = lex(&val,&str);
2930 tok = lex(&val,&str);
2935 arg.ident = iREFERENCE;
2936 tok = lex(&val,&str);
2940 arg.tag = pc_addtag(str);
2941 tok = lex(&val,&str);
2945 error(1, "-identifier-", str);
2948 strcpy(arg.name, str);
2949 if (matchtoken('['))
2951 if (arg.ident == iREFERENCE)
2953 error(67, arg.name);
2958 constvalue *enumroot;
2960 if (arg.dimcount == sDIMEN_MAX)
2965 size = needsub(&ignore_tag, &enumroot);
2966 arg.dims[arg.dimcount++] = size;
2967 } while (matchtoken('['));
2968 /* Handle strings */
2969 if (arg.tag == pc_tag_string && arg.dims[arg.dimcount-1])
2971 arg.dims[arg.dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
2973 if (arg.dimcount == 1 && !arg.dims[arg.dimcount-1])
2975 arg.ident = iREFARRAY;
2977 } else if (!arg.ident) {
2978 arg.ident = iVARIABLE;
2980 if (pstructs_addarg(pstruct, &arg) == NULL)
2982 error(99, arg.name, pstruct->name);
2984 } while (matchtoken(','));
2986 matchtoken(';'); /* eat up optional semicolon */
2991 * dofuncenum - declare function enumerations
2993 static void dofuncenum(int listmode)
2998 char tagname[sNAMEMAX+1];
3000 funcenum_t *fenum = NULL;
3003 /* get the explicit tag (required!) */
3004 int l = lex(&val,&str);
3010 /* This tag can't already exist! */
3011 cur=tagname_tab.next;
3014 if (strcmp(cur->name, str) == 0)
3016 /* Another bad one... */
3017 if (!(cur->value & FUNCTAG))
3025 strcpy(tagname, str);
3027 fenum = funcenums_add(tagname);
3036 if (listmode && matchtoken('}'))
3042 memset(&func, 0, sizeof(func));
3043 func.ret_tag = pc_addtag(NULL); /* Get the return tag */
3044 l = lex(&val, &str);
3046 * Right now, there is a problem. We can't pass non-public function pointer addresses around,
3047 * because the address isn't known until the final reparse. Unfortunately, you can write code
3048 * before the address is known, because Pawn's compiler isn't truly multipass.
3050 * When you call a function, there's no problem, because it does not write the address.
3051 * The assembly looks like this:
3053 * Then, only at assembly time (once all passes are done), does it know the address.
3054 * I do not see any solution to this because there is no way I know to inject the function name
3055 * rather than the constant value. And even if we could, we'd have to change the assembler recognize that.
3060 } else */if (l == tPUBLIC) {
3061 func.type = uPUBLIC;
3063 error(1, "-public-", str);
3068 funcarg_t *arg = &(func.args[func.argcount]);
3071 if (matchtoken(')'))
3076 l = lex(&val, &str);
3079 if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
3081 error(1, "-identifier-", "&");
3083 arg->ident = iREFERENCE;
3084 } else if (l == tCONST) {
3085 if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
3087 error(1, "-identifier-", "const");
3090 } else if (l == tLABEL) {
3091 if (arg->tagcount > 0)
3093 error(1, "-identifier-", "-tagname-");
3095 arg->tags[arg->tagcount++] = pc_addtag(str);
3097 while (arg->tagcount < sTAGS_MAX)
3099 if (!matchtoken('_') && !needtoken(tSYMBOL))
3103 tokeninfo(&val, &ptr);
3104 arg->tags[arg->tagcount++] = pc_addtag(ptr);
3105 if (matchtoken('}'))
3114 } else if (l == tSYMBOL) {
3115 if (func.argcount >= sARGS_MAX)
3119 if (str[0] == PUBLIC_CHAR)
3123 if (matchtoken('['))
3126 if (arg->ident == iREFERENCE)
3132 constvalue *enumroot;
3134 if (arg->dimcount == sDIMEN_MAX)
3139 size = needsub(&ignore_tag, &enumroot);
3140 arg->dims[arg->dimcount] = size;
3142 } while (matchtoken('['));
3143 /* Handle strings */
3144 if ((arg->tagcount == 1 && arg->tags[0] == pc_tag_string)
3145 && arg->dims[arg->dimcount-1])
3147 arg->dims[arg->dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
3149 arg->ident=iREFARRAY;
3150 } else if (arg->ident == 0) {
3151 arg->ident = iVARIABLE;
3154 if (matchtoken('='))
3157 arg->ommittable = TRUE;
3158 func.ommittable = TRUE;
3159 } else if (func.ommittable) {
3163 } else if (l == tELLIPS) {
3164 if (arg->ident == iVARIABLE)
3168 arg->ident = iVARARGS;
3173 } while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(','));
3175 for (i=0; i<func.argcount; i++)
3177 if (func.args[i].tagcount == 0)
3179 func.args[i].tags[0] = 0;
3180 func.args[i].tagcount = 1;
3183 functags_add(fenum, &func);
3188 } while (matchtoken(','));
3193 matchtoken(';'); /* eat an optional semicolon. nom nom nom */
3194 errorset(sRESET, 0);
3197 /* decl_enum - declare enumerated constants
3200 static void decl_enum(int vclass)
3202 char enumname[sNAMEMAX+1],constname[sNAMEMAX+1];
3203 cell val,value,size;
3205 int tag,explicittag;
3206 cell increment,multiplier;
3207 constvalue *enumroot;
3210 /* get an explicit tag, if any (we need to remember whether an explicit
3211 * tag was passed, even if that explicit tag was "_:", so we cannot call
3214 if (lex(&val,&str)==tLABEL) {
3223 /* get optional enum name (also serves as a tag if no explicit tag was set) */
3224 if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */
3225 strcpy(enumname,str); /* save enum name (last constant) */
3227 tag=pc_addtag(enumname);
3229 lexpush(); /* analyze again */
3233 /* get increment and multiplier */
3236 if (matchtoken('(')) {
3237 if (matchtoken(taADD)) {
3238 constexpr(&increment,NULL,NULL);
3239 } else if (matchtoken(taMULT)) {
3240 constexpr(&multiplier,NULL,NULL);
3241 } else if (matchtoken(taSHL)) {
3242 constexpr(&val,NULL,NULL);
3249 if (strlen(enumname)>0) {
3250 /* already create the root symbol, so the fields can have it as their "parent" */
3251 enumsym=add_constant(enumname,0,vclass,tag);
3253 enumsym->usage |= uENUMROOT;
3254 /* start a new list for the element names */
3255 if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL)
3256 error(123); /* insufficient memory (fatal error) */
3257 memset(enumroot,0,sizeof(constvalue));
3264 /* go through all constants */
3265 value=0; /* default starting value */
3267 int idxtag,fieldtag;
3269 if (matchtoken('}')) { /* quick exit if '}' follows ',' */
3273 idxtag=pc_addtag(NULL); /* optional explicit item tag */
3274 if (needtoken(tSYMBOL)) { /* read in (new) token */
3275 tokeninfo(&val,&str); /* get the information */
3276 strcpy(constname,str); /* save symbol name */
3280 size=increment; /* default increment of 'val' */
3281 fieldtag=0; /* default field tag */
3282 if (matchtoken('[')) {
3283 constexpr(&size,&fieldtag,NULL); /* get size */
3286 /* :TODO: do we need a size modifier here for pc_tag_string? */
3287 if (matchtoken('='))
3288 constexpr(&value,NULL,NULL); /* get value */
3289 /* add_constant() checks whether a variable (global or local) or
3290 * a constant with the same name already exists
3292 sym=add_constant(constname,value,vclass,tag);
3294 continue; /* error message already given */
3295 /* set the item tag and the item size, for use in indexing arrays */
3296 sym->x.tags.index=idxtag;
3297 sym->x.tags.field=fieldtag;
3298 sym->dim.array.length=size;
3299 sym->dim.array.level=0;
3300 sym->dim.array.slength=0;
3301 sym->parent=enumsym;
3302 /* add the constant to a separate list as well */
3303 if (enumroot!=NULL) {
3304 sym->usage |= uENUMFIELD;
3305 append_constval(enumroot,constname,value,tag);
3310 value*=size*multiplier;
3311 } while (matchtoken(','));
3312 needtoken('}'); /* terminates the constant list */
3313 matchtoken(';'); /* eat an optional ; */
3315 /* set the enum name to the "next" value (typically the last value plus one) */
3316 if (enumsym!=NULL) {
3317 assert((enumsym->usage & uENUMROOT)!=0);
3318 enumsym->addr=value;
3319 /* assign the constant list */
3320 assert(enumroot!=NULL);
3321 enumsym->dim.enumlist=enumroot;
3322 sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */
3326 static int getstates(const char *funcname)
3328 char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1];
3331 constvalue *automaton;
3335 int count,listsize,state_id;
3337 if (!matchtoken('<'))
3339 if (matchtoken('>'))
3340 return -1; /* special construct: all other states (fall-back) */
3348 if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
3350 tokeninfo(&val,&str);
3351 assert(strlen(str)<sizeof fsaname);
3352 strcpy(fsaname,str); /* assume this is the name of the automaton */
3353 if (islabel || matchtoken(':')) {
3354 /* token is an automaton name, add the name and get a new token */
3355 if (!needtoken(tSYMBOL))
3357 tokeninfo(&val,&str);
3358 assert(strlen(str)<sizeof statename);
3359 strcpy(statename,str);
3361 /* the token was the state name (part of an anynymous automaton) */
3362 assert(strlen(fsaname)<sizeof statename);
3363 strcpy(statename,fsaname);
3366 if (fsa<0 || fsaname[0]!='\0') {
3367 automaton=automaton_add(fsaname);
3368 assert(automaton!=NULL);
3369 if (fsa>=0 && automaton->index!=fsa)
3370 error(83,funcname); /* multiple automatons for a single function/variable */
3371 fsa=automaton->index;
3373 state=state_add(statename,fsa);
3374 /* add this state to the state combination list (it will be attached to the
3375 * automaton later) */
3376 state_buildlist(&list,&listsize,&count,(int)state->value);
3377 } while (matchtoken(','));
3381 assert(automaton!=NULL);
3383 state_id=state_addlist(list,count,fsa);
3386 /* error is already given */
3395 static void attachstatelist(symbol *sym, int state_id)
3398 if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0))
3399 error(21,sym->name); /* function already defined, either without states or the current definition has no states */
3402 /* add the state list id */
3403 constvalue *stateptr;
3404 if (sym->states==NULL) {
3405 if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL)
3406 error(123); /* insufficient memory (fatal error) */
3407 memset(sym->states,0,sizeof(constvalue));
3409 /* see whether the id already exists (add new state only if it does not
3412 assert(sym->states!=NULL);
3413 for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next)
3415 assert(state_id<=SHRT_MAX);
3417 append_constval(sym->states,"",code_idx,(short)state_id);
3418 else if (stateptr->value==0)
3419 stateptr->value=code_idx;
3421 error(84,sym->name);
3422 /* also check for another conflicting situation: a fallback function
3423 * without any states
3425 if (state_id==-1 && sc_status!=statFIRST) {
3426 /* in the second round, all states should have been accumulated */
3427 assert(sym->states!=NULL);
3428 for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next)
3431 error(85,sym->name); /* no states are defined for this function */
3437 * Finds a function in the global symbol table or creates a new entry.
3438 * It does some basic processing and error checking.
3440 SC_FUNC symbol *fetchfunc(char *name,int tag)
3444 if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */
3445 if (sym->ident!=iFUNCTN) {
3446 error(21,name); /* yes, but not as a function */
3447 return NULL; /* make sure the old symbol is not damaged */
3448 } else if ((sym->usage & uNATIVE)!=0) {
3449 error(21,name); /* yes, and it is a native */
3451 assert(sym->vclass==sGLOBAL);
3452 if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag)
3453 error(25); /* mismatch from earlier prototype */
3454 if ((sym->usage & uDEFINE)==0) {
3455 /* as long as the function stays undefined, update the address and the tag */
3456 if (sym->states==NULL)
3461 /* don't set the "uDEFINE" flag; it may be a prototype */
3462 sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0);
3463 assert(sym!=NULL); /* fatal error 103 must be given on error */
3464 /* assume no arguments */
3465 sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo));
3466 sym->dim.arglist[0].ident=0;
3467 /* set library ID to NULL (only for native functions) */
3469 /* set the required stack size to zero (only for non-native functions) */
3470 sym->x.stacksize=1; /* 1 for PROC opcode */
3472 if (pc_deprecate!=NULL) {
3474 sym->flags|=flgDEPRECATED;
3475 if (sc_status==statWRITE) {
3476 if (sym->documentation!=NULL) {
3477 free(sym->documentation);
3478 sym->documentation=NULL;
3480 sym->documentation=pc_deprecate;
3490 /* This routine adds symbolic information for each argument.
3492 static void define_args(void)
3496 /* At this point, no local variables have been declared. All
3497 * local symbols are function arguments.
3501 assert(sym->ident!=iLABEL);
3502 assert(sym->vclass==sLOCAL);
3503 markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */
3508 static int operatorname(char *name)
3516 /* check the operator */
3517 opertok=lex(&val,&str);
3529 name[0]=(char)opertok;
3552 error(7); /* operator cannot be redefined (or bad operator name) */
3559 static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag)
3564 char tmpname[sNAMEMAX+1];
3570 assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL);
3571 /* count arguments and save (first two) tags */
3572 while (arg=&sym->dim.arglist[count], arg->ident!=0) {
3575 error(65,count+1); /* function argument may only have a single tag */
3576 else if (arg->numtags==1)
3577 tags[count]=arg->tags[0];
3579 if (opertok=='~' && count==0) {
3580 if (arg->ident!=iREFARRAY)
3581 error(73,arg->name);/* must be an array argument */
3583 //if (arg->ident!=iVARIABLE)
3584 //error(66,arg->name);/* must be non-reference argument */
3586 if (arg->hasdefault)
3587 error(59,arg->name); /* arguments of an operator may not have a default value */
3591 /* for '!', '++' and '--', count must be 1
3592 * for '-', count may be 1 or 2
3593 * for '=', count must be 1, and the resulttag is also important
3594 * for all other (binary) operators and the special '~' operator, count must be 2
3602 error(62); /* number or placement of the operands does not fit the operator */
3605 if (count!=1 && count!=2)
3606 error(62); /* number or placement of the operands does not fit the operator */
3610 error(62); /* number or placement of the operands does not fit the operator */
3613 if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0))
3614 error(64); /* cannot change predefined operators */
3616 /* change the operator name */
3617 assert(strlen(opername)>0);
3618 operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag);
3619 if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) {
3621 if ((oldsym->usage & uDEFINE)!=0) {
3622 char errname[2*sNAMEMAX+16];
3623 funcdisplayname(errname,tmpname);
3624 error(21,errname); /* symbol already defined */
3626 sym->usage|=oldsym->usage; /* copy flags from the previous definition */
3627 for (i=0; i<oldsym->numrefers; i++)
3628 if (oldsym->refer[i]!=NULL)
3629 refer_symbol(sym,oldsym->refer[i]);
3630 delete_symbol(&glbtab,oldsym);
3632 strcpy(sym->name,tmpname);
3633 sym->hash=namehash(sym->name);/* calculate new hash */
3635 /* operators should return a value, except the '~' operator */
3637 sym->usage |= uRETVALUE;
3642 static int check_operatortag(int opertok,int resulttag,char *opername)
3644 assert(opername!=NULL && strlen(opername)>0);
3653 if (resulttag!=pc_addtag("bool")) {
3654 error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */
3660 error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */
3668 static char *tag2str(char *dest,int tag)
3672 sprintf(dest,"0%x",tag);
3673 return isdigit(dest[1]) ? &dest[1] : dest;
3676 SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag)
3678 char tagstr1[10], tagstr2[10];
3681 assert(numtags>=1 && numtags<=2);
3682 opertok= (opername[1]=='\0') ? opername[0] : 0;
3684 sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1));
3685 else if (numtags==1 || opertok=='~')
3686 sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1));
3688 sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2));
3692 static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname)
3697 /* tags are only positive, so if the function name starts with a '-',
3698 * the operator is an unary '-' or '--' operator.
3705 *tag1=(int)strtol(fname,&ptr,16);
3706 unary= ptr==fname; /* unary operator if it doesn't start with a tag name */
3708 assert(!unary || *tag1==0);
3710 for (name=opname; !isdigit(*ptr); )
3713 *tag2=(int)strtol(ptr,NULL,16);
3717 constvalue *find_tag_byval(int tag)
3720 tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG);
3722 tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);
3726 SC_FUNC char *funcdisplayname(char *dest,char *funcname)
3730 constvalue *tagsym[2];
3733 if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') {
3735 strcpy(dest,funcname);
3739 unary=parse_funcname(funcname,&tags[0],&tags[1],opname);
3740 tagsym[1]=find_tag_byval(tags[1]);
3741 assert(tagsym[1]!=NULL);
3743 sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name);
3745 tagsym[0]=find_tag_byval(tags[0]);
3746 assert(tagsym[0]!=NULL);
3747 /* special case: the assignment operator has the return value as the 2nd tag */
3748 if (opname[0]=='=' && opname[1]=='\0')
3749 sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name);
3751 sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name);
3756 static void funcstub(int fnative)
3758 int tok,tag,fpublic;
3761 char symbolname[sNAMEMAX+1];
3762 int idxtag[sDIMEN_MAX];
3763 int dim[sDIMEN_MAX];
3770 litidx=0; /* clear the literal pool */
3771 assert(loctab.next==NULL); /* local symbol table should be empty */
3773 tag=pc_addtag(NULL); /* get the tag of the return value */
3775 while (matchtoken('[')) {
3776 /* the function returns an array, get this tag for the index and the array
3779 if (numdim == sDIMEN_MAX) {
3780 error(53); /* exceeding maximum number of dimensions */
3783 size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
3785 error(9); /* invalid array size */
3786 #if INT_MAX < LONG_MAX
3788 error(125); /* overflow, exceeding capacity */
3790 dim[numdim++]=(int)size;
3793 if (tag == pc_tag_string && numdim && dim[numdim-1])
3794 dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
3797 fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR);
3799 if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR)
3800 error(42); /* invalid combination of class specifiers */
3802 if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC)
3806 if (tok==tOPERATOR) {
3807 opertok=operatorname(symbolname);
3809 return; /* error message already given */
3810 check_operatortag(opertok,tag,symbolname);
3812 if (tok!=tSYMBOL && freading) {
3813 error(10); /* illegal function or declaration */
3816 strcpy(symbolname,str);
3818 needtoken('('); /* only functions may be native/forward */
3820 sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
3824 sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
3825 sym->x.lib=curlibrary;
3826 } else if (fpublic) {
3827 sym->usage|=uPUBLIC;
3829 sym->usage|=uFORWARD;
3831 declargs(sym,FALSE);
3832 /* "declargs()" found the ")" */
3833 sc_attachdocumentation(sym); /* attach any documenation to the function */
3834 if (!operatoradjust(opertok,sym,symbolname,tag))
3835 sym->usage &= ~uDEFINE;
3837 if (getstates(symbolname)!=0) {
3838 if (fnative || opertok!=0)
3839 error(82); /* native functions and operators may not have states */
3841 error(231); /* ignoring state specifications on forward declarations */
3844 /* for a native operator, also need to specify an "exported" function name;
3845 * for a native function, this is optional
3849 if (matchtoken('['))
3854 lexpush(); /* push back, for matchtoken() to retrieve again */
3856 if (matchtoken('=')) {
3857 /* allow number or symbol */
3858 if (matchtoken(tSYMBOL)) {
3859 tokeninfo(&val,&str);
3860 insert_alias(sym->name,str);
3862 constexpr(&val,NULL,NULL);
3864 /* At the moment, I have assumed that this syntax is only valid if
3865 * val < 0. To properly mix "normal" native functions and indexed
3866 * native functions, one should use negative indices anyway.
3867 * Special code for a negative index in sym->addr exists in SC4.C
3868 * (ffcall()) and in SC6.C (the loops for counting the number of native
3869 * variables and for writing them).
3876 /* attach the array to the function symbol */
3879 sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag);
3883 litidx=0; /* clear the literal pool */
3884 delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
3887 /* newfunc - begin a function
3889 * This routine is called from "parse" and tries to make a function
3890 * out of the following text
3892 * Global references: funcstatus,lastst,litidx
3895 * declared (altered)
3896 * glb_declared (altered)
3897 * sc_alignnext (altered)
3899 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock)
3902 int argcnt,tok,tag,funcline;
3903 int opertok,opererror;
3904 char symbolname[sNAMEMAX+1];
3906 cell val,cidx,glbdecl;
3910 assert(litidx==0); /* literal queue should be empty */
3911 litidx=0; /* clear the literal pool (should already be empty) */
3913 lastst=0; /* no statement yet */
3914 cidx=0; /* just to avoid compiler warnings */
3916 assert(loctab.next==NULL); /* local symbol table should be empty */
3917 filenum=fcurrent; /* save file number at the start of the declaration */
3919 if (firstname!=NULL) {
3920 assert(strlen(firstname)<=sNAMEMAX);
3921 strcpy(symbolname,firstname); /* save symbol name */
3924 tag= (firsttag>=0) ? firsttag : pc_addtag(NULL);
3927 if (tok==tNATIVE || tok==tPUBLIC && stock)
3928 error(42); /* invalid combination of class specifiers */
3929 if (tok==tOPERATOR) {
3930 opertok=operatorname(symbolname);
3932 return TRUE; /* error message already given */
3933 check_operatortag(opertok,tag,symbolname);
3935 if (tok!=tSYMBOL && freading) {
3936 error(20,str); /* invalid symbol name */
3939 assert(strlen(str)<=sNAMEMAX);
3940 strcpy(symbolname,str);
3943 /* check whether this is a function or a variable declaration */
3944 if (!matchtoken('('))
3946 /* so it is a function, proceed */
3947 funcline=fline; /* save line at which the function is defined */
3948 if (symbolname[0]==PUBLIC_CHAR) {
3949 fpublic=TRUE; /* implicitly public function */
3951 error(42); /* invalid combination of class specifiers */
3953 sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
3954 if (sym==NULL || (sym->usage & uNATIVE)!=0)
3955 return TRUE; /* it was recognized as a function declaration, but not as a valid one */
3957 sym->usage|=uPUBLIC;
3959 sym->fnumber=filenum;
3960 /* if the function was used before being declared, and it has a tag for the
3961 * result, add a third pass (as second "skimming" parse) because the function
3962 * result may have been used with user-defined operators, which have now
3963 * been incorrectly flagged (as the return tag was unknown at the time of
3966 if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) {
3967 int curstatus=sc_status;
3968 sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */
3969 #if 0 /* SourceMod - silly, should be removed in first pass, so removed */
3972 sc_status=curstatus;
3973 sc_reparse=TRUE; /* must add another pass to "initial scan" phase */
3975 #if 0 /* Not used for SourceMod */
3976 /* we want public functions to be explicitly prototyped, as they are called
3979 if (fpublic && (sym->usage & uFORWARD)==0)
3980 error(235,symbolname);
3982 /* declare all arguments */
3983 argcnt=declargs(sym,TRUE);
3984 opererror=!operatoradjust(opertok,sym,symbolname,tag);
3985 if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) {
3987 error(5); /* "main()" and "entry()" functions may not have any arguments */
3988 sym->usage|=uREAD; /* "main()" is the program's entry point: always used */
3990 state_id=getstates(symbolname);
3991 if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0))
3992 error(82); /* operators may not have states, main() may neither */
3993 attachstatelist(sym,state_id);
3994 /* "declargs()" found the ")"; if a ";" appears after this, it was a
3996 if (matchtoken(';')) {
3997 sym->usage|=uFORWARD;
3998 if (!sc_needsemicolon)
3999 error(10); /* old style prototypes used with optional semicolumns */
4000 delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */
4003 /* so it is not a prototype, proceed */
4004 /* if this is a function that is not referred to (this can only be detected
4005 * in the second stage), shut code generation off */
4006 if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) {
4009 glbdecl=glb_declared;
4011 if ((sym->flags & flgDEPRECATED)!=0) {
4012 char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
4013 error(234,symbolname,ptr); /* deprecated (probably a public function) */
4016 sym->usage|=uDEFINE; /* set the definition flag */
4019 if (opertok!=0 && opererror)
4020 sym->usage &= ~uDEFINE;
4021 /* if the function has states, dump the label to the start of the function */
4023 constvalue *ptr=sym->states->next;
4025 assert(sc_status!=statWRITE || strlen(ptr->name)>0);
4026 if (ptr->index==state_id) {
4027 setlabel((int)strtol(ptr->name,NULL,16));
4033 startfunc(sym->name); /* creates stack frame */
4034 insert_dbgline(funcline);
4037 alignframe(sc_dataalign);
4040 declared=0; /* number of local cells */
4043 rettype=(sym->usage & uRETVALUE); /* set "return type" variable */
4045 define_args(); /* add the symbolic info for the function arguments */
4046 #if !defined SC_LIGHT
4047 if (matchtoken('{')) {
4050 /* Insert a separator so that comments following the statement will not
4051 * be attached to this function; they should be attached to the next
4052 * function. This is not a problem for functions having a compound block,
4053 * because the closing brace is an explicit "end token" for the function.
4054 * With single statement functions, the preprocessor may overread the
4055 * source code before the parser determines an "end of statement".
4057 insert_docstring_separator();
4060 sc_curstates=state_id;/* set state id, for accessing global state variables */
4061 statement(NULL,FALSE);
4063 if ((rettype & uRETVALUE)!=0)
4064 sym->usage|=uRETVALUE;
4066 /* This happens only in a very special (and useless) case, where a function
4067 * has only a single statement in its body (no compound block) and that
4068 * statement declares a new variable
4073 if ((lastst!=tRETURN) && (lastst!=tGOTO)){
4075 ffret(strcmp(sym->name,uENTRYFUNC)!=0);
4076 if ((sym->usage & uRETVALUE)!=0) {
4077 char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
4078 funcdisplayname(symname,sym->name);
4079 error(209,symname); /* function should return a value */
4083 sym->codeaddr=code_idx;
4084 sc_attachdocumentation(sym); /* attach collected documenation to the function */
4085 if (litidx) { /* if there are literals defined */
4086 glb_declared+=litidx;
4087 begdseg(); /* flip to DATA segment */
4088 dumplits(); /* dump literal strings */
4091 testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */
4092 delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */
4093 assert(loctab.next==NULL);
4095 if (sc_status==statSKIP) {
4096 sc_status=statWRITE;
4098 glb_declared=glbdecl;
4103 static int argcompare(arginfo *a1,arginfo *a2)
4107 #if 0 /* SourceMod uses case insensitive args for forwards */
4108 result= strcmp(a1->name,a2->name)==0; /* name */
4113 result= a1->ident==a2->ident; /* type/class */
4115 result= a1->usage==a2->usage; /* "const" flag */
4117 result= a1->numtags==a2->numtags; /* tags (number and names) */
4118 for (i=0; result && i<a1->numtags; i++)
4119 result= a1->tags[i]==a2->tags[i];
4121 result= a1->numdim==a2->numdim; /* array dimensions & index tags */
4122 for (level=0; result && level<a1->numdim; level++)
4123 result= a1->dim[level]==a2->dim[level];
4124 for (level=0; result && level<a1->numdim; level++)
4125 result= a1->idxtag[level]==a2->idxtag[level];
4127 result= a1->hasdefault==a2->hasdefault; /* availability of default value */
4128 if (a1->hasdefault) {
4129 if (a1->ident==iREFARRAY) {
4131 result= a1->defvalue.array.size==a2->defvalue.array.size;
4133 result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize;
4134 /* ??? should also check contents of the default array (these troubles
4135 * go away in a 2-pass compiler that forbids double declarations, but
4136 * Pawn currently does not forbid them) */
4139 if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0 || (a1->hasdefault & uCOUNTOF)!=0)
4140 result= a1->hasdefault==a2->hasdefault
4141 && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0
4142 && a1->defvalue.size.level==a2->defvalue.size.level;
4144 result= a1->defvalue.val==a2->defvalue.val;
4148 result= a1->defvalue_tag==a2->defvalue_tag;
4155 * This routine adds an entry in the local symbol table for each argument
4156 * found in the argument list. It returns the number of arguments.
4158 static int declargs(symbol *sym,int chkshadow)
4162 int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags;
4164 arginfo arg, *arglist;
4165 char name[sNAMEMAX+1];
4166 int ident,fpublic,fconst;
4169 /* if the function is already defined earlier, get the number of arguments
4170 * of the existing definition
4173 if ((sym->usage & uPROTOTYPED)!=0)
4174 while (sym->dim.arglist[oldargcnt].ident!=0)
4176 argcnt=0; /* zero aruments up to now */
4180 fpublic= (sym->usage & (uPUBLIC|uSTOCK))!=0;
4181 /* the '(' parantheses has already been parsed */
4182 if (!matchtoken(')')){
4183 do { /* there are arguments; process them */
4184 /* any legal name increases argument count (and stack offset) */
4191 if (ident!=iVARIABLE || numtags>0)
4192 error(1,"-identifier-","&");
4196 if (ident!=iVARIABLE || numtags>0)
4197 error(1,"-identifier-","const");
4202 error(1,"-identifier-","-tagname-");
4203 tags[0]=pc_addtag(ptr);
4208 error(1,"-identifier-","-tagname-");
4210 while (numtags<MAXTAGS) {
4211 if (!matchtoken('_') && !needtoken(tSYMBOL))
4213 tokeninfo(&val,&ptr);
4214 tags[numtags++]=pc_addtag(ptr);
4215 if (matchtoken('}'))
4220 tok=tLABEL; /* for outer loop: flag that we have seen a tagname */
4223 if (argcnt>=sMAXARGS)
4224 error(45); /* too many function arguments */
4225 strcpy(name,ptr); /* save symbol name */
4226 if (name[0]==PUBLIC_CHAR)
4227 error(56,name); /* function arguments cannot be public */
4229 tags[numtags++]=0; /* default tag */
4231 * base + 0*sizeof(cell) == previous "base"
4232 * base + 1*sizeof(cell) == function return address
4233 * base + 2*sizeof(cell) == number of arguments
4234 * base + 3*sizeof(cell) == first argument of the function
4235 * So the offset of each argument is "(argcnt+3) * sizeof(cell)".
4237 doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg);
4238 /* :TODO: fix this so stocks that are func pointers can't have default arguments? */
4239 if ((sym->usage & uPUBLIC) && arg.hasdefault)
4240 error(59,name); /* arguments of a public function may not have a default value */
4241 if ((sym->usage & uPROTOTYPED)==0) {
4242 /* redimension the argument list, add the entry */
4243 sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
4244 if (sym->dim.arglist==0)
4245 error(123); /* insufficient memory */
4246 memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
4247 sym->dim.arglist[argcnt]=arg;
4249 /* check the argument with the earlier definition */
4250 if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg))
4251 error(25); /* function definition does not match prototype */
4252 /* may need to free default array argument and the tag list */
4253 if (arg.ident==iREFARRAY && arg.hasdefault)
4254 free(arg.defvalue.array.data);
4255 else if (arg.ident==iVARIABLE
4256 && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0) || (arg.hasdefault & uCOUNTOF)!=0)
4257 free(arg.defvalue.size.symname);
4266 if (ident!=iVARIABLE)
4267 error(10); /* illegal function or declaration */
4269 tags[numtags++]=0; /* default tag */
4270 if ((sym->usage & uPROTOTYPED)==0) {
4271 /* redimension the argument list, add the entry iVARARGS */
4272 sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
4273 if (sym->dim.arglist==0)
4274 error(123); /* insufficient memory */
4275 memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */
4276 sym->dim.arglist[argcnt].ident=iVARARGS;
4277 sym->dim.arglist[argcnt].hasdefault=FALSE;
4278 sym->dim.arglist[argcnt].defvalue.val=0;
4279 sym->dim.arglist[argcnt].defvalue_tag=0;
4280 sym->dim.arglist[argcnt].numtags=numtags;
4281 sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]);
4282 if (sym->dim.arglist[argcnt].tags==NULL)
4283 error(123); /* insufficient memory */
4284 memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]);
4286 if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS)
4287 error(25); /* function definition does not match prototype */
4292 error(10); /* illegal function or declaration */
4294 } while (tok=='&' || tok==tLABEL || tok==tCONST
4295 || tok!=tELLIPS && matchtoken(',')); /* more? */
4296 /* if the next token is not ",", it should be ")" */
4299 /* resolve any "sizeof" arguments (now that all arguments are known) */
4300 assert(sym->dim.arglist!=NULL);
4301 arglist=sym->dim.arglist;
4302 for (idx=0; idx<argcnt && arglist[idx].ident!=0; idx++) {
4303 if ((arglist[idx].hasdefault & uSIZEOF)!=0
4304 || (arglist[idx].hasdefault & uTAGOF)!=0
4305 || (arglist[idx].hasdefault & uCOUNTOF)!=0) {
4307 /* Find the argument with the name mentioned after the "sizeof". Note
4308 * that we cannot use findloc here because we need the arginfo struct,
4311 ptr=arglist[idx].defvalue.size.symname;
4313 for (altidx=0; altidx<argcnt && strcmp(ptr,arglist[altidx].name)!=0; altidx++)
4315 if (altidx>=argcnt) {
4316 error(17,ptr); /* undefined symbol */
4318 assert(arglist[idx].defvalue.size.symname!=NULL);
4319 /* check the level against the number of dimensions */
4320 if (arglist[idx].defvalue.size.level>0
4321 && arglist[idx].defvalue.size.level>=arglist[altidx].numdim)
4322 error(28,arglist[idx].name); /* invalid subscript */
4323 /* check the type of the argument whose size to take; for a iVARIABLE
4324 * or a iREFERENCE, this is always 1 (so the code is redundant)
4326 assert(arglist[altidx].ident!=iVARARGS);
4327 if (arglist[altidx].ident!=iREFARRAY
4328 && (((arglist[idx].hasdefault & uSIZEOF)!=0)
4329 || (arglist[idx].hasdefault & uCOUNTOF)!=0)) {
4330 if ((arglist[idx].hasdefault & uTAGOF)!=0) {
4331 error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */
4333 assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE);
4334 error(223,ptr); /* redundant sizeof */
4341 sym->usage|=uPROTOTYPED;
4342 errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
4346 /* doarg - declare one argument type
4348 * this routine is called from "declargs()" and adds an entry in the local
4349 * symbol table for one argument.
4351 * "fpublic" indicates whether the function for this argument list is public.
4352 * The arguments themselves are never public.
4354 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
4355 int fpublic,int fconst,int chkshadow,arginfo *arg)
4358 constvalue *enumroot;
4362 strcpy(arg->name,name);
4363 arg->hasdefault=FALSE; /* preset (most common case) */
4364 arg->defvalue.val=0; /* clear */
4365 arg->defvalue_tag=0;
4367 if (matchtoken('[')) {
4368 if (ident==iREFERENCE)
4369 error(67,name); /* illegal declaration ("&name[]" is unsupported) */
4371 if (arg->numdim == sDIMEN_MAX) {
4372 error(53); /* exceeding maximum number of dimensions */
4375 size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */
4376 #if INT_MAX < LONG_MAX
4378 error(125); /* overflow, exceeding capacity */
4380 arg->dim[arg->numdim]=(int)size;
4382 } while (matchtoken('['));
4383 ident=iREFARRAY; /* "reference to array" (is a pointer) */
4384 if (checktag(tags, numtags, pc_tag_string)) {
4385 slength = arg->dim[arg->numdim - 1];
4386 arg->dim[arg->numdim - 1] = (size + sizeof(cell) - 1) / sizeof(cell);
4388 if (matchtoken('=')) {
4389 lexpush(); /* initials() needs the "=" token again */
4390 assert(litidx==0); /* at the start of a function, this is reset */
4392 initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot);
4393 assert(size>=litidx);
4394 /* allocate memory to hold the initial values */
4395 arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell));
4396 if (arg->defvalue.array.data!=NULL) {
4398 memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell));
4399 arg->hasdefault=TRUE; /* argument has default value */
4400 arg->defvalue.array.size=litidx;
4401 arg->defvalue.array.addr=-1;
4402 /* calulate size to reserve on the heap */
4403 arg->defvalue.array.arraysize=1;
4404 for (i=0; i<arg->numdim; i++)
4405 arg->defvalue.array.arraysize*=arg->dim[i];
4406 if (arg->defvalue.array.arraysize < arg->defvalue.array.size)
4407 arg->defvalue.array.arraysize = arg->defvalue.array.size;
4409 litidx=0; /* reset */
4412 if (matchtoken('=')) {
4413 unsigned char size_tag_token;
4414 assert(ident==iVARIABLE || ident==iREFERENCE);
4415 arg->hasdefault=TRUE; /* argument has a default value */
4416 size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0);
4417 if (size_tag_token==0)
4418 size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0);
4419 if (size_tag_token==0)
4420 size_tag_token=(unsigned char)(matchtoken(tCELLSOF) ? uCOUNTOF : 0);
4421 if (size_tag_token!=0) {
4423 if (ident==iREFERENCE)
4424 error(66,name); /* argument may not be a reference */
4426 while (matchtoken('('))
4428 if (needtoken(tSYMBOL)) {
4429 /* save the name of the argument whose size id to take */
4432 tokeninfo(&val,&name);
4433 if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL)
4434 error(123); /* insufficient memory */
4435 arg->defvalue.size.level=0;
4436 if (size_tag_token==uSIZEOF || size_tag_token==uCOUNTOF) {
4437 while (matchtoken('[')) {
4438 arg->defvalue.size.level+=(short)1;
4442 if (ident==iVARIABLE) /* make sure we set this only if not a reference */
4443 arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */
4445 while (paranthese--)
4448 constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL);
4450 if (!matchtag(tags[0],arg->defvalue_tag,TRUE))
4451 error(213); /* tagname mismatch */
4455 arg->ident=(char)ident;
4456 arg->usage=(char)(fconst ? uCONST : 0);
4457 arg->numtags=numtags;
4458 arg->tags=(int*)malloc(numtags*sizeof tags[0]);
4459 if (arg->tags==NULL)
4460 error(123); /* insufficient memory */
4461 memcpy(arg->tags,tags,numtags*sizeof tags[0]);
4462 argsym=findloc(name);
4464 error(21,name); /* symbol already defined */
4466 if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN)
4467 error(219,name); /* variable shadows another symbol */
4468 /* add details of type and address */
4470 argsym=addvariable2(name,offset,ident,sLOCAL,tags[0],
4471 arg->dim,arg->numdim,arg->idxtag,slength);
4473 if (ident==iREFERENCE)
4474 argsym->usage|=uREAD; /* because references are passed back */
4476 argsym->usage|=uREAD; /* arguments of public functions are always "used" */
4478 argsym->usage|=uCONST;
4482 static int count_referrers(symbol *entry)
4487 for (i=0; i<entry->numrefers; i++)
4488 if (entry->refer[i]!=NULL)
4493 #if !defined SC_LIGHT
4494 static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue,
4495 char **outer_start,int *outer_length,
4496 char **inner_start,int *inner_length)
4498 char *ptr,*inner_end;
4499 int xmltag_len,xmlparam_len,xmlvalue_len;
4502 assert(source!=NULL);
4503 assert(xmltag!=NULL);
4504 assert(outer_start!=NULL);
4505 assert(outer_length!=NULL);
4506 assert(inner_start!=NULL);
4507 assert(inner_length!=NULL);
4509 /* both NULL or both non-NULL */
4510 assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL);
4512 xmltag_len=strlen(xmltag);
4513 xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0;
4514 xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0;
4516 /* find an opening '<' */
4517 while ((ptr=strchr(ptr,'<'))!=NULL) {
4518 *outer_start=ptr; /* be optimistic... */
4519 match=FALSE; /* ...and pessimistic at the same time */
4520 ptr++; /* skip '<' */
4521 while (*ptr!='\0' && *ptr<=' ')
4522 ptr++; /* skip white space */
4523 if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
4524 /* xml tag found, optionally check the parameter */
4526 while (*ptr!='\0' && *ptr<=' ')
4527 ptr++; /* skip white space */
4528 if (xmlparam!=NULL) {
4529 if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) {
4531 while (*ptr!='\0' && *ptr<=' ')
4532 ptr++; /* skip white space */
4534 ptr++; /* skip '=' */
4535 while (*ptr!='\0' && *ptr<=' ')
4536 ptr++; /* skip white space */
4537 if (*ptr=='"' || *ptr=='\'')
4538 ptr++; /* skip " or ' */
4539 assert(xmlvalue!=NULL);
4540 if (strncmp(ptr,xmlvalue,xmlvalue_len)==0
4541 && (*(ptr+xmlvalue_len)<=' '
4542 || *(ptr+xmlvalue_len)=='>'
4543 || *(ptr+xmlvalue_len)=='"'
4544 || *(ptr+xmlvalue_len)=='\''))
4545 match=TRUE; /* found it */
4549 match=TRUE; /* don't check the parameter */
4553 /* now find the end of the opening tag */
4554 while (*ptr!='\0' && *ptr!='>')
4558 while (*ptr!='\0' && *ptr<=' ')
4559 ptr++; /* skip white space */
4561 /* find the start of the closing tag (assume no nesting) */
4562 while ((ptr=strchr(ptr,'<'))!=NULL) {
4564 ptr++; /* skip '<' */
4565 while (*ptr!='\0' && *ptr<=' ')
4566 ptr++; /* skip white space */
4569 while (*ptr!='\0' && *ptr<=' ')
4570 ptr++; /* skip white space */
4571 if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
4572 /* find the end of the closing tag */
4573 while (*ptr!='\0' && *ptr!='>')
4577 /* set the lengths of the inner and outer segment */
4578 assert(*inner_start!=NULL);
4579 *inner_length=(int)(inner_end-*inner_start);
4580 assert(*outer_start!=NULL);
4581 *outer_length=(int)(ptr-*outer_start);
4582 break; /* break out of the loop */
4589 return FALSE; /* not found */
4592 static char *xmlencode(char *dest,char *source)
4594 char temp[2*sNAMEMAX+20],*ptr;
4596 /* replace < by < and such; normally, such a symbol occurs at most once in
4597 * a symbol name (e.g. "operator<")
4600 while (*source!='\0') {
4611 strcpy(ptr,"&");
4624 static void make_report(symbol *root,FILE *log,char *sourcefile)
4626 char symname[_MAX_PATH];
4630 constvalue *enumroot;
4633 /* adapt the installation directory */
4634 strcpy(symname,sc_rootpath);
4635 #if DIRSEP_CHAR=='\\'
4636 while ((ptr=strchr(symname,':'))!=NULL)
4638 while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL)
4642 /* the XML header */
4643 fprintf(log,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
4644 fprintf(log,"<?xml-stylesheet href=\"file:///%s/xml/pawndoc.xsl\" type=\"text/xsl\"?>\n",symname);
4645 fprintf(log,"<doc source=\"%s\">\n",sourcefile);
4646 ptr=strrchr(sourcefile,DIRSEP_CHAR);
4651 fprintf(log,"\t<assembly>\n\t\t<name>%s</name>\n\t</assembly>\n",ptr);
4653 /* attach the global documentation, if any */
4654 if (sc_documentation!=NULL) {
4655 fprintf(log,"\n\t<!-- general -->\n");
4656 fprintf(log,"\t<general>\n\t\t");
4657 fputs(sc_documentation,log);
4658 fprintf(log,"\n\t</general>\n\n");
4661 /* use multiple passes to print constants variables and functions in
4664 fprintf(log,"\t<members>\n");
4666 fprintf(log,"\n\t\t<!-- enumerations -->\n");
4667 for (sym=root->next; sym!=NULL; sym=sym->next) {
4668 if (sym->parent!=NULL)
4669 continue; /* hierarchical data type */
4670 assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
4671 || sym->ident==iARRAY || sym->ident==iFUNCTN);
4672 if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0)
4674 if ((sym->usage & uREAD)==0)
4676 fprintf(log,"\t\t<member name=\"T:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
4678 tagsym=find_tag_byval(sym->tag);
4679 assert(tagsym!=NULL);
4680 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4682 /* browse through all fields */
4683 if ((enumroot=sym->dim.enumlist)!=NULL) {
4684 enumroot=enumroot->next; /* skip root */
4685 while (enumroot!=NULL) {
4686 fprintf(log,"\t\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,enumroot->name),enumroot->value);
4687 /* find the constant with this name and get the tag */
4688 ref=findglb(enumroot->name,sGLOBAL);
4690 if (ref->x.tags.index!=0) {
4691 tagsym=find_tag_byval(ref->x.tags.index);
4692 assert(tagsym!=NULL);
4693 fprintf(log,"\t\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4695 if (ref->dim.array.length!=1)
4696 fprintf(log,"\t\t\t\t<size value=\"%ld\"/>\n",(long)ref->dim.array.length);
4698 fprintf(log,"\t\t\t</member>\n");
4699 enumroot=enumroot->next;
4702 assert(sym->refer!=NULL);
4703 for (i=0; i<sym->numrefers; i++) {
4704 if ((ref=sym->refer[i])!=NULL)
4705 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4707 if (sym->documentation!=NULL)
4708 fprintf(log,"\t\t\t%s\n",sym->documentation);
4709 fprintf(log,"\t\t</member>\n");
4712 fprintf(log,"\n\t\t<!-- constants -->\n");
4713 for (sym=root->next; sym!=NULL; sym=sym->next) {
4714 if (sym->parent!=NULL)
4715 continue; /* hierarchical data type */
4716 assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
4717 || sym->ident==iARRAY || sym->ident==iFUNCTN);
4718 if (sym->ident!=iCONSTEXPR)
4720 if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0)
4722 fprintf(log,"\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
4724 tagsym=find_tag_byval(sym->tag);
4725 assert(tagsym!=NULL);
4726 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4728 assert(sym->refer!=NULL);
4729 for (i=0; i<sym->numrefers; i++) {
4730 if ((ref=sym->refer[i])!=NULL)
4731 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4733 if (sym->documentation!=NULL)
4734 fprintf(log,"\t\t\t%s\n",sym->documentation);
4735 fprintf(log,"\t\t</member>\n");
4738 fprintf(log,"\n\t\t<!-- variables -->\n");
4739 for (sym=root->next; sym!=NULL; sym=sym->next) {
4740 if (sym->parent!=NULL)
4741 continue; /* hierarchical data type */
4742 if (sym->ident!=iVARIABLE && sym->ident!=iARRAY)
4744 fprintf(log,"\t\t<member name=\"F:%s\">\n",funcdisplayname(symname,sym->name));
4746 tagsym=find_tag_byval(sym->tag);
4747 assert(tagsym!=NULL);
4748 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4750 assert(sym->refer!=NULL);
4751 if ((sym->usage & uPUBLIC)!=0)
4752 fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
4753 for (i=0; i<sym->numrefers; i++) {
4754 if ((ref=sym->refer[i])!=NULL)
4755 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4757 if (sym->documentation!=NULL)
4758 fprintf(log,"\t\t\t%s\n",sym->documentation);
4759 fprintf(log,"\t\t</member>\n");
4762 fprintf(log,"\n\t\t<!-- functions -->\n");
4763 for (sym=root->next; sym!=NULL; sym=sym->next) {
4764 if (sym->parent!=NULL)
4765 continue; /* hierarchical data type */
4766 if (sym->ident!=iFUNCTN)
4768 if ((sym->usage & (uREAD | uNATIVE))==uNATIVE)
4769 continue; /* unused native function */
4770 funcdisplayname(symname,sym->name);
4771 xmlencode(symname,symname);
4772 fprintf(log,"\t\t<member name=\"M:%s\" syntax=\"%s(",symname,symname);
4773 /* print only the names of the parameters between the parentheses */
4774 assert(sym->dim.arglist!=NULL);
4775 for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
4779 switch (sym->dim.arglist[arg].ident) {
4781 fprintf(log,"%s",sym->dim.arglist[arg].name);
4784 fprintf(log,"&%s",sym->dim.arglist[arg].name);
4787 fprintf(log,"%s",sym->dim.arglist[arg].name);
4788 for (dim=0; dim<sym->dim.arglist[arg].numdim;dim++)
4796 /* ??? should also print an "array return" size */
4797 fprintf(log,")\">\n");
4799 tagsym=find_tag_byval(sym->tag);
4800 assert(tagsym!=NULL);
4801 fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
4803 /* check whether this function is called from the outside */
4804 if ((sym->usage & uNATIVE)!=0)
4805 fprintf(log,"\t\t\t<attribute name=\"native\"/>\n");
4806 if ((sym->usage & uPUBLIC)!=0)
4807 fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
4808 if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0)
4809 fprintf(log,"\t\t\t<attribute name=\"entry\"/>\n");
4810 if ((sym->usage & uNATIVE)==0)
4811 fprintf(log,"\t\t\t<stacksize value=\"%ld\"/>\n",(long)sym->x.stacksize);
4812 if (sym->states!=NULL) {
4813 constvalue *stlist=sym->states->next;
4814 assert(stlist!=NULL); /* there should be at least one state item */
4815 while (stlist!=NULL && stlist->index==-1)
4816 stlist=stlist->next;
4817 assert(stlist!=NULL); /* state id should be found */
4818 i=state_getfsa(stlist->index);
4819 assert(i>=0); /* automaton 0 exists */
4820 stlist=automaton_findid(i);
4821 assert(stlist!=NULL); /* automaton should be found */
4822 fprintf(log,"\t\t\t<automaton name=\"%s\"/>\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)");
4823 //??? dump state decision table
4825 assert(sym->refer!=NULL);
4826 for (i=0; i<sym->numrefers; i++)
4827 if ((ref=sym->refer[i])!=NULL)
4828 fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4829 /* print all symbols that are required for this function to compile */
4830 for (ref=root->next; ref!=NULL; ref=ref->next) {
4833 for (i=0; i<ref->numrefers; i++)
4834 if (ref->refer[i]==sym)
4835 fprintf(log,"\t\t\t<dependency name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
4837 /* print parameter list, with tag & const information, plus descriptions */
4838 assert(sym->dim.arglist!=NULL);
4839 for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
4841 char *outer_start,*inner_start;
4842 int outer_length,inner_length;
4843 if (sym->dim.arglist[arg].ident==iVARARGS)
4844 fprintf(log,"\t\t\t<param name=\"...\">\n");
4846 fprintf(log,"\t\t\t<param name=\"%s\">\n",sym->dim.arglist[arg].name);
4847 /* print the tag name(s) for each parameter */
4848 assert(sym->dim.arglist[arg].numtags>0);
4849 assert(sym->dim.arglist[arg].tags!=NULL);
4850 paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0)
4851 || sym->dim.arglist[arg].ident==iREFERENCE
4852 || sym->dim.arglist[arg].ident==iREFARRAY;
4854 fprintf(log,"\t\t\t\t<paraminfo>");
4855 if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) {
4857 if (sym->dim.arglist[arg].numtags>1)
4859 for (i=0; i<sym->dim.arglist[arg].numtags; i++) {
4862 tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]);
4863 assert(tagsym!=NULL);
4864 fprintf(log,"%s",tagsym->name);
4866 if (sym->dim.arglist[arg].numtags>1)
4869 switch (sym->dim.arglist[arg].ident) {
4871 fprintf(log," &");
4875 for (dim=0; dim<sym->dim.arglist[arg].numdim; dim++) {
4876 if (sym->dim.arglist[arg].dim[dim]==0) {
4879 //??? find index tag
4880 fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]);
4886 fprintf(log," </paraminfo>\n");
4887 /* print the user description of the parameter (parse through
4888 * sym->documentation)
4890 if (sym->documentation!=NULL
4891 && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name,
4892 &outer_start, &outer_length, &inner_start, &inner_length))
4895 fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start);
4896 /* delete from documentation string */
4897 tail=outer_start+outer_length;
4898 memmove(outer_start,tail,strlen(tail)+1);
4900 fprintf(log,"\t\t\t</param>\n");
4902 if (sym->documentation!=NULL)
4903 fprintf(log,"\t\t\t%s\n",sym->documentation);
4904 fprintf(log,"\t\t</member>\n");
4907 fprintf(log,"\n\t</members>\n");
4908 fprintf(log,"</doc>\n");
4912 /* Every symbol has a referrer list, that contains the functions that use
4913 * the symbol. Now, if function "apple" is accessed by functions "banana" and
4914 * "citron", but neither function "banana" nor "citron" are used by anyone
4915 * else, then, by inference, function "apple" is not used either.
4917 static void reduce_referrers(symbol *root)
4924 for (sym=root->next; sym!=NULL; sym=sym->next) {
4925 if (sym->parent!=NULL)
4926 continue; /* hierarchical data type */
4927 if (sym->ident==iFUNCTN
4928 && (sym->usage & uNATIVE)==0
4929 && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0
4930 && count_referrers(sym)==0)
4932 sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
4933 /* find all symbols that are referred by this symbol */
4934 for (ref=root->next; ref!=NULL; ref=ref->next) {
4935 if (ref->parent!=NULL)
4936 continue; /* hierarchical data type */
4937 assert(ref->refer!=NULL);
4938 for (i=0; i<ref->numrefers && ref->refer[i]!=sym; i++)
4940 if (i<ref->numrefers) {
4941 assert(ref->refer[i]==sym);
4946 } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY)
4947 && (sym->usage & uPUBLIC)==0
4948 && sym->parent==NULL
4949 && count_referrers(sym)==0)
4951 sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */
4954 /* after removing a symbol, check whether more can be removed */
4955 } while (restart>0);
4958 #if !defined SC_LIGHT
4959 static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion)
4965 assert(sym->ident==iFUNCTN);
4966 assert((sym->usage & uNATIVE)==0);
4967 assert(recursion!=NULL);
4969 maxsize=sym->x.stacksize;
4970 for (i=0; i<sym->numrefers; i++) {
4971 if (sym->refer[i]!=NULL) {
4972 assert(sym->refer[i]->ident==iFUNCTN);
4973 assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */
4974 if (sym->refer[i]==sourcesym) { /* recursion detection */
4976 break; /* recursion was detected, quit loop */
4978 size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion);
4984 if ((sym->usage & uPUBLIC)!=0) {
4985 /* Find out how many parameters a public function has, then see if this
4986 * is bigger than some maximum
4988 arginfo *arg=sym->dim.arglist;
4991 while (arg->ident!=0) {
4995 assert(pubfuncparams!=0);
4996 if (count>*pubfuncparams)
4997 *pubfuncparams=count;
5000 return maxsize+basesize;
5003 static long max_stacksize(symbol *root,int *recursion)
5005 /* Loop over all non-native functions. For each function, loop
5006 * over all of its referrers, accumulating the stack requirements.
5007 * Detect (indirect) recursion with a "mark-and-sweep" algorithm.
5008 * I (mis-)use the "compound" field of the symbol structure for
5009 * the marker, as this field is unused for functions.
5011 * Note that the stack is shared with the heap. A host application
5012 * may "eat" cells from the heap as well, through amx_Allot(). The
5013 * stack requirements are thus only an estimate.
5020 assert(recursion!=NULL);
5022 for (sym=root->next; sym!=NULL; sym=sym->next)
5023 if (sym->ident==iFUNCTN)
5024 assert(sym->compound==0);
5029 *recursion=0; /* assume no recursion */
5030 for (sym=root->next; sym!=NULL; sym=sym->next) {
5031 /* drop out if this is not a user-implemented function */
5032 if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0)
5034 /* accumulate stack size for this symbol */
5035 size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion);
5041 maxsize++; /* +1 because a zero cell is always pushed on top
5042 * of the stack to catch stack overwrites */
5043 return maxsize+(maxparams+1);/* +1 because # of parameters is always pushed on entry */
5047 /* testsymbols - test for unused local or global variables
5049 * "Public" functions are excluded from the check, since these
5050 * may be exported to other object modules.
5051 * Labels are excluded from the check if the argument 'testlabs'
5052 * is 0. Thus, labels are not tested until the end of the function.
5053 * Constants may also be excluded (convenient for global constants).
5055 * When the nesting level drops below "level", the check stops.
5057 * The function returns whether there is an "entry" point for the file.
5058 * This flag will only be 1 when browsing the global symbol table.
5060 static int testsymbols(symbol *root,int level,int testlabs,int testconst)
5062 char symname[2*sNAMEMAX+16];
5065 symbol *sym=root->next;
5066 while (sym!=NULL && sym->compound>=level) {
5067 switch (sym->ident) {
5070 if ((sym->usage & uDEFINE)==0) {
5071 error(19,sym->name); /* not a label: ... */
5072 } else if ((sym->usage & uREAD)==0) {
5073 errorset(sSETPOS,sym->lnumber);
5074 error(203,sym->name); /* symbol isn't used: ... */
5079 if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) {
5080 funcdisplayname(symname,sym->name);
5081 if (strlen(symname)>0)
5082 error(203,symname); /* symbol isn't used ... (and not public/native/stock) */
5084 if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0)
5085 entry=TRUE; /* there is an entry point */
5086 /* also mark the function to the debug information */
5087 if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0)
5088 insert_dbgsymbol(sym);
5091 if (testconst && (sym->usage & uREAD)==0) {
5092 errorset(sSETPOS,sym->lnumber);
5093 error(203,sym->name); /* symbol isn't used: ... */
5098 if (sym->parent!=NULL)
5099 break; /* hierarchical data type */
5100 if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) {
5101 errorset(sSETPOS,sym->lnumber);
5102 error(203,sym->name); /* symbol isn't used (and not stock) */
5103 } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) {
5104 errorset(sSETPOS,sym->lnumber);
5105 error(204,sym->name); /* value assigned to symbol is never used */
5106 #if 0 // ??? not sure whether it is a good idea to force people use "const"
5107 } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) {
5108 errorset(sSETPOS,sym->lnumber);
5109 error(214,sym->name); /* make array argument "const" */
5112 /* also mark the variable (local or global) to the debug information */
5113 if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0)
5114 insert_dbgsymbol(sym);
5122 static cell calc_array_datasize(symbol *sym, cell *offset)
5127 assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
5128 length=sym->dim.array.length;
5129 if (sym->dim.array.level > 0) {
5130 cell sublength=calc_array_datasize(finddepend(sym),offset);
5132 *offset=length*(*offset+sizeof(cell));
5134 length*=length*sublength;
5144 static void destructsymbols(symbol *root,int level)
5148 symbol *sym=root->next;
5149 while (sym!=NULL && sym->compound>=level) {
5150 if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
5151 char symbolname[16];
5154 /* check that the '~' operator is defined for this tag */
5155 operator_symname(symbolname,"~",sym->tag,0,1,0);
5156 if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) {
5157 /* save PRI, in case of a return statment */
5159 pushreg(sPRI); /* right-hand operand is in PRI */
5162 /* if the variable is an array, get the number of elements */
5163 if (sym->ident==iARRAY) {
5164 elements=calc_array_datasize(sym,&offset);
5165 /* "elements" can be zero when the variable is declared like
5166 * new mytag: myvar[2][] = { {1, 2}, {3, 4} }
5167 * one should declare all dimensions!
5170 error(46,sym->name); /* array size is unknown */
5176 /* call the '~' operator */
5178 addconst(offset); /* add offset to array data to the address */
5180 pushval(2 /* *sizeof(cell)*/ );/* 2 parameters */
5181 assert(opsym->ident==iFUNCTN);
5182 ffcall(opsym,NULL,1);
5183 if (sc_status!=statSKIP)
5184 markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */
5185 if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL)
5186 opsym->x.lib->value += 1; /* increment "usage count" of the library */
5191 /* restore PRI, if it was saved */
5196 static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,
5201 if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL)
5202 error(123); /* insufficient memory (fatal error) */
5203 memset(cur,0,sizeof(constvalue));
5205 assert(strlen(name)<sNAMEMAX);
5206 strcpy(cur->name,name);
5215 SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index)
5217 constvalue *cur,*prev;
5219 /* find the end of the constant table */
5220 for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next)
5222 return insert_constval(prev,NULL,name,val,index);
5225 SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index)
5227 constvalue *ptr = table->next;
5230 if (strcmp(name,ptr->name)==0 && ptr->index==index)
5237 static constvalue *find_constval_byval(constvalue *table,cell val)
5239 constvalue *ptr = table->next;
5242 if (ptr->value==val)
5249 #if 0 /* never used */
5250 static int delete_constval(constvalue *table,char *name)
5252 constvalue *prev = table;
5253 constvalue *cur = prev->next;
5256 if (strcmp(name,cur->name)==0) {
5257 prev->next=cur->next;
5268 SC_FUNC void delete_consttable(constvalue *table)
5270 constvalue *cur=table->next, *next;
5277 memset(table,0,sizeof(constvalue));
5282 * Adds a symbol to the symbol table. Returns NULL on failure.
5284 SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag)
5288 /* Test whether a global or local symbol with the same name exists. Since
5289 * constants are stored in the symbols table, this also finds previously
5290 * defind constants. */
5291 sym=findglb(name,sSTATEVAR);
5296 if (sym->ident!=iCONSTEXPR)
5297 redef=1; /* redefinition a function/variable to a constant is not allowed */
5298 if ((sym->usage & uENUMFIELD)!=0) {
5299 /* enum field, special case if it has a different tag and the new symbol is also an enum field */
5303 redef=1; /* enumeration field is redefined (same tag) */
5304 tagid=find_tag_byval(tag);
5306 redef=1; /* new constant does not have a tag */
5308 tagsym=findconst(tagid->name,NULL);
5309 if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0)
5310 redef=1; /* new constant is not an enumeration field */
5312 /* in this particular case (enumeration field that is part of a different
5313 * enum, and non-conflicting with plain constants) we want to be able to
5317 goto redef_enumfield;
5318 } else if (sym->tag!=tag) {
5319 redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */
5322 error(21,name); /* symbol already defined */
5324 } else if (sym->addr!=val) {
5325 error(201,name); /* redefinition of constant (different value) */
5326 sym->addr=val; /* set new value */
5328 /* silently ignore redefinitions of constants with the same value & tag */
5332 /* constant doesn't exist yet (or is allowed to be redefined) */
5334 sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE);
5335 assert(sym!=NULL); /* fatal error 103 must be given on error */
5336 if (sc_status == statIDLE)
5337 sym->usage |= uPREDEF;
5341 /* statement - The Statement Parser
5343 * This routine is called whenever the parser needs to know what statement
5344 * it encounters (i.e. whenever program syntax requires a statement).
5346 static void statement(int *lastindent,int allow_decl)
5353 error(36); /* empty statement */
5360 insert_dbgline(fline);
5363 /* lex() has set stmtindent */
5364 if (lastindent!=NULL && tok!=tLABEL) {
5365 if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0)
5366 error(217); /* loose indentation */
5367 *lastindent=stmtindent;
5368 indent_nowarn=FALSE; /* if warning was blocked, re-enable it */
5380 error(3); /* declaration only valid in a block */
5389 error(3); /* declaration only valid in a block */
5397 error(3); /* declaration only valid in a block */
5403 if (!matchtoken('}')) /* {} is the empty statement */
5404 compound(save==fline,tok);
5405 /* lastst (for "last statement") does not change */
5408 error(36); /* empty statement */
5428 error(14); /* not in switch */
5472 default: /* non-empty expression */
5473 sc_allowproccall=optproccall;
5474 lexpush(); /* analyze token later */
5475 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);
5478 sc_allowproccall=FALSE;
5482 static void compound(int stmt_sameline,int starttok)
5485 cell save_decl=declared;
5487 int block_start=fline; /* save line where the compound block started */
5492 /* if there is more text on this line, we should adjust the statement indent */
5493 if (stmt_sameline) {
5495 const unsigned char *p=lptr;
5496 /* go back to the opening brace */
5497 while (*p!=starttok) {
5501 assert(*p==starttok); /* it should be found */
5502 /* go forward, skipping white-space */
5504 while (*p<=' ' && *p!='\0')
5506 assert(*p!='\0'); /* a token should be found */
5508 for (i=0; i<(int)(p-pline); i++)
5509 if (pline[i]=='\t' && sc_tabsize>0)
5510 stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
5515 endtok=(starttok=='{') ? '}' : tEND;
5516 nestlevel+=1; /* increase compound statement level */
5517 while (matchtoken(endtok)==0){/* repeat until compound statement is closed */
5519 error(30,block_start); /* compound block not closed at end of file */
5522 if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE || lastst==tENDLESS))
5523 error(225); /* unreachable code */
5524 statement(&indent,TRUE); /* do a statement */
5528 if (lastst!=tRETURN)
5529 destructsymbols(&loctab,nestlevel);
5530 if (lastst!=tRETURN && lastst!=tGOTO) {
5537 testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
5539 delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but
5540 * retain block local labels
5541 * (within the function) */
5542 nestlevel-=1; /* decrease compound statement level */
5547 * Global references: stgidx (referred to only)
5549 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
5550 int *tag,symbol **symptr,int chkfuncresult)
5552 return doexpr2(comma,chkeffect,allowarray,mark_endexpr,tag,symptr,chkfuncresult,NULL);
5557 * Global references: stgidx (referred to only)
5559 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
5560 int *tag,symbol **symptr,int chkfuncresult,value *lval)
5563 int localstaging=FALSE;
5567 stgset(TRUE); /* start stage-buffering */
5572 errorset(sEXPRMARK,0);
5574 /* on second round through, mark the end of the previous expression */
5576 markexpr(sEXPR,NULL,0);
5578 ident=expression(&val,tag,symptr,chkfuncresult,lval);
5579 if (!allowarray && (ident==iARRAY || ident==iREFARRAY))
5580 error(33,"-unknown-"); /* array must be indexed */
5581 if (chkeffect && !sideeffect)
5582 error(215); /* expression has no effect */
5583 sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */
5584 } while (comma && matchtoken(',')); /* more? */
5586 markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */
5587 errorset(sEXPRRELEASE,0);
5590 stgset(FALSE); /* stop staging */
5597 SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr)
5602 stgset(TRUE); /* start stage-buffering */
5603 stgget(&index,&cidx); /* mark position in code generator */
5604 errorset(sEXPRMARK,0);
5605 ident=expression(val,tag,symptr,FALSE,NULL);
5606 stgdel(index,cidx); /* scratch generated code */
5607 stgset(FALSE); /* stop stage-buffering */
5608 if (ident!=iCONSTEXPR) {
5609 error(8); /* must be constant expression */
5617 errorset(sEXPRRELEASE,0);
5618 return (ident==iCONSTEXPR);
5623 * In the case a "simple assignment" operator ("=") is used within a test,
5624 * the warning "possibly unintended assignment" is displayed. This routine
5625 * sets the global variable "sc_intest" to true, it is restored upon termination.
5626 * In the case the assignment was intended, use parantheses around the
5627 * expression to avoid the warning; primary() sets "sc_intest" to 0.
5629 * Global references: sc_intest (altered, but restored upon termination)
5631 static int test(int label,int parens,int invert)
5639 int localstaging=FALSE;
5642 stgset(TRUE); /* start staging */
5645 stgget(&index,&cidx); /* should start at zero if started locally */
5650 PUSHSTK_I(sc_intest);
5653 if (parens!=TEST_PLAIN) {
5654 if (matchtoken('('))
5656 else if (parens==TEST_THEN)
5658 else if (parens==TEST_DO)
5662 stgget(&index,&cidx); /* mark position (of last expression) in
5664 ident=expression(&constval,&tag,&sym,TRUE,NULL);
5665 tok=matchtoken(',');
5667 markexpr(sEXPR,NULL,0);
5668 } while (tok); /* do */
5671 if (ident==iARRAY || ident==iREFARRAY) {
5672 char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-";
5673 error(33,ptr); /* array must be indexed */
5675 if (ident==iCONSTEXPR) { /* constant expression */
5677 sc_intest=(short)POPSTK_I();/* restore stack */
5679 if (constval) { /* code always executed */
5680 error(206); /* redundant test: always non-zero */
5683 error(205); /* redundant code: never executed */
5687 stgout(0); /* write "jumplabel" code */
5688 stgset(FALSE); /* stop staging */
5692 if (tag!=0 && tag!=pc_addtag("bool"))
5693 if (check_userop(lneg,tag,0,1,NULL,&tag))
5694 invert= !invert; /* user-defined ! operator inverted result */
5696 jmp_ne0(label); /* jump to label if true (different from 0) */
5698 jmp_eq0(label); /* jump to label if false (equal to 0) */
5699 markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */
5700 sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */
5702 stgout(0); /* output queue from the very beginning (see
5703 * assert() when localstaging is set to TRUE) */
5704 stgset(FALSE); /* stop staging */
5709 static int doif(void)
5715 ifindent=stmtindent; /* save the indent of the "if" instruction */
5716 flab1=getlabel(); /* get label number for false branch */
5717 test(flab1,TEST_THEN,FALSE); /* get expression, branch to flab1 if false */
5718 statement(NULL,FALSE); /* if true, do a statement */
5719 if (!matchtoken(tELSE)) { /* if...else ? */
5720 setlabel(flab1); /* no, simple if..., print false label */
5722 lastst_true=lastst; /* save last statement of the "true" branch */
5723 /* to avoid the "dangling else" error, we want a warning if the "else"
5724 * has a lower indent than the matching "if" */
5725 if (stmtindent<ifindent && sc_tabsize>0)
5726 error(217); /* loose indentation */
5728 if ((lastst!=tRETURN) && (lastst!=tGOTO))
5729 jumplabel(flab2); /* "true" branch jumps around "else" clause, unless the "true" branch statement already jumped */
5730 setlabel(flab1); /* print false label */
5731 statement(NULL,FALSE); /* do "else" clause */
5732 setlabel(flab2); /* print true label */
5733 /* if both the "true" branch and the "false" branch ended with the same
5734 * kind of statement, set the last statement id to that kind, rather than
5735 * to the generic tIF; this allows for better "unreachable code" checking
5737 if (lastst==lastst_true)
5743 static int dowhile(void)
5745 int wq[wqSIZE]; /* allocate local queue */
5746 int save_endlessloop,retcode;
5748 save_endlessloop=endlessloop;
5749 addwhile(wq); /* add entry to queue for "break" */
5750 setlabel(wq[wqLOOP]); /* loop label */
5751 /* The debugger uses the "break" opcode to be able to "break" out of
5752 * a loop. To make sure that each loop has a break opcode, even for the
5753 * tiniest loop, set it below the top of the loop
5756 endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
5757 statement(NULL,FALSE); /* if so, do a statement */
5758 jumplabel(wq[wqLOOP]); /* and loop to "while" start */
5759 setlabel(wq[wqEXIT]); /* exit label */
5760 delwhile(); /* delete queue entry */
5762 retcode=endlessloop ? tENDLESS : tWHILE;
5763 endlessloop=save_endlessloop;
5768 * Note that "continue" will in this case not jump to the top of the loop, but
5769 * to the end: just before the TRUE-or-FALSE testing code.
5771 static int dodo(void)
5774 int save_endlessloop,retcode;
5776 save_endlessloop=endlessloop;
5777 addwhile(wq); /* see "dowhile" for more info */
5778 top=getlabel(); /* make a label first */
5779 setlabel(top); /* loop label */
5780 statement(NULL,FALSE);
5782 setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */
5784 endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
5786 setlabel(wq[wqEXIT]);
5790 retcode=endlessloop ? tENDLESS : tDO;
5791 endlessloop=save_endlessloop;
5795 static int dofor(void)
5797 int wq[wqSIZE],skiplab;
5799 int save_nestlevel,save_endlessloop;
5804 save_nestlevel=nestlevel;
5805 save_endlessloop=endlessloop;
5810 endtok= matchtoken('(') ? ')' : tDO;
5811 if (matchtoken(';')==0) {
5812 /* new variable declarations are allowed here */
5813 if (matchtoken(tNEW)) {
5814 /* The variable in expr1 of the for loop is at a
5815 * 'compound statement' level of it own.
5819 declloc(FALSE); /* declare local variable */
5821 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */
5825 /* Adjust the "declared" field in the "while queue", in case that
5826 * local variables were declared in the first expression of the
5827 * "for" loop. These are deleted in separately, so a "break" or a "continue"
5828 * must ignore these fields.
5832 /*ptr[wqBRK]=(int)declared;
5833 *ptr[wqCONT]=(int)declared;
5835 ptr[wqBRK] = stackusage->list_id;
5836 ptr[wqCONT] = stackusage->list_id;
5837 jumplabel(skiplab); /* skip expression 3 1st time */
5838 setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */
5840 /* Expressions 2 and 3 are reversed in the generated code: expression 3
5841 * precedes expression 2. When parsing, the code is buffered and marks for
5842 * the start of each expression are insterted in the buffer.
5845 stgset(TRUE); /* start staging */
5848 stgmark(sSTARTREORDER);
5849 stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */
5850 setlabel(skiplab); /* jump to this point after 1st expression */
5851 if (matchtoken(';')) {
5854 endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
5857 stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */
5858 if (!matchtoken(endtok)) {
5859 doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */
5862 stgmark(sENDREORDER); /* mark end of reversed evaluation */
5864 stgset(FALSE); /* stop staging */
5865 statement(NULL,FALSE);
5866 jumplabel(wq[wqLOOP]);
5867 setlabel(wq[wqEXIT]);
5870 assert(nestlevel>=save_nestlevel);
5871 if (nestlevel>save_nestlevel) {
5872 /* Clean up the space and the symbol table for the local
5873 * variable in "expr1".
5875 destructsymbols(&loctab,nestlevel);
5877 testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */
5879 delete_symbols(&loctab,nestlevel,FALSE,TRUE);
5880 nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */
5885 index=endlessloop ? tENDLESS : tFOR;
5886 endlessloop=save_endlessloop;
5890 /* The switch statement is incompatible with its C sibling:
5891 * 1. the cases are not drop through
5892 * 2. only one instruction may appear below each case, use a compound
5893 * instruction to execute multiple instructions
5894 * 3. the "case" keyword accepts a comma separated list of values to
5898 * PRI = expression result
5899 * param = table offset (code segment)
5902 static void doswitch(void)
5904 int lbl_table,lbl_exit,lbl_case;
5905 int swdefault,casecount;
5909 constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */
5910 constvalue *cse,*csp;
5911 char labelname[sNAMEMAX+1];
5913 endtok= matchtoken('(') ? ')' : tDO;
5914 doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */
5916 /* generate the code for the switch statement, the label is the address
5917 * of the case table (to be generated later).
5919 lbl_table=getlabel();
5920 lbl_case=0; /* just to avoid a compiler warning */
5921 ffswitch(lbl_table);
5923 if (matchtoken(tBEGIN)) {
5929 lbl_exit=getlabel(); /* get label number for jumping out of switch */
5933 tok=lex(&val,&str); /* read in (new) token */
5936 if (swdefault!=FALSE)
5937 error(15); /* "default" case must be last in switch statement */
5938 lbl_case=getlabel();
5939 PUSHSTK_I(sc_allowtags);
5940 sc_allowtags=FALSE; /* do not allow tagnames here */
5944 /* ??? enforce/document that, in a switch, a statement cannot start
5945 * with a label. Then, you can search for:
5946 * * the first semicolon (marks the end of a statement)
5947 * * an opening brace (marks the start of a compound statement)
5948 * and search for the right-most colon before that statement
5949 * Now, by replacing the ':' by a special COLON token, you can
5950 * parse all expressions until that special token.
5953 constexpr(&val,NULL,NULL);
5954 /* Search the insertion point (the table is kept in sorted order, so
5955 * that advanced abstract machines can sift the case table with a
5956 * binary search). Check for duplicate case values at the same time.
5958 for (csp=&caselist, cse=caselist.next;
5959 cse!=NULL && cse->value<val;
5960 csp=cse, cse=cse->next)
5962 if (cse!=NULL && cse->value==val)
5963 error(40,val); /* duplicate "case" label */
5964 /* Since the label is stored as a string in the "constvalue", the
5965 * size of an identifier must be at least 8, as there are 8
5966 * hexadecimal digits in a 32-bit number.
5969 #error Length of identifier (sNAMEMAX) too small.
5972 assert(csp->next==cse);
5973 insert_constval(csp,cse,itoh(lbl_case),val,0);
5974 if (matchtoken(tDBLDOT)) {
5975 error(1, ":", "..");
5977 } while (matchtoken(','));
5978 needtoken(':'); /* ':' ends the case */
5979 sc_allowtags=(short)POPSTK_I(); /* reset */
5981 statement(NULL,FALSE);
5982 jumplabel(lbl_exit);
5985 if (swdefault!=FALSE)
5986 error(16); /* multiple defaults in switch */
5987 lbl_case=getlabel();
5991 statement(NULL,FALSE);
5992 /* Jump to lbl_exit, even thouh this is the last clause in the
5993 * switch, because the jump table is generated between the last
5994 * clause of the switch and the exit label.
5996 jumplabel(lbl_exit);
6001 indent_nowarn=TRUE; /* disable this check */
6002 tok=endtok; /* break out of the loop after an error */
6005 } while (tok!=endtok);
6008 /* verify that the case table is sorted (unfortunatly, duplicates can
6009 * occur; there really shouldn't be duplicate cases, but the compiler
6010 * may not crash or drop into an assertion for a user error). */
6011 for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next)
6012 assert(cse->value <= cse->next->value);
6014 /* generate the table here, before lbl_exit (general jump target) */
6015 setlabel(lbl_table);
6016 assert(swdefault==FALSE || swdefault==TRUE);
6017 if (swdefault==FALSE) {
6018 /* store lbl_exit as the "none-matched" label in the switch table */
6019 strcpy(labelname,itoh(lbl_exit));
6021 /* lbl_case holds the label of the "default" clause */
6022 strcpy(labelname,itoh(lbl_case));
6024 ffcase(casecount,labelname,TRUE);
6025 /* generate the rest of the table */
6026 for (cse=caselist.next; cse!=NULL; cse=cse->next)
6027 ffcase(cse->value,cse->name,FALSE);
6030 delete_consttable(&caselist); /* clear list of case labels */
6033 static void doassert(void)
6038 if ((sc_debug & sCHKBOUNDS)!=0) {
6039 flab1=getlabel(); /* get label number for "OK" branch */
6040 test(flab1,TEST_PLAIN,TRUE);/* get expression and branch to flab1 if true */
6041 insert_dbgline(fline); /* make sure we can find the correct line number */
6042 ffabort(xASSERTION);
6045 stgset(TRUE); /* start staging */
6046 stgget(&index,&cidx); /* mark position in code generator */
6048 expression(NULL,NULL,NULL,FALSE,NULL);
6049 stgdel(index,cidx); /* just scrap the code */
6050 } while (matchtoken(','));
6051 stgset(FALSE); /* stop staging */
6056 static void dogoto(void)
6062 /* if we were inside an endless loop, assume that we jump out of it */
6065 if (lex(&val,&st)==tSYMBOL) {
6067 jumplabel((int)sym->addr);
6068 sym->usage|=uREAD; /* set "uREAD" bit */
6069 // ??? if the label is defined (check sym->usage & uDEFINE), check
6070 // sym->compound (nesting level of the label) against nestlevel;
6071 // if sym->compound < nestlevel, call the destructor operator
6073 error(20,st); /* illegal symbol name */
6078 static void dolabel(void)
6084 tokeninfo(&val,&st); /* retrieve label name again */
6085 if (find_constval(&tagname_tab,st,0)!=NULL)
6086 error(221,st); /* label name shadows tagname */
6088 setlabel((int)sym->addr);
6089 /* since one can jump around variable declarations or out of compound
6090 * blocks, the stack must be manually adjusted
6092 //:TODO: This is actually generated, egads!
6093 //We have to support this and LCTRL/SCTRL
6094 setstk(-declared*sizeof(cell));
6095 sym->usage|=uDEFINE; /* label is now defined */
6100 * Finds a label from the (local) symbol table or adds one to it.
6101 * Labels are local in scope.
6103 * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()"
6104 * must set this bit accordingly.
6106 static symbol *fetchlab(char *name)
6110 sym=findloc(name); /* labels are local in scope */
6112 if (sym->ident!=iLABEL)
6113 error(19,sym->name); /* not a label: ... */
6115 sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0);
6116 assert(sym!=NULL); /* fatal error 103 must be given on error */
6117 sym->x.declared=(int)declared;
6118 sym->compound=nestlevel;
6125 * Global references: rettype (altered)
6127 static void doreturn(void)
6133 if (!matchtoken(tTERM)) {
6134 /* "return <value>" */
6135 if ((rettype & uRETNONE)!=0)
6136 error(78); /* mix "return;" and "return value;" */
6137 ident=doexpr(TRUE,FALSE,TRUE,FALSE,&tag,&sym,TRUE);
6139 /* see if this function already has a sub type (an array attached) */
6140 sub=finddepend(curfunc);
6141 assert(sub==NULL || sub->ident==iREFARRAY);
6142 if ((rettype & uRETVALUE)!=0) {
6143 int retarray=(ident==iARRAY || ident==iREFARRAY);
6144 /* there was an earlier "return" statement in this function */
6145 if (sub==NULL && retarray || sub!=NULL && !retarray)
6146 error(79); /* mixing "return array;" and "return value;" */
6147 if (retarray && (curfunc->usage & uPUBLIC)!=0)
6148 error(90,curfunc->name); /* public function may not return array */
6150 rettype|=uRETVALUE; /* function returns a value */
6151 /* check tagname with function tagname */
6152 assert(curfunc!=NULL);
6153 if (!matchtag_string(ident, tag) && !matchtag(curfunc->tag,tag,TRUE))
6154 error(213); /* tagname mismatch */
6155 if (ident==iARRAY || ident==iREFARRAY) {
6156 int dim[sDIMEN_MAX],numdim;
6160 assert(sub->ident==iREFARRAY);
6161 /* this function has an array attached already; check that the current
6162 * "return" statement returns exactly the same array
6164 level=sym->dim.array.level;
6165 if (sub->dim.array.level!=level) {
6166 error(48); /* array dimensions must match */
6168 for (numdim=0; numdim<=level; numdim++) {
6169 dim[numdim]=(int)sub->dim.array.length;
6170 if (sym->dim.array.length!=dim[numdim])
6171 error(47); /* array sizes must match */
6173 sym=finddepend(sym);
6174 sub=finddepend(sub);
6175 assert(sym!=NULL && sub!=NULL);
6176 /* ^^^ both arrays have the same dimensions (this was checked
6177 * earlier) so the dependend should always be found
6183 int idxtag[sDIMEN_MAX];
6185 /* this function does not yet have an array attached; clone the
6186 * returned symbol beneath the current function
6190 level=sub->dim.array.level;
6191 for (numdim=0; numdim<=level; numdim++) {
6192 dim[numdim]=(int)sub->dim.array.length;
6193 idxtag[numdim]=sub->x.tags.index;
6195 sub=finddepend(sub);
6198 /* check that all dimensions are known */
6200 error(46,sym->name);
6202 /* the address of the array is stored in a hidden parameter; the address
6203 * of this parameter is 1 + the number of parameters (times the size of
6204 * a cell) + the size of the stack frame and the return address
6205 * base + 0*sizeof(cell) == previous "base"
6206 * base + 1*sizeof(cell) == function return address
6207 * base + 2*sizeof(cell) == number of arguments
6208 * base + 3*sizeof(cell) == first argument of the function
6210 * base + ((n-1)+3)*sizeof(cell) == last argument of the function
6211 * base + (n+3)*sizeof(cell) == hidden parameter with array address
6213 assert(curfunc!=NULL);
6214 assert(curfunc->dim.arglist!=NULL);
6215 for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++)
6217 sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag);
6218 sub->parent=curfunc;
6220 /* get the hidden parameter, copy the array (the array is on the heap;
6221 * it stays on the heap for the moment, and it is removed -usually- at
6222 * the end of the expression/statement, see expression() in SC3.C)
6224 address(sub,sALT); /* ALT = destination */
6225 arraysize=calc_arraysize(dim,numdim,0);
6226 memcopy(arraysize*sizeof(cell)); /* source already in PRI */
6227 /* moveto1(); is not necessary, callfunction() does a popreg() */
6230 /* this return statement contains no expression */
6232 if ((rettype & uRETVALUE)!=0) {
6233 char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */
6234 assert(curfunc!=NULL);
6235 funcdisplayname(symname,curfunc->name);
6236 error(209,symname); /* function should return a value */
6238 rettype|=uRETNONE; /* function does not return anything */
6240 destructsymbols(&loctab,0); /* call destructor for *all* locals */
6242 genstackfree(-1); /* free everything on the stack */
6243 ffret(strcmp(curfunc->name,uENTRYFUNC)!=0);
6246 static void dobreak(void)
6250 endlessloop=0; /* if we were inside an endless loop, we just jumped out */
6251 ptr=readwhile(); /* readwhile() gives an error if not in loop */
6255 destructsymbols(&loctab,nestlevel);
6256 genstackfree(ptr[wqBRK]);
6257 jumplabel(ptr[wqEXIT]);
6260 static void docont(void)
6264 ptr=readwhile(); /* readwhile() gives an error if not in loop */
6268 destructsymbols(&loctab,nestlevel);
6269 genstackfree(ptr[wqCONT]);
6270 genheapfree(ptr[wqCONT]);
6271 jumplabel(ptr[wqLOOP]);
6274 SC_FUNC void exporttag(int tag)
6276 /* find the tag by value in the table, then set the top bit to mark it
6279 if (tag!=0 && (tag & PUBLICTAG)==0) {
6281 for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next)
6284 ptr->value |= PUBLICTAG;
6288 static void doexit(void)
6292 if (matchtoken(tTERM)==0){
6293 doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
6300 destructsymbols(&loctab,0); /* call destructor for *all* locals */
6304 static void dosleep(void)
6308 if (matchtoken(tTERM)==0){
6309 doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
6318 /* for stack usage checking, mark the use of the sleep instruction */
6319 pc_memflags |= suSLEEP_INSTR;
6322 static void dostate(void)
6324 char name[sNAMEMAX+1];
6325 constvalue *automaton;
6330 #if !defined SC_LIGHT
6331 int length,index,listid,listindex,stateindex;
6335 /* check for an optional condition */
6336 if (matchtoken('(')) {
6337 flabel=getlabel(); /* get label number for "false" branch */
6338 pc_docexpr=TRUE; /* attach expression as a documentation string */
6339 test(flabel,TEST_PLAIN,FALSE);/* get expression, branch to flabel if false */
6346 if (!sc_getstateid(&automaton,&state)) {
6347 delete_autolisttable();
6352 /* store the new state id */
6353 assert(state!=NULL);
6354 ldconst(state->value,sPRI);
6355 assert(automaton!=NULL);
6356 assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
6357 storereg(automaton->value,sPRI);
6359 /* find the optional entry() function for the state */
6360 sym=findglb(uENTRYFUNC,sGLOBAL);
6361 if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) {
6362 for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) {
6363 assert(strlen(stlist->name)!=0);
6364 if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value))
6367 assert(stlist==NULL || state_inlist(stlist->index,state->value));
6369 /* the label to jump to is in stlist->name */
6370 ffcall(sym,stlist->name,0);
6375 setlabel(flabel); /* condition was false, jump around the state switch */
6377 #if !defined SC_LIGHT
6378 /* mark for documentation */
6379 if (sc_status==statFIRST) {
6381 /* get the last list id attached to the function, this contains the source states */
6382 assert(curfunc!=NULL);
6383 if (curfunc->states!=NULL) {
6384 stlist=curfunc->states->next;
6385 assert(stlist!=NULL);
6386 while (stlist->next!=NULL)
6387 stlist=stlist->next;
6388 listid=stlist->index;
6393 length=strlen(name)+70; /* +70 for the fixed part "<transition ... />\n" */
6394 /* see if there are any condition strings to attach */
6395 for (index=0; (str=get_autolist(index))!=NULL; index++)
6396 length+=strlen(str);
6397 if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) {
6399 sprintf(doc,"<transition target=\"%s\"",name);
6401 /* get the source state */
6402 stateindex=state_listitem(listid,listindex);
6403 state=state_findid(stateindex);
6404 assert(state!=NULL);
6405 sprintf(doc+strlen(doc)," source=\"%s\"",state->name);
6407 if (get_autolist(0)!=NULL) {
6408 /* add the condition */
6409 strcat(doc," condition=\"");
6410 for (index=0; (str=get_autolist(index))!=NULL; index++) {
6411 /* remove the ')' token that may be appended before detecting that the expression has ended */
6412 if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL)
6418 insert_docstring(doc);
6419 } while (listid>=0 && ++listindex<state_count(listid));
6424 delete_autolisttable();
6428 static void addwhile(int *ptr)
6432 ptr[wqBRK]=stackusage->list_id; /* stack pointer (for "break") */
6433 ptr[wqCONT]=stackusage->list_id; /* for "continue", possibly adjusted later */
6434 ptr[wqLOOP]=getlabel();
6435 ptr[wqEXIT]=getlabel();
6436 if (wqptr>=(wq+wqTABSZ-wqSIZE))
6437 error(122,"loop table"); /* loop table overflow (too many active loops)*/
6439 while (k<wqSIZE){ /* copy "ptr" to while queue table */
6447 static void delwhile(void)
6453 static int *readwhile(void)
6456 error(24); /* out of context */
6459 return (wqptr-wqSIZE);