1 /* Pawn compiler - File input, preprocessing and lexical analysis functions
3 * Copyright (c) ITB CompuPhase, 1997-2006
5 * This software is provided "as-is", without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in
15 * a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
31 #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
36 #include <alloc/fortify.h>
39 /* flags for litchar() */
42 static cell litchar(const unsigned char **lptr,int flags);
43 static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag);
45 static void substallpatterns(unsigned char *line,int buffersize);
46 static int match(char *st,int end);
47 static int alpha(char c);
49 #define SKIPMODE 1 /* bit field in "#if" stack */
50 #define PARSEMODE 2 /* bit field in "#if" stack */
51 #define HANDLED_ELSE 4 /* bit field in "#if" stack */
52 #define SKIPPING (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE)
54 static short icomment; /* currently in multiline comment? */
55 static char ifstack[sCOMP_STACK]; /* "#if" stack */
56 static short iflevel; /* nesting level if #if/#else/#endif */
57 static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */
58 static unsigned char term_expr[] = "";
59 static int listline=-1; /* "current line" for the list file */
64 * Uses a LIFO stack to store information. The stack is used by doinclude(),
65 * doswitch() (to hold the state of "swactive") and some other routines.
67 * Porting note: I made the bold assumption that an integer will not be
68 * larger than a pointer (it may be smaller). That is, the stack element
69 * is typedef'ed as a pointer type, but I also store integers on it. See
72 * Global references: stack,stkidx,stktop (private to pushstk(), popstk()
75 static stkitem *stack=NULL;
76 static int stkidx=0,stktop=0;
78 SC_FUNC void pushstk(stkitem val)
80 assert(stkidx<=stktop);
83 int newsize= (stktop==0) ? 16 : 2*stktop;
84 /* try to resize the stack */
85 assert(newsize>stktop);
86 newstack=(stkitem*)malloc(newsize*sizeof(stkitem));
88 error(122,"parser stack"); /* stack overflow (recursive include?) */
90 memcpy(newstack,stack,stkidx*sizeof(stkitem));
96 assert(stkidx<stktop);
101 SC_FUNC stkitem popstk(void)
105 s.i=-1; /* stack is empty */
110 return stack[stkidx];
113 SC_FUNC void clearstk(void)
115 assert(stack!=NULL || stktop==0);
124 SC_FUNC int plungequalifiedfile(char *name)
126 static char *extensions[] = { ".inc", ".p", ".pawn" };
133 fp=(FILE*)pc_opensrc(name);
134 ext=strchr(name,'\0'); /* save position */
136 /* try to append an extension */
137 strcpy(ext,extensions[ext_idx]);
138 fp=(FILE*)pc_opensrc(name);
140 *ext='\0'; /* on failure, restore filename */
143 } while (fp==NULL && ext_idx<(sizeof extensions / sizeof extensions[0]));
145 *ext='\0'; /* restore filename */
149 PUSHSTK_P(inpfname); /* pointer to current file name */
150 PUSHSTK_P(curlibrary);
153 assert(skiplevel==iflevel); /* these two are always the same when "parsing" */
154 PUSHSTK_I(sc_is_utf8);
158 inpfname=duplicatestring(name);/* set name of include file */
160 error(123); /* insufficient memory */
161 inpf=fp; /* set input file pointer to include file */
163 fline=0; /* set current line number to 0 */
165 icomment=0; /* not in a comment */
166 insert_dbgfile(inpfname);
167 setfiledirect(inpfname);
168 listline=-1; /* force a #line directive when changing the file */
169 sc_is_utf8=(short)scan_utf8(inpf,name);
173 SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths)
177 if (try_currentpath) {
178 result=plungequalifiedfile(name);
180 /* failed to open the file in the active directory, try to open the file
181 * in the same directory as the current file --but first check whether
182 * there is a (relative) path for the current file
185 if ((ptr=strrchr(inpfname,DIRSEP_CHAR))!=0) {
186 int len=(int)(ptr-inpfname)+1;
187 if (len+strlen(name)<_MAX_PATH) {
188 char path[_MAX_PATH];
189 strlcpy(path,inpfname,len+1);
190 strlcat(path,name,sizeof path);
191 result=plungequalifiedfile(path);
197 if (try_includepaths && name[0]!=DIRSEP_CHAR) {
200 for (i=0; !result && (ptr=get_path(i))!=NULL; i++) {
201 char path[_MAX_PATH];
202 strlcpy(path,ptr,sizeof path);
203 strlcat(path,name,sizeof path);
204 result=plungequalifiedfile(path);
210 static void check_empty(const unsigned char *lptr)
212 /* verifies that the string contains only whitespace */
213 while (*lptr<=' ' && *lptr!='\0')
216 error(38); /* extra characters on line */
221 * Gets the name of an include file, pushes the old file on the stack and
222 * sets some options. This routine doesn't use lex(), since lex() doesn't
223 * recognize file names (and directories).
225 * Global references: inpf (altered)
230 static void doinclude(int silent)
232 char name[_MAX_PATH];
233 char symname[sNAMEMAX];
238 while (*lptr<=' ' && *lptr!='\0') /* skip leading whitespace */
240 if (*lptr=='<' || *lptr=='\"'){
241 c=(char)((*lptr=='\"') ? '\"' : '>'); /* termination character */
243 while (*lptr<=' ' && *lptr!='\0') /* skip whitespace after quote */
250 while (*lptr!=c && *lptr!='\0' && i<sizeof name - 1) /* find the end of the string */
252 while (i>0 && name[i-1]<=' ')
253 i--; /* strip trailing whitespace */
254 assert(i>=0 && i<sizeof name);
255 name[i]='\0'; /* zero-terminate the string */
257 if (*lptr!=c) { /* verify correct string termination */
258 error(37); /* invalid string */
262 check_empty(lptr+1); /* verify that the rest of the line is whitespace */
264 /* create a symbol from the name of the include file; this allows the system
265 * to test for multiple inclusions
267 strcpy(symname,"_inc_");
268 if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
269 strlcat(symname,ptr+1,sizeof symname);
271 strlcat(symname,name,sizeof symname);
272 if (find_symbol(&glbtab,symname,fcurrent,-1,NULL)==NULL) {
273 /* constant is not present, so this file has not been included yet */
275 /* Include files between "..." or without quotes are read from the current
276 * directory, or from a list of "include directories". Include files
277 * between <...> are only read from the list of include directories.
279 result=plungefile(name,(c!='>'),TRUE);
281 add_constant(symname,1,sGLOBAL,0);
283 error(120,name); /* cannot read from ... (fatal error) */
289 * Reads in a new line from the input file pointed to by "inpf". readline()
290 * concatenates lines that end with a \ with the next line. If no more data
291 * can be read from the file, readline() attempts to pop off the previous file
292 * from the stack. If that fails too, it sets "freading" to 0.
294 * Global references: inpf,fline,inpfname,freading,icomment (altered)
296 static void readline(unsigned char *line)
306 if (inpf==NULL || pc_eofsrc(inpf)) {
308 error(49); /* invalid line continuation */
309 if (inpf!=NULL && inpf!=inpf_org)
312 if (i==-1) { /* All's done; popstk() returns "stack is empty" */
315 /* when there is nothing more to read, the #if/#else stack should
316 * be empty and we should not be in a comment
320 error(1,"#endif","-end of file-");
321 else if (icomment!=0)
322 error(1,"*/","-end of file-");
326 fcurrent=(short)POPSTK_I();
327 icomment=(short)POPSTK_I();
328 sc_is_utf8=(short)POPSTK_I();
329 iflevel=(short)POPSTK_I();
330 skiplevel=iflevel; /* this condition held before including the file */
331 assert(!SKIPPING); /* idem ditto */
332 curlibrary=(constvalue *)POPSTK_P();
333 free(inpfname); /* return memory allocated for the include file name */
334 inpfname=(char *)POPSTK_P();
335 inpf=(FILE *)POPSTK_P();
336 insert_dbgfile(inpfname);
337 setfiledirect(inpfname);
338 listline=-1; /* force a #line directive when changing the file */
341 if (pc_readsrc(inpf,line,num)==NULL) {
342 *line='\0'; /* delete line */
345 /* check whether to erase leading spaces */
347 unsigned char *ptr=line;
348 while (*ptr<=' ' && *ptr!='\0')
351 memmove(line,ptr,strlen((char*)ptr)+1);
354 /* check whether a full line was read */
355 if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf))
356 error(75); /* line too long */
357 /* check if the next line must be concatenated to this line */
358 if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL)
359 ptr=(unsigned char*)strchr((char*)line,'\r');
360 if (ptr!=NULL && ptr>line) {
361 assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */
362 while (ptr>line && *ptr<=' ')
363 ptr--; /* skip trailing whitespace */
366 /* set '\a' at the position of '\\' to make it possible to check
367 * for a line continuation in a single line comment (error 49)
370 *ptr='\0'; /* erase '\n' (and any trailing whitespace) */
373 num-=strlen((char*)line);
374 line+=strlen((char*)line);
377 } while (num>=0 && cont);
382 * Replaces all comments from the line by space characters. It updates
383 * a global variable ("icomment") for multiline comments.
385 * This routine also supports the C++ extension for single line comments.
386 * These comments are started with "//" and end at the end of the line.
388 * The function also detects (and manages) "documentation comments". The
389 * global variable "icomment" is set to 2 for documentation comments.
391 * Global references: icomment (private to "stripcom")
393 static void stripcom(unsigned char *line)
396 #if !defined SC_LIGHT
397 #define COMMENT_LIMIT 100
398 #define COMMENT_MARGIN 40 /* length of the longest word */
399 char comment[COMMENT_LIMIT+COMMENT_MARGIN];
402 static int prev_singleline=FALSE;
403 int singleline=prev_singleline;
405 prev_singleline=FALSE; /* preset */
410 if (*line=='*' && *(line+1)=='/') {
411 #if !defined SC_LIGHT
413 assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
414 comment[commentidx]='\0';
415 if (strlen(comment)>0)
416 insert_docstring(comment);
419 icomment=0; /* comment has ended */
420 *line=' '; /* replace '*' and '/' characters by spaces */
424 if (*line=='/' && *(line+1)=='*')
425 error(216); /* nested comment */
426 #if !defined SC_LIGHT
427 /* collect the comment characters in a string */
429 if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) {
430 /* ignore leading whitespace and '*' characters */
431 } else if (commentidx<COMMENT_LIMIT+COMMENT_MARGIN-1) {
432 comment[commentidx++]=(char)((*line!='\n') ? *line : ' ');
433 if (commentidx>COMMENT_LIMIT && *line!='\0' && *line<=' ') {
434 comment[commentidx]='\0';
435 insert_docstring(comment);
442 *line=' '; /* replace comments by spaces */
446 if (*line=='/' && *(line+1)=='*'){
447 icomment=1; /* start comment */
448 #if !defined SC_LIGHT
449 /* there must be two "*" behind the slash and then white space */
450 if (*(line+2)=='*' && *(line+3)<=' ') {
451 /* if we are not in a function, we must attach the previous block
452 * to the global documentation
454 if (curfunc==NULL && get_docstring(0)!=NULL)
455 sc_attachdocumentation(NULL);
456 icomment=2; /* documentation comment */
461 *line=' '; /* replace '/' and '*' characters by spaces */
466 } else if (*line=='/' && *(line+1)=='/'){ /* comment to end of line */
467 if (strchr((char*)line,'\a')!=NULL)
468 error(49); /* invalid line continuation */
469 #if !defined SC_LIGHT
470 if (*(line+2)=='/' && *(line+3)<=' ') {
471 /* documentation comment */
472 char *str=(char*)line+3;
474 while (*str<=' ' && *str!='\0')
475 str++; /* skip leading whitespace */
476 if ((end=strrchr(str,'\n'))!=NULL)
477 *end='\0';/* erase trailing '\n' */
478 /* if there is a disjunct block, we may need to attach the previous
479 * block to the global documentation
481 if (!singleline && curfunc==NULL && get_docstring(0)!=NULL)
482 sc_attachdocumentation(NULL);
483 insert_docstring(str);
484 prev_singleline=TRUE;
487 *line++='\n'; /* put "newline" at first slash */
488 *line='\0'; /* put "zero-terminator" at second slash */
490 if (*line=='\"' || *line=='\''){ /* leave literals unaltered */
491 c=*line; /* ending quote, single or double */
493 while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0')
495 line+=1; /* skip final quote */
502 #if !defined SC_LIGHT
504 assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
505 comment[commentidx]='\0';
506 if (strlen(comment)>0)
507 insert_docstring(comment);
514 * Attempts to interpret a numeric symbol as a boolean value. On success
515 * it returns the number of characters processed (so the line pointer can be
516 * adjusted) and the value is stored in "val". Otherwise it returns 0 and
519 * A boolean value must start with "0b"
521 static int btoi(cell *val,const unsigned char *curptr)
523 const unsigned char *ptr;
527 if (*ptr=='0' && *(ptr+1)=='b') {
529 while (*ptr=='0' || *ptr=='1' || *ptr=='_') {
531 *val=(*val<<1) | (*ptr-'0');
537 if (alphanum(*ptr)) /* number must be delimited by non-alphanumeric char */
540 return (int)(ptr-curptr);
545 * Attempts to interpret a numeric symbol as a decimal value. On success
546 * it returns the number of characters processed and the value is stored in
547 * "val". Otherwise it returns 0 and "val" is garbage.
549 static int dtoi(cell *val,const unsigned char *curptr)
551 const unsigned char *ptr;
555 if (!isdigit(*ptr)) /* should start with digit */
557 while (isdigit(*ptr) || *ptr=='_') {
559 *val=(*val*10)+(*ptr-'0');
562 if (alphanum(*ptr)) /* number must be delimited by non-alphanumerical */
564 if (*ptr=='.' && isdigit(*(ptr+1)))
565 return 0; /* but a fractional part must not be present */
566 return (int)(ptr-curptr);
571 * Attempts to interpret a numeric symbol as a hexadecimal value. On
572 * success it returns the number of characters processed and the value is
573 * stored in "val". Otherwise it return 0 and "val" is garbage.
575 static int htoi(cell *val,const unsigned char *curptr)
577 const unsigned char *ptr;
581 if (!isdigit(*ptr)) /* should start with digit */
583 if (*ptr=='0' && *(ptr+1)=='x') { /* C style hexadecimal notation */
585 while (ishex(*ptr) || *ptr=='_') {
592 *val+= (tolower(*ptr)-'a'+10);
602 return (int)(ptr-curptr);
606 static double pow10(int value)
627 * Attempts to interpret a numeric symbol as a rational number, either as
628 * IEEE 754 single/double precision floating point or as a fixed point integer.
629 * On success it returns the number of characters processed and the value is
630 * stored in "val". Otherwise it returns 0 and "val" is unchanged.
632 * Pawn has stricter definition for rational numbers than most:
633 * o the value must start with a digit; ".5" is not a valid number, you
635 * o a period must appear in the value, even if an exponent is given; "2e3"
636 * is not a valid number, you should write "2.0e3"
637 * o at least one digit must follow the period; "6." is not a valid number,
638 * you should write "6.0"
640 static int ftoi(cell *val,const unsigned char *curptr)
642 const unsigned char *ptr;
643 double fnum,ffrac,fmult;
644 unsigned long dnum,dbase;
647 assert(rational_digits>=0 && rational_digits<9);
648 for (i=0,dbase=1; i<rational_digits; i++)
653 if (!isdigit(*ptr)) /* should start with digit */
655 while (isdigit(*ptr) || *ptr=='_') {
657 fnum=(fnum*10.0)+(*ptr-'0');
658 dnum=(dnum*10L)+(*ptr-'0')*dbase;
663 return 0; /* there must be a period */
665 if (!isdigit(*ptr)) /* there must be at least one digit after the dot */
670 while (isdigit(*ptr) || *ptr=='_') {
672 ffrac=(ffrac*10.0)+(*ptr-'0');
675 dnum += (*ptr-'0')*dbase;
676 if (dbase==0L && sc_rationaltag && rational_digits>0 && !ignore) {
677 error(222); /* number of digits exceeds rational number precision */
683 fnum += ffrac*fmult; /* form the number so far */
684 if (*ptr=='e') { /* optional fractional part */
693 if (!isdigit(*ptr)) /* 'e' should be followed by a digit */
696 while (isdigit(*ptr)) {
697 exp=(exp*10)+(*ptr-'0');
701 fmult=pow10(exp*sign);
703 fmult=pow(10,exp*sign);
706 dnum *= (unsigned long)(fmult+0.5);
709 /* decide how to store the number */
710 if (sc_rationaltag==0) {
711 error(70); /* rational number support was not enabled */
713 } else if (rational_digits==0) {
715 #if PAWN_CELL_SIZE==32
716 float value=(float)fnum;
717 *val=*((cell *)&value);
718 #if 0 /* SourceMod - not needed */
719 /* I assume that the C/C++ compiler stores "float" values in IEEE 754
720 * format (as mandated in the ANSI standard). Test this assumption
722 * Note: problems have been reported with GCC 3.2.x, version 3.3.x works.
724 { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
726 /* test 0.0 == all bits 0 */
727 assert(*(uint32_t*)&test1==0x00000000L);
728 /* test sign & magnitude format */
729 assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
730 /* test a known value */
731 assert(*(uint32_t*)&test2==0x42480000L);
734 #elif PAWN_CELL_SIZE==64
735 *val=*((cell *)&fnum);
736 #if 0 /* SourceMod - not needed */
737 /* I assume that the C/C++ compiler stores "double" values in IEEE 754
738 * format (as mandated in the ANSI standard).
740 { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
742 /* test 0.0 == all bits 0 */
743 assert(*(uint64_t*)&test1==0x00000000L);
744 /* test sign & magnitude format */
745 assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
749 #error Unsupported cell size
756 return (int)(ptr-curptr);
761 * Reads in a number (binary, decimal or hexadecimal). It returns the number
762 * of characters processed or 0 if the symbol couldn't be interpreted as a
763 * number (in this case the argument "val" remains unchanged). This routine
764 * relies on the 'early dropout' implementation of the logical or (||)
767 * Note: the routine doesn't check for a sign (+ or -). The - is checked
768 * for at "hier2()" (in fact, it is viewed as an operator, not as a
769 * sign) and the + is invalid (as in K&R C, and unlike ANSI C).
771 static int number(cell *val,const unsigned char *curptr)
776 if ((i=btoi(&value,curptr))!=0 /* binary? */
777 || (i=htoi(&value,curptr))!=0 /* hexadecimal? */
778 || (i=dtoi(&value,curptr))!=0) /* decimal? */
783 return 0; /* else not a number */
787 static void chrcat(char *str,char chr)
789 str=strchr(str,'\0');
794 static int preproc_expr(cell *val,int *tag)
801 /* Disable staging; it should be disabled already because
802 * expressions may not be cut off half-way between conditional
803 * compilations. Reset the staging index, but keep the code
806 if (stgget(&index,&code_index)) {
807 error(57); /* unfinished expression */
808 stgdel(0,code_index);
811 assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must point inside the string */
812 #if !defined NO_DEFINE
813 /* preprocess the string */
814 substallpatterns(pline,sLINEMAX);
815 assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */
817 /* append a special symbol to the string, so the expression
818 * analyzer won't try to read a next line when it encounters
821 assert(strlen((char*)pline)<sLINEMAX);
822 term=strchr((char*)pline,'\0');
824 chrcat((char*)pline,PREPROC_TERM); /* the "DEL" code (see SC.H) */
825 result=constexpr(val,tag,NULL); /* get value (or 0 on error) */
826 *term='\0'; /* erase the token (if still present) */
827 lexclr(FALSE); /* clear any "pushed" tokens */
832 * Returns returns a pointer behind the closing quote or to the other
833 * character that caused the input to be ended.
835 static const unsigned char *getstring(unsigned char *dest,int max,const unsigned char *line)
837 assert(dest!=NULL && line!=NULL);
839 while (*line<=' ' && *line!='\0')
840 line++; /* skip whitespace */
844 while (*line!='"' && *line!='\0') {
851 line++; /* skip closing " */
853 error(37); /* invalid string */
855 error(37); /* invalid string */
873 * Recognizes the compiler directives. The function returns:
874 * CMD_NONE the line must be processed
875 * CMD_TERM a pending expression must be completed before processing further lines
876 * Other value: the line must be skipped, because:
877 * CMD_CONDFALSE false "#if.." code
878 * CMD_EMPTYLINE line is empty
879 * CMD_INCLUDE the line contains a #include directive
880 * CMD_DEFINE the line contains a #subst directive
881 * CMD_IF the line contains a #if/#else/#endif directive
882 * CMD_DIRECTIVE the line contains some other compiler directive
884 * Global variables: iflevel, ifstack (altered)
887 static int command(void)
895 while (*lptr<=' ' && *lptr!='\0')
898 return CMD_EMPTYLINE; /* empty line */
900 return SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* it is not a compiler directive */
901 /* compiler directive found */
902 indent_nowarn=TRUE; /* allow loose indentation" */
903 lexclr(FALSE); /* clear any "pushed" tokens */
904 /* on a pending expression, force to return a silent ';' token and force to
907 if (!sc_needsemicolon && stgget(&index,&code_index)) {
912 ret=SKIPPING ? CMD_CONDFALSE : CMD_DIRECTIVE; /* preset 'ret' to CMD_DIRECTIVE (most common case) */
914 case tpIF: /* conditional compilation */
917 if (iflevel>=sCOMP_STACK)
918 error(122,"Conditional compilation stack"); /* table overflow */
921 break; /* break out of switch */
923 preproc_expr(&val,NULL); /* get value (or 0 on error) */
924 ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
932 error(26); /* no matching #if */
935 /* check for earlier #else */
936 if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) {
938 error(61); /* #elseif directive may not follow an #else */
940 error(60); /* multiple #else directives between #if ... #endif */
944 /* if there has been a "parse mode" on this level, set "skip mode",
945 * otherwise, clear "skip mode"
947 if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) {
948 /* there has been a parse mode already on this level, so skip the rest */
949 ifstack[iflevel-1] |= (char)SKIPMODE;
950 /* if we were already skipping this section, allow expressions with
951 * undefined symbols; otherwise check the expression to catch errors
954 if (skiplevel==iflevel)
955 preproc_expr(&val,NULL); /* get, but ignore the expression */
957 lptr=strchr(lptr,'\0');
960 /* previous conditions were all FALSE */
962 /* if we were already skipping this section, allow expressions with
963 * undefined symbols; otherwise check the expression to catch errors
965 if (skiplevel==iflevel) {
966 preproc_expr(&val,NULL); /* get value (or 0 on error) */
968 lptr=strchr(lptr,'\0');
971 ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
973 /* a simple #else, clear skip mode */
974 ifstack[iflevel-1] &= (char)~SKIPMODE;
984 error(26); /* no matching "#if" */
988 if (iflevel<skiplevel)
993 case tINCLUDE: /* #include directive */
997 doinclude(tok==tpTRYINCLUDE);
1001 char pathname[_MAX_PATH];
1002 lptr=getstring((unsigned char*)pathname,sizeof pathname,lptr);
1003 if (strlen(pathname)>0) {
1005 inpfname=duplicatestring(pathname);
1007 error(123); /* insufficient memory */
1015 if (lex(&val,&str)!=tNUMBER)
1016 error(8); /* invalid/non-constant expression */
1022 if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) {
1023 for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++)
1024 /* nothing */; /* save start of expression */
1025 preproc_expr(&val,NULL); /* get constant expression (or 0 on error) */
1027 error(130,str); /* assertion failed */
1033 if (lex(&val,&str)==tSYMBOL) {
1034 if (strcmp(str,"amxlimit")==0) {
1035 preproc_expr(&pc_amxlimit,NULL);
1036 } else if (strcmp(str,"amxram")==0) {
1037 preproc_expr(&pc_amxram,NULL);
1038 } else if (strcmp(str,"codepage")==0) {
1039 char name[sNAMEMAX+1];
1040 while (*lptr<=' ' && *lptr!='\0')
1043 lptr=getstring((unsigned char*)name,sizeof name,lptr);
1046 for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
1051 error(128); /* codepage mapping file not found */
1052 } else if (strcmp(str,"compress")==0) {
1054 preproc_expr(&val,NULL);
1055 sc_compress=(int)val; /* switch code packing on/off */
1056 } else if (strcmp(str,"ctrlchar")==0) {
1057 while (*lptr<=' ' && *lptr!='\0')
1060 sc_ctrlchar=sc_ctrlchar_org;
1062 if (lex(&val,&str)!=tNUMBER)
1063 error(27); /* invalid character constant */
1064 sc_ctrlchar=(char)val;
1066 } else if (strcmp(str,"deprecated")==0) {
1067 while (*lptr<=' ' && *lptr!='\0')
1069 pc_deprecate=(char*)malloc(strlen((char*)lptr)+1);
1070 if (pc_deprecate!=NULL)
1071 strcpy(pc_deprecate,(char*)lptr);
1072 lptr=(unsigned char*)strchr((char*)lptr,'\0'); /* skip to end (ignore "extra characters on line") */
1073 } else if (strcmp(str,"dynamic")==0) {
1074 preproc_expr(&pc_stksize,NULL);
1075 } else if (!strcmp(str,"library")
1076 ||!strcmp(str,"reqclass")
1077 ||!strcmp(str,"loadlib")
1078 ||!strcmp(str,"explib")
1079 ||!strcmp(str,"expclass")
1080 ||!strcmp(str,"defclasslib")) {
1081 char name[sNAMEMAX+1],sname[sNAMEMAX+1];
1082 const char *prefix="";
1085 if (!strcmp(str,"library"))
1087 else if (!strcmp(str,"reqclass"))
1089 else if (!strcmp(str,"loadlib"))
1091 else if (!strcmp(str,"explib"))
1093 else if (!strcmp(str,"expclass"))
1095 else if (!strcmp(str,"defclasslib"))
1097 while (*lptr<=' ' && *lptr!='\0')
1100 lptr=getstring((unsigned char*)name,sizeof name,lptr);
1103 for (i=0; i<sizeof name && (alphanum(*lptr) || *lptr=='-'); i++,lptr++)
1106 if (!strncmp(str,"exp",3) || !strncmp(str,"def",3)) {
1107 while (*lptr && isspace(*lptr))
1109 for (i=1; i<sizeof sname && alphanum(*lptr); i++,lptr++)
1119 if (strlen(name)==0) {
1121 } else if (strcmp(name,"-")==0) {
1122 pc_addlibtable=FALSE;
1124 /* add the name if it does not yet exist in the table */
1125 char newname[sNAMEMAX+1];
1126 if (strlen(name)+strlen(prefix)+strlen(sname)<=sNAMEMAX) {
1127 strcpy(newname,prefix);
1128 strcat(newname,name);
1129 strcat(newname,sname);
1130 if (find_constval(&libname_tab,newname,0)==NULL) {
1131 if (!strcmp(str,"library") || !strcmp(str,"reqclass")) {
1132 curlibrary=append_constval(&libname_tab,newname,1,0);
1134 append_constval(&libname_tab,newname,1,0);
1139 #if 0 /* more unused */
1140 } else if (strcmp(str,"pack")==0) {
1142 preproc_expr(&val,NULL); /* default = packed/unpacked */
1143 sc_packstr=(int)val;
1145 } else if (strcmp(str,"rational")==0) {
1146 char name[sNAMEMAX+1];
1149 /* first gather all information, start with the tag name */
1150 while (*lptr<=' ' && *lptr!='\0')
1152 for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
1155 /* then the precision (for fixed point arithmetic) */
1156 while (*lptr<=' ' && *lptr!='\0')
1159 preproc_expr(&digits,NULL);
1160 if (digits<=0 || digits>9) {
1161 error(68); /* invalid rational number precision */
1167 /* add the tag (make it public) and check the values */
1170 if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) {
1172 rational_digits=(int)digits;
1174 error(69); /* rational number format already set, can only be set once */
1176 } else if (strcmp(str,"semicolon")==0) {
1178 preproc_expr(&val,NULL);
1179 sc_needsemicolon=(int)val;
1180 } else if (strcmp(str,"tabsize")==0) {
1182 preproc_expr(&val,NULL);
1183 sc_tabsize=(int)val;
1184 } else if (strcmp(str,"align")==0) {
1186 } else if (strcmp(str,"unused")==0) {
1187 char name[sNAMEMAX+1];
1192 while (*lptr<=' ' && *lptr!='\0')
1194 for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
1197 /* get the symbol */
1200 sym=findglb(name,sSTATEVAR);
1202 sym->usage |= uREAD;
1203 if (sym->ident==iVARIABLE || sym->ident==iREFERENCE
1204 || sym->ident==iARRAY || sym->ident==iREFARRAY)
1205 sym->usage |= uWRITTEN;
1207 error(17,name); /* undefined symbol */
1209 /* see if a comma follows the name */
1210 while (*lptr<=' ' && *lptr!='\0')
1212 comma= (*lptr==',');
1217 error(207); /* unknown #pragma */
1220 error(207); /* unknown #pragma */
1237 /* write opcode to output file */
1240 while (*lptr<=' ' && *lptr!='\0')
1242 for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++)
1243 name[i]=(char)tolower(*lptr);
1248 code_idx+=opcodes(1);
1249 /* write parameter (if any) */
1250 while (*lptr<=' ' && *lptr!='\0')
1259 code_idx+=opargs(1);
1264 sym=findglb(str,sSTATEVAR);
1265 if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) {
1266 error(17,str); /* undefined symbol */
1268 outval(sym->addr,FALSE);
1269 /* mark symbol as "used", unknown whether for read or write */
1270 markusage(sym,uREAD | uWRITTEN);
1271 code_idx+=opargs(1);
1276 extern char *sc_tokens[];/* forward declaration */
1278 sprintf(s2,"%c",(char)tok);
1280 strcpy(s2,sc_tokens[tok-tFIRST]);
1281 error(1,sc_tokens[tSYMBOL-tFIRST],s2);
1291 #if !defined NO_DEFINE
1295 char *pattern,*substitution;
1296 const unsigned char *start,*end;
1297 int count,prefixlen;
1299 /* find the pattern to match */
1300 while (*lptr<=' ' && *lptr!='\0')
1302 start=lptr; /* save starting point of the match pattern */
1304 while (*lptr>' ' && *lptr!='\0') {
1305 litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */
1309 /* check pattern to match */
1310 if (!alpha(*start)) {
1311 error(74); /* pattern must start with an alphabetic character */
1314 /* store matched pattern */
1315 pattern=(char*)malloc(count+1);
1317 error(123); /* insufficient memory */
1322 assert(*lptr!='\0');
1323 pattern[count++]=(char)litchar(&lptr,0);
1325 pattern[count]='\0';
1326 /* special case, erase trailing variable, because it could match anything */
1327 if (count>=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%')
1328 pattern[count-2]='\0';
1329 /* find substitution string */
1330 while (*lptr<=' ' && *lptr!='\0')
1332 start=lptr; /* save starting point of the match pattern */
1335 while (*lptr!='\0') {
1336 /* keep position of the start of trailing whitespace */
1348 /* store matched substitution */
1349 substitution=(char*)malloc(count+1); /* +1 for '\0' */
1350 if (substitution==NULL)
1351 error(123); /* insufficient memory */
1356 assert(*lptr!='\0');
1357 substitution[count++]=*lptr++;
1359 substitution[count]='\0';
1360 /* check whether the definition already exists */
1361 for (prefixlen=0,start=(unsigned char*)pattern; alphanum(*start); prefixlen++,start++)
1363 assert(prefixlen>0);
1364 if ((def=find_subst(pattern,prefixlen))!=NULL) {
1365 if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0)
1366 error(201,pattern); /* redefinition of macro (non-identical) */
1367 delete_subst(pattern,prefixlen);
1369 /* add the pattern/substitution pair to the list */
1370 assert(strlen(pattern)>0);
1371 insert_subst(pattern,substitution,prefixlen);
1379 if (lex(&val,&str)==tSYMBOL) {
1380 ret=delete_subst(str,strlen(str));
1382 /* also undefine normal constants */
1383 symbol *sym=findconst(str,NULL);
1384 if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) {
1385 delete_symbol(&glbtab,sym);
1390 error(17,str); /* undefined symbol */
1392 error(20,str); /* invalid symbol name */
1399 while (*lptr<=' ' && *lptr!='\0')
1402 error(131,lptr); /* user error */
1405 error(31); /* unknown compiler directive */
1406 ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* process as normal line */
1411 #if !defined NO_DEFINE
1412 static int is_startstring(const unsigned char *string)
1414 if (*string=='\"' || *string=='\'')
1415 return TRUE; /* "..." */
1419 if (*string=='\"' || *string=='\'')
1420 return TRUE; /* !"..." */
1421 if (*string==sc_ctrlchar) {
1423 if (*string=='\"' || *string=='\'')
1424 return TRUE; /* !\"..." */
1426 } else if (*string==sc_ctrlchar) {
1428 if (*string=='\"' || *string=='\'')
1429 return TRUE; /* \"..." */
1432 if (*string=='\"' || *string=='\'')
1433 return TRUE; /* \!"..." */
1440 static const unsigned char *skipstring(const unsigned char *string)
1445 while (*string=='!' || *string==sc_ctrlchar) {
1446 if (*string==sc_ctrlchar)
1452 assert(endquote=='"' || endquote=='\'');
1453 string++; /* skip open quote */
1454 while (*string!=endquote && *string!='\0')
1455 litchar(&string,flags);
1459 static const unsigned char *skippgroup(const unsigned char *string)
1480 close='\0'; /* only to avoid a compiler warning */
1484 while (*string!=close || nest>0) {
1487 else if (*string==close)
1489 else if (is_startstring(string))
1490 string=skipstring(string);
1498 static char *strdel(char *str,size_t len)
1500 size_t length=strlen(str);
1503 memmove(str, str+len, length-len+1); /* include EOS byte */
1507 static char *strins(char *dest,char *src,size_t srclen)
1509 size_t destlen=strlen(dest);
1510 assert(srclen<=strlen(src));
1511 memmove(dest+srclen, dest, destlen+1);/* include EOS byte */
1512 memcpy(dest, src, srclen);
1516 static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution)
1519 const unsigned char *p,*s,*e;
1520 unsigned char *args[10];
1521 int match,arg,len,argsnum=0;
1523 memset(args,0,sizeof args);
1525 /* check the length of the prefix */
1526 for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++)
1528 assert(prefixlen>0);
1529 assert(strncmp((char*)line,pattern,prefixlen)==0);
1531 /* pattern prefix matches; match the rest of the pattern, gather
1535 p=(unsigned char*)pattern+prefixlen;
1536 match=TRUE; /* so far, pattern matches */
1537 while (match && *s!='\0' && *p!='\0') {
1542 assert(arg>=0 && arg<=9);
1543 p++; /* skip parameter id */
1545 /* match the source string up to the character after the digit
1546 * (skipping strings in the process
1549 while (*e!=*p && *e!='\0' && *e!='\n') {
1550 if (is_startstring(e)) /* skip strings */
1552 else if (strchr("({[",*e)!=NULL) /* skip parenthized groups */
1555 e++; /* skip non-alphapetic character (or closing quote of
1556 * a string, or the closing paranthese of a group) */
1558 /* store the parameter (overrule any earlier) */
1559 if (args[arg]!=NULL)
1564 args[arg]=(unsigned char*)malloc(len+1);
1565 if (args[arg]==NULL)
1566 error(123); /* insufficient memory */
1567 strlcpy((char*)args[arg],(char*)s,len+1);
1568 /* character behind the pattern was matched too */
1571 } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
1572 s=e; /* allow a trailing ; in the pattern match to end of line */
1574 assert(*e=='\0' || *e=='\n');
1582 } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
1583 /* source may be ';' or end of the line */
1584 while (*s<=' ' && *s!='\0')
1585 s++; /* skip white space */
1586 if (*s!=';' && *s!='\0')
1588 p++; /* skip the semicolon in the pattern */
1591 /* skip whitespace between two non-alphanumeric characters, except
1592 * for two identical symbols
1594 assert((char*)p>pattern);
1595 if (!alphanum(*p) && *(p-1)!=*p)
1596 while (*s<=' ' && *s!='\0')
1597 s++; /* skip white space */
1598 ch=litchar(&p,0); /* this increments "p" */
1602 s++; /* this character matches */
1606 if (match && *p=='\0') {
1607 /* if the last character to match is an alphanumeric character, the
1608 * current character in the source may not be alphanumeric
1610 assert(p>(unsigned char*)pattern);
1611 if (alphanum(*(p-1)) && alphanum(*s))
1616 /* calculate the length of the substituted string */
1617 for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) {
1618 if (*e=='%' && isdigit(*(e+1)) && argsnum) {
1620 assert(arg>=0 && arg<=9);
1621 if (args[arg]!=NULL) {
1622 len+=strlen((char*)args[arg]);
1631 /* check length of the string after substitution */
1632 if (strlen((char*)line) + len - (int)(s-line) > buffersize) {
1633 error(75); /* line too long */
1635 /* substitute pattern */
1636 strdel((char*)line,(int)(s-line));
1637 for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) {
1638 if (*e=='%' && isdigit(*(e+1))) {
1640 assert(arg>=0 && arg<=9);
1641 if (args[arg]!=NULL) {
1642 strins((char*)s,(char*)args[arg],strlen((char*)args[arg]));
1643 s+=strlen((char*)args[arg]);
1646 error(236); /* parameter does not exist, incorrect #define pattern */
1647 strins((char*)s,(char*)e,2);
1650 e++; /* skip %, digit is skipped later */
1651 } else if (*e == '"') {
1653 if (is_startstring(e)) {
1655 strins((char*)s,(char *)p,(e-p+1));
1658 strins((char*)s,(char*)e,1);
1662 strins((char*)s,(char*)e,1);
1669 for (arg=0; arg<10; arg++)
1670 if (args[arg]!=NULL)
1676 static void substallpatterns(unsigned char *line,int buffersize)
1678 unsigned char *start, *end;
1683 while (*start!='\0') {
1684 /* find the start of a prefix (skip all non-alphabetic characters),
1687 while (!alpha(*start) && *start!='\0') {
1689 if (is_startstring(start)) {
1690 start=(unsigned char *)skipstring(start);
1692 break; /* abort loop on error */
1694 start++; /* skip non-alphapetic character (or closing quote of a string) */
1697 break; /* abort loop on error */
1698 /* if matching the operator "defined", skip it plus the symbol behind it */
1699 if (strncmp((char*)start,"defined",7)==0 && !isalpha((char)*(start+7))) {
1700 start+=7; /* skip "defined" */
1701 /* skip white space & parantheses */
1702 while (*start<=' ' && *start!='\0' || *start=='(')
1704 /* skip the symbol behind it */
1705 while (alphanum(*start))
1707 /* drop back into the main loop */
1710 /* get the prefix (length), look for a matching definition */
1713 while (alphanum(*end)) {
1717 assert(prefixlen>0);
1718 subst=find_subst((char*)start,prefixlen);
1720 /* properly match the pattern and substitute */
1721 if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second))
1722 start=end; /* match failed, skip this prefix */
1723 /* match succeeded: do not update "start", because the substitution text
1724 * may be matched by other macros
1727 start=end; /* no macro with this prefix, skip this prefix */
1735 * Reads a line by readline() into "pline" and performs basic preprocessing:
1736 * deleting comments, skipping lines with false "#if.." code and recognizing
1737 * other compiler directives. There is an indirect recursion: lex() calls
1738 * preprocess() if a new line must be read, preprocess() calls command(),
1739 * which at his turn calls lex() to identify the token.
1741 * Global references: lptr (altered)
1743 * freading (referred to only)
1745 SC_FUNC void preprocess(void)
1753 stripcom(pline); /* ??? no need for this when reading back from list file (in the second pass) */
1754 lptr=pline; /* set "line pointer" to start of the parsing buffer */
1755 iscommand=command();
1756 if (iscommand!=CMD_NONE)
1757 errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */
1758 #if !defined NO_DEFINE
1759 if (iscommand==CMD_NONE) {
1760 assert(lptr!=term_expr);
1761 substallpatterns(pline,sLINEMAX);
1762 lptr=pline; /* reset "line pointer" to start of the parsing buffer */
1765 if (sc_status==statFIRST && sc_listing && freading
1766 && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE))
1769 if (fline!=listline) {
1771 setlinedirect(fline);
1773 if (iscommand==CMD_EMPTYLINE)
1774 pc_writeasm(outf,"\n");
1776 pc_writeasm(outf,(char*)pline);
1778 } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */
1781 static const unsigned char *unpackedstring(const unsigned char *lptr,int flags)
1783 while (*lptr!='\"' && *lptr!='\0') {
1784 if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */
1788 litadd(litchar(&lptr,flags | UTF8MODE)); /* litchar() alters "lptr" */
1790 litadd(0); /* terminate string */
1794 static const unsigned char *packedstring(const unsigned char *lptr,int flags)
1799 i=0; /* start at least significant byte */
1802 while (*lptr!='\"' && *lptr!='\0') {
1803 if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */
1807 c=litchar(&lptr,flags); /* litchar() alters "lptr" */
1808 if (c>=(ucell)(1 << sCHARBITS))
1809 error(43); /* character constant exceeds range */
1812 if (i==sizeof(ucell)-(sCHARBITS/8)) {
1820 /* save last code; make sure there is at least one terminating zero character */
1822 litadd(val); /* at least one zero character in "val" */
1824 litadd(0); /* add full cell of zeros */
1828 /* lex(lexvalue,lexsym) Lexical Analysis
1830 * lex() first deletes leading white space, then checks for multi-character
1831 * operators, keywords (including most compiler directives), numbers,
1832 * labels, symbols and literals (literal characters are converted to a number
1833 * and are returned as such). If every check fails, the line must contain
1834 * a single-character operator. So, lex() returns this character. In the other
1835 * case (something did match), lex() returns the number of the token. All
1836 * these tokens have been assigned numbers above 255.
1838 * Some tokens have "attributes":
1839 * tNUMBER the value of the number is return in "lexvalue".
1840 * tRATIONAL the value is in IEEE 754 encoding or in fixed point
1841 * encoding in "lexvalue".
1842 * tSYMBOL the first sNAMEMAX characters of the symbol are
1843 * stored in a buffer, a pointer to this buffer is
1844 * returned in "lexsym".
1845 * tLABEL the first sNAMEMAX characters of the label are
1846 * stored in a buffer, a pointer to this buffer is
1847 * returned in "lexsym".
1848 * tSTRING the string is stored in the literal pool, the index
1849 * in the literal pool to this string is stored in
1852 * lex() stores all information (the token found and possibly its attribute)
1853 * in global variables. This allows a token to be examined twice. If "_pushed"
1854 * is true, this information is returned.
1856 * Global references: lptr (altered)
1857 * fline (referred to only)
1858 * litidx (referred to only)
1859 * _lextok, _lexval, _lexstr
1865 static cell _lexval;
1866 static char _lexstr[sLINEMAX+1];
1867 static int _lexnewline;
1869 SC_FUNC void lexinit(void)
1871 stkidx=0; /* index for pushstk() and popstk() */
1872 iflevel=0; /* preprocessor: nesting of "#if" is currently 0 */
1873 skiplevel=0; /* preprocessor: not currently skipping */
1874 icomment=0; /* currently not in a multiline comment */
1875 _pushed=FALSE; /* no token pushed back into lex */
1879 char *sc_tokens[] = {
1880 "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=",
1881 "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--",
1883 "assert", "*begin", "break", "case", "cellsof", "chars", "const", "continue", "default",
1884 "defined", "do", "else", "*end", "enum", "exit", "for", "forward", "funcenum", "functag", "goto",
1885 "if", "native", "new", "decl", "operator", "public", "return", "sizeof",
1886 "sleep", "state", "static", "stock", "struct", "switch", "tagof", "*then", "while",
1887 "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput",
1888 "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma",
1889 "#tryinclude", "#undef",
1890 ";", ";", "-integer value-", "-rational value-", "-identifier-",
1891 "-label-", "-string-"
1894 SC_FUNC int lex(cell *lexvalue,char **lexsym)
1896 int i,toolong,newline,stringflags;
1898 const unsigned char *starttoken;
1901 _pushed=FALSE; /* reset "_pushed" flag */
1907 _lextok=0; /* preset all values */
1916 newline= (lptr==pline); /* does lptr point to start of line buffer */
1917 while (*lptr<=' ') { /* delete leading white space */
1919 preprocess(); /* preprocess resets "lptr" */
1922 if (lptr==term_expr) /* special sequence to terminate a pending expression */
1923 return (_lextok=tENDEXPR);
1924 _lexnewline=TRUE; /* set this after preprocess(), because
1925 * preprocess() calls lex() recursively */
1933 for (i=0; i<(int)(lptr-pline); i++)
1934 if (pline[i]=='\t' && sc_tabsize>0)
1935 stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
1942 while (i<=tMIDDLE) { /* match multi-character operators */
1943 if (*lptr==**tokptr && match(*tokptr,FALSE)) {
1945 if (pc_docexpr) /* optionally concatenate to documentation string */
1946 insert_autolist(*tokptr);
1952 while (i<=tLAST) { /* match reserved words and compiler directives */
1953 if (*lptr==**tokptr && match(*tokptr,TRUE)) {
1955 errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
1956 if (pc_docexpr) /* optionally concatenate to documentation string */
1957 insert_autolist(*tokptr);
1964 starttoken=lptr; /* save start pointer (for concatenating to documentation string) */
1965 if ((i=number(&_lexval,lptr))!=0) { /* number */
1969 } else if ((i=ftoi(&_lexval,lptr))!=0) {
1973 } else if (alpha(*lptr)) { /* symbol or label */
1974 /* Note: only sNAMEMAX characters are significant. The compiler
1975 * generates a warning if a symbol exceeds this length.
1980 while (alphanum(*lptr)){
1990 error(200,_lexstr,sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */
1991 if (_lexstr[0]==PUBLIC_CHAR && _lexstr[1]=='\0') {
1992 _lextok=PUBLIC_CHAR; /* '@' all alone is not a symbol, it is an operator */
1993 } else if (_lexstr[0]=='_' && _lexstr[1]=='\0') {
1994 _lextok='_'; /* '_' by itself is not a symbol, it is a placeholder */
1996 if (*lptr==':' && *(lptr+1)!=':' && _lextok!=PUBLIC_CHAR) {
1998 _lextok=tLABEL; /* it wasn't a normal symbol, it was a label/tagname */
1999 lptr+=1; /* skip colon */
2000 } else if (find_constval(&tagname_tab,_lexstr,0)!=NULL) {
2001 /* this looks like a tag override (because a tag with this name
2002 * exists), but tags are not allowed right now, so it is probably an
2008 } else if (*lptr=='\"' || *lptr==sc_ctrlchar && *(lptr+1)=='\"')
2009 { /* unpacked string literal */
2011 stringflags= (*lptr==sc_ctrlchar) ? RAWMODE : 0;
2012 *lexvalue=_lexval=litidx;
2013 lptr+=1; /* skip double quote */
2014 if ((stringflags & RAWMODE)!=0)
2015 lptr+=1; /* skip "escape" character too */
2016 /* Note that this should always be packedstring() for SourcePawn */
2017 lptr=sc_packstr ? packedstring(lptr,stringflags) : unpackedstring(lptr,stringflags);
2019 lptr+=1; /* skip final quote */
2021 error(37); /* invalid (non-terminated) string */
2022 } else if (*lptr=='!' && *(lptr+1)=='\"'
2023 || *lptr=='!' && *(lptr+1)==sc_ctrlchar && *(lptr+2)=='\"'
2024 || *lptr==sc_ctrlchar && *(lptr+1)=='!' && *(lptr+2)=='\"')
2025 { /* packed string literal */
2027 stringflags= (*lptr==sc_ctrlchar || *(lptr+1)==sc_ctrlchar) ? RAWMODE : 0;
2028 *lexvalue=_lexval=litidx;
2029 lptr+=2; /* skip exclamation point and double quote */
2030 if ((stringflags & RAWMODE)!=0)
2031 lptr+=1; /* skip "escape" character too */
2032 lptr=sc_packstr ? unpackedstring(lptr,stringflags) : packedstring(lptr,stringflags);
2034 lptr+=1; /* skip final quote */
2036 error(37); /* invalid (non-terminated) string */
2037 } else if (*lptr=='\'') { /* character literal */
2038 lptr+=1; /* skip quote */
2040 *lexvalue=_lexval=litchar(&lptr,UTF8MODE);
2042 lptr+=1; /* skip final quote */
2044 error(27); /* invalid character constant (must be one character) */
2045 } else if (*lptr==';') { /* semicolumn resets "error" flag */
2048 errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
2050 _lextok=*lptr; /* if every match fails, return the character */
2051 lptr+=1; /* increase the "lptr" pointer */
2054 if (pc_docexpr) { /* optionally concatenate to documentation string */
2055 char *docstr=(char*)malloc(((int)(lptr-starttoken)+1)*sizeof(char));
2057 strlcpy(docstr,(char*)starttoken,(int)(lptr-starttoken)+1);
2058 insert_autolist(docstr);
2067 * Pushes a token back, so the next call to lex() will return the token
2068 * last examined, instead of a new token.
2070 * Only one token can be pushed back.
2072 * In fact, lex() already stores the information it finds into global
2073 * variables, so all that is to be done is set a flag that informs lex()
2074 * to read and return the information from these variables, rather than
2075 * to read in a new token from the input file.
2077 SC_FUNC void lexpush(void)
2079 assert(_pushed==FALSE);
2085 * Sets the variable "_pushed" to 0 to make sure lex() will read in a new
2086 * symbol (a not continue with some old one). This is required upon return
2087 * from Assembler mode, and in a few cases after detecting an syntax error.
2089 SC_FUNC void lexclr(int clreol)
2093 lptr=(unsigned char*)strchr((char*)pline,'\0');
2100 * This routine is useful if only a simple check is needed. If the token
2101 * differs from the one expected, it is pushed back.
2102 * This function returns 1 for "token found" and 2 for "implied statement
2103 * termination token" found --the statement termination is an end of line in
2104 * an expression where there is no pending operation. Such an implied token
2105 * (i.e. not present in the source code) should not be pushed back, which is
2106 * why it is sometimes important to distinguish the two.
2108 SC_FUNC int matchtoken(int token)
2115 if (tok==token || token==tTERM && (tok==';' || tok==tENDEXPR)) {
2117 } else if (!sc_needsemicolon && token==tTERM && (_lexnewline || !freading)) {
2118 /* Push "tok" back, because it is the token following the implicit statement
2119 * termination (newline) token.
2131 * Returns additional information of a token after using "matchtoken()"
2132 * or needtoken(). It does no harm using this routine after a call to
2133 * "lex()", but lex() already returns the same information.
2135 * The token itself is the return value. Normally, this one is already known.
2137 SC_FUNC int tokeninfo(cell *val,char **str)
2139 /* if the token was pushed back, tokeninfo() returns the token and
2140 * parameters of the *next* token, not of the *current* token.
2150 * This routine checks for a required token and gives an error message if
2151 * it isn't there (and returns 0/FALSE in that case). Like function matchtoken(),
2152 * this function returns 1 for "token found" and 2 for "statement termination
2153 * token" found; see function matchtoken() for details.
2155 * Global references: _lextok;
2157 SC_FUNC int needtoken(int token)
2162 if ((t=matchtoken(token))!=0) {
2165 /* token already pushed back */
2168 sprintf(s1,"%c",(char)token); /* single character token */
2170 strcpy(s1,sc_tokens[token-tFIRST]); /* multi-character symbol */
2172 strcpy(s2,"-end of file-");
2173 else if (_lextok<256)
2174 sprintf(s2,"%c",(char)_lextok);
2176 strcpy(s2,sc_tokens[_lextok-tFIRST]);
2177 error(1,s1,s2); /* expected ..., but found ... */
2184 * Compares a series of characters from the input file with the characters
2185 * in "st" (that contains a token). If the token on the input file matches
2186 * "st", the input file pointer "lptr" is adjusted to point to the next
2187 * token, otherwise "lptr" remains unaltered.
2189 * If the parameter "end: is true, match() requires that the first character
2190 * behind the recognized token is non-alphanumeric.
2192 * Global references: lptr (altered)
2194 static int match(char *st,int end)
2197 const unsigned char *ptr;
2202 if ((unsigned char)st[k]!=*ptr)
2207 if (end) { /* symbol must terminate with non-alphanumeric char */
2211 lptr=ptr; /* match found, skip symbol */
2215 static void chk_grow_litq(void)
2217 if (litidx>=litmax) {
2220 litmax+=sDEF_LITMAX;
2221 p=(cell *)realloc(litq,litmax*sizeof(cell));
2223 error(122,"literal table"); /* literal table overflow (fatal error) */
2230 * Adds a value at the end of the literal queue. The literal queue is used
2231 * for literal strings used in functions and for initializing array variables.
2233 * Global references: litidx (altered)
2236 SC_FUNC void litadd(cell value)
2239 assert(litidx<litmax);
2240 litq[litidx++]=value;
2245 * Inserts a value into the literal queue. This is sometimes necessary for
2246 * initializing multi-dimensional arrays.
2248 * Global references: litidx (altered)
2251 SC_FUNC void litinsert(cell value,int pos)
2254 assert(litidx<litmax);
2255 assert(pos>=0 && pos<=litidx);
2256 memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell));
2263 * Return current literal character and increase the pointer to point
2264 * just behind this literal character.
2266 * Note: standard "escape sequences" are suported, but the backslash may be
2267 * replaced by another character; the syntax '\ddd' is supported,
2268 * but ddd must be decimal!
2270 static cell litchar(const unsigned char **lptr,int flags)
2273 const unsigned char *cptr;
2276 if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) { /* no escape character */
2277 #if !defined NO_UTF8
2278 if (sc_is_utf8 && (flags & UTF8MODE)!=0) {
2279 c=get_utf8_char(cptr,&cptr);
2280 assert(c>=0); /* file was already scanned for conformance to UTF-8 */
2283 #if !defined NO_CODEPAGE
2284 c=cp_translate(cptr,&cptr);
2289 #if !defined NO_UTF8
2294 if (*cptr==sc_ctrlchar) {
2295 c=*cptr; /* \\ == \ (the escape character itself) */
2299 case 'a': /* \a == audible alarm */
2303 case 'b': /* \b == backspace */
2307 case 'e': /* \e == escape */
2311 case 'f': /* \f == form feed */
2315 case 'n': /* \n == NewLine character */
2319 case 'r': /* \r == carriage return */
2323 case 't': /* \t == horizontal TAB */
2327 case 'v': /* \v == vertical TAB */
2336 while (ishex(*cptr) && digits < 2) {
2338 c=(c<<4)+(*cptr-'0');
2340 c=(c<<4)+(tolower(*cptr)-'a'+10);
2345 cptr++; /* swallow a trailing ';' */
2348 case '\'': /* \' == ' (single quote) */
2349 case '"': /* \" == " (single quote) */
2350 case '%': /* \% == % (percent) */
2355 if (isdigit(*cptr)) { /* \ddd */
2357 while (*cptr>='0' && *cptr<='9') /* decimal! */
2358 c=c*10 + *cptr++ - '0';
2360 cptr++; /* swallow a trailing ';' */
2362 error(27); /* invalid character constant */
2374 * Test if character "c" is alphabetic ("a".."z"), an underscore ("_")
2375 * or an "at" sign ("@"). The "@" is an extension to standard C.
2377 static int alpha(char c)
2379 return (isalpha(c) || c=='_' || c==PUBLIC_CHAR);
2384 * Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@")
2386 SC_FUNC int alphanum(char c)
2388 return (alpha(c) || isdigit(c));
2393 * Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f").
2395 SC_FUNC int ishex(char c)
2397 return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
2400 /* The local variable table must be searched backwards, so that the deepest
2401 * nesting of local variables is searched first. The simplest way to do
2402 * this is to insert all new items at the head of the list.
2403 * In the global list, the symbols are kept in sorted order, so that the
2404 * public functions are written in sorted order.
2406 static symbol *add_symbol(symbol *root,symbol *entry,int sort)
2411 while (root->next!=NULL && strcmp(entry->name,root->next->name)>0)
2414 if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) {
2418 memcpy(newsym,entry,sizeof(symbol));
2419 newsym->next=root->next;
2424 static void free_symbol(symbol *sym)
2428 /* free all sub-symbol allocated memory blocks, depending on the
2429 * kind of the symbol
2432 if (sym->ident==iFUNCTN) {
2433 /* run through the argument list; "default array" arguments
2434 * must be freed explicitly; the tag list must also be freed */
2435 assert(sym->dim.arglist!=NULL);
2436 for (arg=sym->dim.arglist; arg->ident!=0; arg++) {
2437 if (arg->ident==iREFARRAY && arg->hasdefault)
2438 free(arg->defvalue.array.data);
2439 else if (arg->ident==iVARIABLE
2440 && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0))
2441 free(arg->defvalue.size.symname);
2442 assert(arg->tags!=NULL);
2445 free(sym->dim.arglist);
2446 if (sym->states!=NULL) {
2447 delete_consttable(sym->states);
2450 } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
2451 if (sym->states!=NULL) {
2452 delete_consttable(sym->states);
2455 } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) {
2456 /* free the constant list of an enum root */
2457 assert(sym->dim.enumlist!=NULL);
2458 delete_consttable(sym->dim.enumlist);
2459 free(sym->dim.enumlist);
2461 assert(sym->refer!=NULL);
2463 if (sym->documentation!=NULL)
2464 free(sym->documentation);
2468 SC_FUNC void delete_symbol(symbol *root,symbol *sym)
2470 /* find the symbol and its predecessor
2471 * (this function assumes that you will never delete a symbol that is not
2472 * in the table pointed at by "root")
2475 while (root->next!=sym) {
2480 /* unlink it, then free it */
2481 root->next=sym->next;
2485 SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions)
2487 symbol *sym,*parent_sym;
2488 constvalue *stateptr;
2491 /* erase only the symbols with a deeper nesting level than the
2492 * specified nesting level */
2493 while (root->next!=NULL) {
2495 if (sym->compound<level)
2497 switch (sym->ident) {
2499 mustdelete=delete_labels;
2503 /* do not delete global variables if functions are preserved */
2504 mustdelete=delete_functions;
2507 /* always delete references (only exist as function parameters) */
2511 /* a global iREFARRAY symbol is the return value of a function: delete
2512 * this only if "globals" must be deleted; other iREFARRAY instances
2513 * (locals) are also deleted
2515 mustdelete=delete_functions;
2516 for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent)
2517 assert(parent_sym->ident==iREFARRAY);
2518 assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL));
2519 if (parent_sym==NULL || parent_sym->ident!=iFUNCTN)
2523 /* delete constants, except predefined constants */
2524 mustdelete=delete_functions || (sym->usage & uPREDEF)==0;
2527 /* optionally preserve globals (variables & functions), but
2528 * NOT native functions
2530 mustdelete=delete_functions || (sym->usage & uNATIVE)!=0;
2531 assert(sym->parent==NULL);
2542 root->next=sym->next;
2545 /* if the function was prototyped, but not implemented in this source,
2546 * mark it as such, so that its use can be flagged
2548 if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0)
2549 sym->usage |= uMISSING;
2550 if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY)
2551 sym->usage &= ~uDEFINE; /* clear "defined" flag */
2552 /* set all states as "undefined" too */
2553 if (sym->states!=NULL)
2554 for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next)
2556 /* for user defined operators, also remove the "prototyped" flag, as
2557 * user-defined operators *must* be declared before use
2559 if (sym->ident==iFUNCTN && !alpha(*sym->name))
2560 sym->usage &= ~uPROTOTYPED;
2561 root=sym; /* skip the symbol */
2566 /* The purpose of the hash is to reduce the frequency of a "name"
2567 * comparison (which is costly). There is little interest in avoiding
2568 * clusters in similar names, which is why this function is plain simple.
2570 SC_FUNC uint32_t namehash(const char *name)
2572 const unsigned char *ptr=(const unsigned char *)name;
2573 int len=strlen(name);
2577 return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]);
2580 static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag)
2582 symbol *firstmatch=NULL;
2583 symbol *sym=root->next;
2585 unsigned long hash=namehash(name);
2587 if (hash==sym->hash && strcmp(name,sym->name)==0 /* check name */
2588 && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */
2589 && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */
2591 assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */
2592 if (sym->ident==iFUNCTN
2593 || automaton<0 && sym->states==NULL
2594 || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton)
2597 return sym; /* return first match */
2598 /* return closest match or first match; count number of matches */
2599 if (firstmatch==NULL)
2601 assert(cmptag!=NULL);
2604 if (*cmptag==sym->tag) {
2605 *cmptag=1; /* good match found, set number of matches to 1 */
2612 if (cmptag!=NULL && firstmatch!=NULL)
2617 static symbol *find_symbol_child(const symbol *root,const symbol *sym)
2619 symbol *ptr=root->next;
2621 if (ptr->parent==sym)
2628 /* Adds "bywhom" to the list of referrers of "entry". Typically,
2629 * bywhom will be the function that uses a variable or that calls
2632 SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom)
2636 assert(bywhom!=NULL); /* it makes no sense to add a "void" referrer */
2637 assert(entry!=NULL);
2638 assert(entry->refer!=NULL);
2640 /* see if it is already there */
2641 for (count=0; count<entry->numrefers && entry->refer[count]!=bywhom; count++)
2643 if (count<entry->numrefers) {
2644 assert(entry->refer[count]==bywhom);
2648 /* see if there is an empty spot in the referrer list */
2649 for (count=0; count<entry->numrefers && entry->refer[count]!=NULL; count++)
2651 assert(count <= entry->numrefers);
2652 if (count==entry->numrefers) {
2654 int newsize=2*entry->numrefers;
2656 /* grow the referrer list */
2657 refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*));
2659 return FALSE; /* insufficient memory */
2660 /* initialize the new entries */
2662 for (count=entry->numrefers; count<newsize; count++)
2663 entry->refer[count]=NULL;
2664 count=entry->numrefers; /* first empty spot */
2665 entry->numrefers=newsize;
2668 /* add the referrer */
2669 assert(entry->refer[count]==NULL);
2670 entry->refer[count]=bywhom;
2674 SC_FUNC void markusage(symbol *sym,int usage)
2677 sym->usage |= (char)usage;
2678 if ((usage & uWRITTEN)!=0)
2680 /* check if (global) reference must be added to the symbol */
2681 if ((usage & (uREAD | uWRITTEN))!=0) {
2682 /* only do this for global symbols */
2683 if (sym->vclass==sGLOBAL) {
2684 /* "curfunc" should always be valid, since statements may not occurs
2685 * outside functions; in the case of syntax errors, however, the
2686 * compiler may arrive through this function
2689 refer_symbol(sym,curfunc);
2697 * Returns a pointer to the global symbol (if found) or NULL (if not found)
2699 SC_FUNC symbol *findglb(const char *name,int filter)
2701 /* find a symbol with a matching automaton first */
2704 if (filter>sGLOBAL && sc_curstates>0) {
2705 /* find a symbol whose state list matches the current fsa */
2706 sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL);
2707 if (sym!=NULL && sym->ident!=iFUNCTN) {
2708 /* if sym!=NULL, we found a variable in the automaton; now we should
2709 * also verify whether there is an intersection between the symbol's
2710 * state list and the current state list
2712 assert(sym->states!=NULL && sym->states->next!=NULL);
2713 if (!state_conflict_id(sc_curstates,sym->states->next->index))
2718 /* if no symbol with a matching automaton exists, find a variable/function
2719 * that has no state(s) attached to it
2722 sym=find_symbol(&glbtab,name,fcurrent,-1,NULL);
2728 * Returns a pointer to the local symbol (if found) or NULL (if not found).
2729 * See add_symbol() how the deepest nesting level is searched first.
2731 SC_FUNC symbol *findloc(const char *name)
2733 return find_symbol(&loctab,name,-1,-1,NULL);
2736 SC_FUNC symbol *findconst(const char *name,int *cmptag)
2740 sym=find_symbol(&loctab,name,-1,-1,cmptag); /* try local symbols first */
2741 if (sym==NULL || sym->ident!=iCONSTEXPR) /* not found, or not a constant */
2742 sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag);
2743 if (sym==NULL || sym->ident!=iCONSTEXPR)
2745 assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0);
2746 /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */
2750 SC_FUNC symbol *finddepend(const symbol *parent)
2754 sym=find_symbol_child(&loctab,parent); /* try local symbols first */
2755 if (sym==NULL) /* not found */
2756 sym=find_symbol_child(&glbtab,parent);
2762 * Adds a symbol to the symbol table (either global or local variables,
2763 * or global and local constants).
2765 SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage)
2767 symbol entry, **refer;
2769 /* labels may only be defined once */
2770 assert(ident!=iLABEL || findloc(name)==NULL);
2772 /* create an empty referrer list */
2773 if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) {
2774 error(123); /* insufficient memory */
2779 /* first fill in the entry */
2780 memset(&entry,0,sizeof entry);
2781 strcpy(entry.name,name);
2782 entry.hash=namehash(name);
2784 entry.codeaddr=code_idx;
2785 entry.vclass=(char)vclass;
2786 entry.ident=(char)ident;
2788 entry.usage=(char)usage;
2789 entry.fnumber=-1; /* assume global visibility (ignored for local symbols) */
2790 entry.lnumber=fline;
2794 /* then insert it in the list */
2795 if (vclass==sGLOBAL)
2796 return add_symbol(&glbtab,&entry,TRUE);
2798 return add_symbol(&loctab,&entry,FALSE);
2801 SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag,
2802 int dim[],int numdim,int idxtag[])
2804 return addvariable2(name,addr,ident,vclass,tag,dim,numdim,idxtag,0);
2807 SC_FUNC symbol *addvariable2(const char *name,cell addr,int ident,int vclass,int tag,
2808 int dim[],int numdim,int idxtag[],int slength)
2812 /* global variables may only be defined once
2813 * One complication is that functions returning arrays declare an array
2814 * with the same name as the function, so the assertion must allow for
2815 * this special case. Another complication is that variables may be
2816 * "redeclared" if they are local to an automaton (and findglb() will find
2817 * the symbol without states if no symbol with states exists).
2819 assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0
2820 || sym->ident==iFUNCTN && sym==curfunc
2821 || sym->states==NULL && sc_curstates>0);
2823 if (ident==iARRAY || ident==iREFARRAY) {
2824 symbol *parent=NULL,*top;
2826 sym=NULL; /* to avoid a compiler warning */
2827 for (level=0; level<numdim; level++) {
2828 top=addsym(name,addr,ident,vclass,tag,uDEFINE);
2829 top->dim.array.length=dim[level];
2830 if (tag == pc_tag_string && level == numdim - 1) {
2832 top->dim.array.length=dim[level] * sizeof(cell);
2834 top->dim.array.slength=slength;
2836 top->dim.array.slength=0;
2838 top->dim.array.level=(short)(numdim-level-1);
2839 top->x.tags.index=idxtag[level];
2846 sym=addsym(name,addr,ident,vclass,tag,uDEFINE);
2853 * Returns te next internal label number. The global variable sc_labnum is
2854 * initialized to zero.
2856 SC_FUNC int getlabel(void)
2863 * Converts a number to a hexadecimal string and returns a pointer to that
2864 * string. This function is NOT re-entrant.
2866 SC_FUNC char *itoh(ucell val)
2868 static char itohstr[30];
2870 int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */
2873 #if PAWN_CELL_SIZE==16
2875 #elif PAWN_CELL_SIZE==32
2877 #elif PAWN_CELL_SIZE==64
2880 #error Unsupported cell size
2883 for (i=0; i<max; i+=1){
2884 nibble[i]=(int)(val & 0x0f); /* nibble 0 is lowest nibble */
2888 while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */
2892 *ptr++=(char)('a'+(nibble[i]-10));
2894 *ptr++=(char)('0'+nibble[i]);
2897 *ptr='\0'; /* and a zero-terminator */