1 /*
2 * File Tokeniser/Parser/Whatever
3 * by Jonathon Fowler
4 * Remixed completely by Ken Silverman
5 * See the included license file "BUILDLIC.TXT" for license info.
6 */
7
8 #include "build.h"
9 #include "scriptfile.h"
10 #include "cache1d.h"
11 #include <math.h>
12
13
14 #define ISWS(x) ((x == ' ') || (x == '\t') || (x == '\r') || (x == '\n'))
skipoverws(scriptfile * sf)15 static void skipoverws(scriptfile *sf) { if ((sf->textptr < sf->eof) && (!sf->textptr[0])) sf->textptr++; }
skipovertoken(scriptfile * sf)16 static void skipovertoken(scriptfile *sf) { while ((sf->textptr < sf->eof) && (sf->textptr[0])) sf->textptr++; }
17
scriptfile_gettoken(scriptfile * sf)18 char *scriptfile_gettoken(scriptfile *sf)
19 {
20 char *start;
21
22 skipoverws(sf);
23 if (sf->textptr >= sf->eof) return NULL;
24
25 start = sf->ltextptr = sf->textptr;
26 skipovertoken(sf);
27 return start;
28 }
29
scriptfile_peektoken(scriptfile * sf)30 char *scriptfile_peektoken(scriptfile *sf)
31 {
32 scriptfile dupe;
33
34 memcpy(&dupe, sf, sizeof(scriptfile));
35
36 skipoverws(&dupe);
37 if (dupe.textptr >= dupe.eof) return NULL;
38 return dupe.textptr;
39 }
40
scriptfile_getstring(scriptfile * sf,char ** retst)41 int scriptfile_getstring(scriptfile *sf, char **retst)
42 {
43 (*retst) = scriptfile_gettoken(sf);
44 if (*retst == NULL)
45 {
46 buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
47 return(-2);
48 }
49 return(0);
50 }
51
scriptfile_getnumber_radix(scriptfile * sf,int * num,int radix)52 static int scriptfile_getnumber_radix(scriptfile *sf, int *num, int radix)
53 {
54 skipoverws(sf);
55 if (sf->textptr >= sf->eof)
56 {
57 buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
58 return -1;
59 }
60
61 while ((sf->textptr[0] == '0') && (sf->textptr[1] >= '0') && (sf->textptr[1] <= '9'))
62 sf->textptr++; //hack to treat octal numbers like decimal
63
64 sf->ltextptr = sf->textptr;
65 (*num) = strtol((const char *)sf->textptr,&sf->textptr,radix);
66 if (!ISWS(*sf->textptr) && *sf->textptr) {
67 char *p = sf->textptr;
68 skipovertoken(sf);
69 buildprintf("Error on line %s:%d: expecting int, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),p);
70 return -2;
71 }
72 return 0;
73 }
74
scriptfile_getnumber(scriptfile * sf,int * num)75 int scriptfile_getnumber(scriptfile *sf, int *num)
76 {
77 return scriptfile_getnumber_radix(sf, num, 0);
78 }
79
scriptfile_gethex(scriptfile * sf,int * num)80 int scriptfile_gethex(scriptfile *sf, int *num)
81 {
82 return scriptfile_getnumber_radix(sf, num, 16);
83 }
84
parsedouble(char * ptr,char ** end)85 static double parsedouble(char *ptr, char **end)
86 {
87 int beforedecimal = 1, negative = 0, dig;
88 int exposgn = 0, expo = 0;
89 double num = 0.0, decpl = 0.1;
90 char *p;
91
92 p = ptr;
93 if (*p == '-') negative = 1, p++;
94 else if (*p == '+') p++;
95 for (;; p++) {
96 if (*p >= '0' && *p <= '9') {
97 dig = *p - '0';
98 if (beforedecimal) num = num * 10.0 + dig;
99 else if (exposgn) expo = expo*10 + dig;
100 else {
101 num += (double)dig * decpl;
102 decpl /= 10.0;
103 }
104 } else if (*p == '.') {
105 if (beforedecimal) beforedecimal = 0;
106 else break;
107 } else if ((*p == 'E') || (*p == 'e')) {
108 exposgn = 1;
109 if (p[1] == '-') { exposgn = -1; p++; }
110 else if (p[1] == '+') p++;
111 } else break;
112 }
113
114 if (end) *end = p;
115 if (exposgn) num *= pow(10.0,(double)(expo*exposgn));
116 return negative ? -num : num;
117 }
118
scriptfile_getdouble(scriptfile * sf,double * num)119 int scriptfile_getdouble(scriptfile *sf, double *num)
120 {
121 skipoverws(sf);
122 if (sf->textptr >= sf->eof)
123 {
124 buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
125 return -1;
126 }
127
128 sf->ltextptr = sf->textptr;
129
130 // On Linux, locale settings interfere with interpreting x.y format numbers
131 //(*num) = strtod((const char *)sf->textptr,&sf->textptr);
132 (*num) = parsedouble(sf->textptr, &sf->textptr);
133
134 if (!ISWS(*sf->textptr) && *sf->textptr) {
135 char *p = sf->textptr;
136 skipovertoken(sf);
137 buildprintf("Error on line %s:%d: expecting float, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),p);
138 return -2;
139 }
140 return 0;
141 }
142
scriptfile_getsymbol(scriptfile * sf,int * num)143 int scriptfile_getsymbol(scriptfile *sf, int *num)
144 {
145 char *t, *e;
146 int v;
147
148 t = scriptfile_gettoken(sf);
149 if (!t) return -1;
150
151 v = Bstrtol(t, &e, 10);
152 if (*e) {
153 // looks like a string, so find it in the symbol table
154 if (scriptfile_getsymbolvalue(t, num)) return 0;
155 buildprintf("Error on line %s:%d: expecting symbol, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),t);
156 return -2; // not found
157 }
158
159 *num = v;
160 return 0;
161 }
162
scriptfile_getbraces(scriptfile * sf,char ** braceend)163 int scriptfile_getbraces(scriptfile *sf, char **braceend)
164 {
165 int bracecnt;
166 char *bracestart;
167
168 skipoverws(sf);
169 if (sf->textptr >= sf->eof)
170 {
171 buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
172 return -1;
173 }
174
175 if (sf->textptr[0] != '{') {
176 buildprintf("Error on line %s:%d: expecting '{'\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
177 return -1;
178 }
179 bracestart = ++sf->textptr; bracecnt = 1;
180 while (1)
181 {
182 if (sf->textptr >= sf->eof) return(0);
183 if (sf->textptr[0] == '{') bracecnt++;
184 if (sf->textptr[0] == '}') { bracecnt--; if (!bracecnt) break; }
185 sf->textptr++;
186 }
187 (*braceend) = sf->textptr;
188 sf->textptr = bracestart;
189 return 0;
190 }
191
192
scriptfile_getlinum(scriptfile * sf,char * ptr)193 int scriptfile_getlinum (scriptfile *sf, char *ptr)
194 {
195 int i, stp, ind;
196
197 //for(i=0;i<sf->linenum;i++) if (sf->lineoffs[i] >= ind) return(i+1); //brute force algo
198
199 ind = ((intptr_t)ptr) - ((intptr_t)sf->textbuf);
200
201 for(stp=1;stp+stp<sf->linenum;stp+=stp); //stp = highest power of 2 less than sf->linenum
202 for(i=0;stp;stp>>=1)
203 if ((i+stp < sf->linenum) && (sf->lineoffs[i+stp] < ind)) i += stp;
204 return(i+1); //i = index to highest lineoffs which is less than ind; convert to 1-based line numbers
205 }
206
scriptfile_preparse(scriptfile * sf,char * tx,int flen)207 void scriptfile_preparse (scriptfile *sf, char *tx, int flen)
208 {
209 int i, cr, numcr, nflen, ws, cs, inquote;
210
211 //Count number of lines
212 numcr = 1;
213 for(i=0;i<flen;i++)
214 {
215 //detect all 4 types of carriage return (\r, \n, \r\n, \n\r :)
216 cr=0;if (tx[i] == '\r') { i += (tx[i+1] == '\n'); cr = 1; }
217 else if (tx[i] == '\n') { i += (tx[i+1] == '\r'); cr = 1; }
218 if (cr) { numcr++; continue; }
219 }
220
221 sf->linenum = numcr;
222 sf->lineoffs = (int *)malloc(sf->linenum*sizeof(int));
223
224 //Preprocess file for comments (// and /*...*/, and convert all whitespace to single spaces)
225 nflen = 0; ws = 0; cs = 0; numcr = 0; inquote = 0;
226 for(i=0;i<flen;i++)
227 {
228 //detect all 4 types of carriage return (\r, \n, \r\n, \n\r :)
229 cr=0;if (tx[i] == '\r') { i += (tx[i+1] == '\n'); cr = 1; }
230 else if (tx[i] == '\n') { i += (tx[i+1] == '\r'); cr = 1; }
231 if (cr)
232 {
233 //Remember line numbers by storing the byte index at the start of each line
234 //Line numbers can be retrieved by doing a binary search on the byte index :)
235 sf->lineoffs[numcr++] = nflen;
236 if (cs == 1) cs = 0;
237 ws = 1; continue; //strip CR/LF
238 }
239
240 if ((!inquote) && ((tx[i] == ' ') || (tx[i] == '\t'))) { ws = 1; continue; } //strip Space/Tab
241 if ((tx[i] == ';') && (!cs)) cs = 1; // ; comment
242 if ((tx[i] == '/') && (tx[i+1] == '/') && (!cs)) cs = 1;
243 if ((tx[i] == '/') && (tx[i+1] == '*') && (!cs)) { ws = 1; cs = 2; }
244 if ((tx[i] == '*') && (tx[i+1] == '/') && (cs == 2)) { cs = 0; i++; continue; }
245 if (cs) continue;
246
247 if (ws) { tx[nflen++] = 0; ws = 0; }
248
249 //quotes inside strings: \"
250 if ((tx[i] == '\\') && (tx[i+1] == '\"')) { i++; tx[nflen++] = '\"'; continue; }
251 if (tx[i] == '\"') { inquote ^= 1; continue; }
252 tx[nflen++] = tx[i];
253 }
254 tx[nflen++] = 0; sf->lineoffs[numcr] = nflen;
255 tx[nflen++] = 0;
256
257 #if 0
258 //for debugging only:
259 printf("pre-parsed file:flen=%d,nflen=%d\n",flen,nflen);
260 for(i=0;i<nflen;i++) { if (tx[i] < 32) printf("_"); else printf("%c",tx[i]); }
261 printf("[eof]\nnumlines=%d\n",sf->linenum);
262 for(i=0;i<sf->linenum;i++) printf("line %d = byte %d\n",i,sf->lineoffs[i]);
263 #endif
264 flen = nflen;
265
266 sf->textbuf = sf->textptr = tx;
267 sf->textlength = nflen;
268 sf->eof = &sf->textbuf[nflen-1];
269 }
270
scriptfile_fromfile(const char * fn)271 scriptfile *scriptfile_fromfile(const char *fn)
272 {
273 int fp;
274 scriptfile *sf;
275 char *tx;
276 unsigned int flen;
277
278 fp = kopen4load(fn,0);
279 if (fp<0) return NULL;
280
281 flen = kfilelength(fp);
282 tx = (char *) malloc(flen + 2);
283 if (!tx) {
284 kclose(fp);
285 return NULL;
286 }
287
288 sf = (scriptfile*) malloc(sizeof(scriptfile));
289 if (!sf) {
290 kclose(fp);
291 free(tx);
292 return NULL;
293 }
294
295 kread(fp, tx, flen);
296 tx[flen] = tx[flen+1] = 0;
297
298 kclose(fp);
299
300 scriptfile_preparse(sf,tx,flen);
301 sf->filename = strdup(fn);
302
303 return sf;
304 }
305
scriptfile_fromstring(const char * string)306 scriptfile *scriptfile_fromstring(const char *string)
307 {
308 scriptfile *sf;
309 char *tx;
310 unsigned int flen;
311
312 if (!string) return NULL;
313
314 flen = strlen(string);
315
316 tx = (char *) malloc(flen + 2);
317 if (!tx) return NULL;
318
319 sf = (scriptfile*) malloc(sizeof(scriptfile));
320 if (!sf) {
321 free(tx);
322 return NULL;
323 }
324
325 memcpy(tx, string, flen);
326 tx[flen] = tx[flen+1] = 0;
327
328 scriptfile_preparse(sf,tx,flen);
329 sf->filename = NULL;
330
331 return sf;
332 }
333
scriptfile_close(scriptfile * sf)334 void scriptfile_close(scriptfile *sf)
335 {
336 if (!sf) return;
337 if (sf->lineoffs) free(sf->lineoffs);
338 if (sf->textbuf) free(sf->textbuf);
339 if (sf->filename) free(sf->filename);
340 sf->textbuf = NULL;
341 sf->filename = NULL;
342 free(sf);
343 }
344
scriptfile_eof(scriptfile * sf)345 int scriptfile_eof(scriptfile *sf)
346 {
347 skipoverws(sf);
348 if (sf->textptr >= sf->eof) return 1;
349 return 0;
350 }
351
352 #define SYMBTABSTARTSIZE 256
353 static int symbtablength=0, symbtaballoclength=0;
354 static char *symbtab = NULL;
355
getsymbtabspace(int reqd)356 static char * getsymbtabspace(int reqd)
357 {
358 char *pos,*np;
359 int i;
360
361 if (symbtablength + reqd > symbtaballoclength)
362 {
363 for(i=max(symbtaballoclength,SYMBTABSTARTSIZE);symbtablength+reqd>i;i<<=1);
364 np = (char *)realloc(symbtab, i); if (!np) return NULL;
365 symbtab = np; symbtaballoclength = i;
366 }
367
368 pos = &symbtab[symbtablength];
369 symbtablength += reqd;
370 return pos;
371 }
372
scriptfile_getsymbolvalue(const char * name,int * val)373 int scriptfile_getsymbolvalue(const char *name, int *val)
374 {
375 char *scanner = symbtab;
376
377 if (!symbtab) return 0;
378 while (scanner - symbtab < symbtablength) {
379 if (!Bstrcasecmp(name, scanner)) {
380 *val = *(int*)(scanner + strlen(scanner) + 1);
381 return 1;
382 }
383
384 scanner += strlen(scanner) + 1 + sizeof(int);
385 }
386
387 return 0;
388 }
389
scriptfile_addsymbolvalue(const char * name,int val)390 int scriptfile_addsymbolvalue(const char *name, int val)
391 {
392 int x;
393 char *sp;
394 //if (scriptfile_getsymbolvalue(name, &x)) return -1; // already exists
395
396 if (symbtab) {
397 char *scanner = symbtab;
398 while (scanner - symbtab < symbtablength) {
399 if (!Bstrcasecmp(name, scanner)) {
400 *(int*)(scanner + strlen(scanner) + 1) = val;
401 return 1;
402 }
403
404 scanner += strlen(scanner) + 1 + sizeof(int);
405 }
406 }
407
408 sp = getsymbtabspace(strlen(name) + 1 + sizeof(int));
409 if (!sp) return 0;
410 strcpy(sp, name);
411 sp += strlen(name)+1;
412 *(int*)sp = val;
413 return 1; // added
414 }
415
scriptfile_clearsymbols(void)416 void scriptfile_clearsymbols(void)
417 {
418 if (symbtab) free(symbtab);
419 symbtab = NULL;
420 symbtablength = 0;
421 symbtaballoclength = 0;
422 }
423