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