]> git.sur5r.net Git - cc65/blob - src/sim65/scanner.c
Working
[cc65] / src / sim65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*            Configuration file scanner for the sim65 6502 simulator        */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <ctype.h>
41
42 /* common */
43 #include "xsprintf.h"
44
45 /* sim65 */
46 #include "error.h"
47 #include "scanner.h"
48
49
50
51 /*****************************************************************************/
52 /*                                   Data                                    */
53 /*****************************************************************************/
54
55
56
57 /* Current token and attributes */
58 cfgtok_t                CfgTok;
59 char                    CfgSVal [CFG_MAX_IDENT_LEN+1];
60 unsigned long           CfgIVal;
61
62 /* Error location */
63 unsigned                CfgErrorLine;
64 unsigned                CfgErrorCol;
65
66 /* Input sources for the configuration */
67 static const char*      CfgName         = 0;
68 static const char*      CfgBuf          = 0;
69
70 /* Other input stuff */
71 static int              C               = ' ';
72 static unsigned         InputLine       = 1;
73 static unsigned         InputCol        = 0;
74 static FILE*            InputFile       = 0;
75
76
77
78 /*****************************************************************************/
79 /*                              Error handling                               */
80 /*****************************************************************************/
81
82
83
84 void CfgWarning (const char* Format, ...)
85 /* Print a warning message adding file name and line number of the config file */
86 {
87     char Buf [512];
88     va_list ap;
89
90     va_start (ap, Format);
91     xvsprintf (Buf, sizeof (Buf), Format, ap);
92     va_end (ap);
93
94     Warning ("%s(%u): %s", CfgGetName(), CfgErrorLine, Buf);
95 }
96
97
98
99 void CfgError (const char* Format, ...)
100 /* Print an error message adding file name and line number of the config file */
101 {
102     char Buf [512];
103     va_list ap;
104
105     va_start (ap, Format);
106     xvsprintf (Buf, sizeof (Buf), Format, ap);
107     va_end (ap);
108
109     Error ("%s(%u): %s", CfgGetName(), CfgErrorLine, Buf);
110 }
111
112
113
114 /*****************************************************************************/
115 /*                                   Code                                    */
116 /*****************************************************************************/
117
118
119
120 static void NextChar (void)
121 /* Read the next character from the input file */
122 {
123     if (CfgBuf) {
124         /* Read from buffer */
125         C = (unsigned char)(*CfgBuf);
126         if (C == 0) {
127             C = EOF;
128         } else {
129             ++CfgBuf;
130         }
131     } else {
132         /* Read from the file */
133         C = getc (InputFile);
134     }
135
136     /* Count columns */
137     if (C != EOF) {
138         ++InputCol;
139     }
140
141     /* Count lines */
142     if (C == '\n') {
143         ++InputLine;
144         InputCol = 0;
145     }
146 }
147
148
149
150 static unsigned DigitVal (int C)
151 /* Return the value for a numeric digit */
152 {
153     if (isdigit (C)) {
154         return C - '0';
155     } else {
156         return toupper (C) - 'A' + 10;
157     }
158 }
159
160
161
162 void CfgNextTok (void)
163 /* Read the next token from the input stream */
164 {
165     unsigned I;
166
167
168 Again:
169     /* Skip whitespace */
170     while (isspace (C)) {
171         NextChar ();
172     }
173
174     /* Remember the current position */
175     CfgErrorLine = InputLine;
176     CfgErrorCol  = InputCol;
177
178     /* Identifier? */
179     if (C == '_' || isalpha (C)) {
180
181         /* Read the identifier */
182         I = 0;
183         while (C == '_' || isalnum (C)) {
184             if (I < CFG_MAX_IDENT_LEN) {
185                 CfgSVal [I++] = C;
186             }
187             NextChar ();
188         }
189         CfgSVal [I] = '\0';
190         CfgTok = CFGTOK_IDENT;
191         return;
192     }
193
194     /* Hex number? */
195     if (C == '$') {
196         NextChar ();
197         if (!isxdigit (C)) {
198             Error ("%s(%u): Hex digit expected", CfgName, InputLine);
199         }
200         CfgIVal = 0;
201         while (isxdigit (C)) {
202             CfgIVal = CfgIVal * 16 + DigitVal (C);
203             NextChar ();
204         }
205         CfgTok = CFGTOK_INTCON;
206         return;
207     }
208
209     /* Decimal number? */
210     if (isdigit (C)) {
211         CfgIVal = 0;
212         while (isdigit (C)) {
213             CfgIVal = CfgIVal * 10 + DigitVal (C);
214             NextChar ();
215         }
216         CfgTok = CFGTOK_INTCON;
217         return;
218     }
219
220     /* Other characters */
221     switch (C) {
222
223         case '{':
224             NextChar ();
225             CfgTok = CFGTOK_LCURLY;
226             break;
227
228         case '}':
229             NextChar ();
230             CfgTok = CFGTOK_RCURLY;
231             break;
232
233         case ';':
234             NextChar ();
235             CfgTok = CFGTOK_SEMI;
236             break;
237
238         case '.':
239             NextChar ();
240             CfgTok = CFGTOK_DOT;
241             break;
242
243         case ',':
244             NextChar ();
245             CfgTok = CFGTOK_COMMA;
246             break;
247
248         case '=':
249             NextChar ();
250             CfgTok = CFGTOK_EQ;
251             break;
252
253         case ':':
254             NextChar ();
255             CfgTok = CFGTOK_COLON;
256             break;
257
258         case '\"':
259             NextChar ();
260             I = 0;
261             while (C != '\"') {
262                 if (C == EOF || C == '\n') {
263                     Error ("%s(%u): Unterminated string", CfgName, InputLine);
264                 }
265                 if (I < CFG_MAX_IDENT_LEN) {
266                     CfgSVal [I++] = C;
267                 }
268                 NextChar ();
269             }
270             NextChar ();
271             CfgSVal [I] = '\0';
272             CfgTok = CFGTOK_STRCON;
273             break;
274
275         case '#':
276             /* Comment */
277             while (C != '\n' && C != EOF) {
278                 NextChar ();
279             }
280             if (C != EOF) {
281                 goto Again;
282             }
283             CfgTok = CFGTOK_EOF;
284             break;
285
286         case EOF:
287             CfgTok = CFGTOK_EOF;
288             break;
289
290         default:
291             Error ("%s(%u): Invalid character `%c'", CfgName, InputLine, C);
292
293     }
294 }
295
296
297
298 void CfgConsume (cfgtok_t T, const char* Msg)
299 /* Skip a token, print an error message if not found */
300 {
301     if (CfgTok != T) {
302         CfgError (Msg);
303     }
304     CfgNextTok ();
305 }
306
307
308
309 void CfgConsumeSemi (void)
310 /* Consume a semicolon */
311 {
312     CfgConsume (CFGTOK_SEMI, "`;' expected");
313 }
314
315
316
317 void CfgConsumeColon (void)
318 /* Consume a colon */
319 {
320     CfgConsume (CFGTOK_COLON, "`:' expected");
321 }
322
323
324
325 void CfgOptionalComma (void)
326 /* Consume a comma if there is one */
327 {
328     if (CfgTok == CFGTOK_COMMA) {
329         CfgNextTok ();
330     }
331 }
332
333
334
335 void CfgOptionalAssign (void)
336 /* Consume an equal sign if there is one */
337 {
338     if (CfgTok == CFGTOK_EQ) {
339         CfgNextTok ();
340     }
341 }
342
343
344
345 void CfgAssureInt (void)
346 /* Make sure the next token is an integer */
347 {
348     if (CfgTok != CFGTOK_INTCON) {
349         CfgError ("Integer constant expected");
350     }
351 }
352
353
354
355 void CfgAssureStr (void)
356 /* Make sure the next token is a string constant */
357 {
358     if (CfgTok != CFGTOK_STRCON) {
359         CfgError ("String constant expected");
360     }
361 }
362
363
364
365 void CfgAssureIdent (void)
366 /* Make sure the next token is an identifier */
367 {
368     if (CfgTok != CFGTOK_IDENT) {
369         CfgError ("Identifier expected");
370     }
371 }
372
373
374
375 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
376 /* Check the range of CfgIVal */
377 {
378     if (CfgIVal < Lo || CfgIVal > Hi) {
379         CfgError ("Range error");
380     }
381 }
382
383
384
385 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
386 /* Map an identifier to one of the special tokens in the table */
387 {
388     unsigned I;
389
390     /* We need an identifier */
391     if (CfgTok == CFGTOK_IDENT) {
392
393         /* Make it upper case */
394         I = 0;
395         while (CfgSVal [I]) {
396             CfgSVal [I] = toupper (CfgSVal [I]);
397             ++I;
398         }
399
400         /* Linear search */
401         for (I = 0; I < Size; ++I) {
402             if (strcmp (CfgSVal, Table [I].Ident) == 0) {
403                 CfgTok = Table [I].Tok;
404                 return;
405             }
406         }
407
408     }
409
410     /* Not found or no identifier */
411     Error ("%s(%u): %s expected", CfgName, InputLine, Name);
412 }
413
414
415
416 void CfgBoolToken (void)
417 /* Map an identifier or integer to a boolean token */
418 {
419     static const IdentTok Booleans [] = {
420         {   "YES",      CFGTOK_TRUE     },
421         {   "NO",       CFGTOK_FALSE    },
422         {   "TRUE",     CFGTOK_TRUE     },
423         {   "FALSE",    CFGTOK_FALSE    },
424     };
425
426     /* If we have an identifier, map it to a boolean token */
427     if (CfgTok == CFGTOK_IDENT) {
428         CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
429     } else {
430         /* We expected an integer here */
431         if (CfgTok != CFGTOK_INTCON) {
432             CfgError ("Boolean value expected");
433         }
434         CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
435     }
436 }
437
438
439
440 void CfgSetName (const char* Name)
441 /* Set a name for a config file */
442 {
443     CfgName = Name;
444 }
445
446
447
448 const char* CfgGetName (void)
449 /* Get the name of the config file */
450 {
451     if (CfgName) {
452         return CfgName;
453     } else if (CfgBuf) {
454         return "[builtin config]";
455     } else {
456         return "";
457     }
458 }
459
460
461
462 void CfgSetBuf (const char* Buf)
463 /* Set a memory buffer for the config */
464 {
465     CfgBuf = Buf;
466 }
467
468
469
470 int CfgAvail (void)
471 /* Return true if we have a configuration available */
472 {
473     return CfgName != 0 || CfgBuf != 0;
474 }
475
476
477
478 void CfgOpenInput (void)
479 /* Open the input file if we have one */
480 {
481     /* If we have a config name given, open the file, otherwise we will read
482      * from a buffer.
483      */
484     if (!CfgBuf) {
485
486         /* Open the file */
487         InputFile = fopen (CfgName, "r");
488         if (InputFile == 0) {
489             Error ("Cannot open `%s': %s", CfgName, strerror (errno));
490         }
491
492     }
493
494     /* Initialize variables */
495     C         = ' ';
496     InputLine = 1;
497     InputCol  = 0;
498
499     /* Start the ball rolling ... */
500     CfgNextTok ();
501 }
502
503
504
505 void CfgCloseInput (void)
506 /* Close the input file if we have one */
507 {
508     /* Close the input file if we had one */
509     if (InputFile) {
510         (void) fclose (InputFile);
511         InputFile = 0;
512     }
513 }
514
515
516