]> git.sur5r.net Git - cc65/blob - src/da65/scanner.c
Allow conditional directives within .STRUCT7:UNION and .ENUM
[cc65] / src / da65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*           Configuration file scanner for the da65 disassembler            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
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
41 /* common */
42 #include "chartype.h"
43 #include "xsprintf.h"
44
45 /* ld65 */
46 #include "global.h"
47 #include "error.h"
48 #include "scanner.h"
49
50
51
52 /*****************************************************************************/
53 /*                                   Data                                    */
54 /*****************************************************************************/
55
56
57
58 /* Current token and attributes */
59 unsigned        InfoTok;
60 char            InfoSVal [CFG_MAX_IDENT_LEN+1];
61 long            InfoIVal;
62
63 /* Error location */
64 unsigned                InfoErrorLine;
65 unsigned                InfoErrorCol;
66
67 /* Input sources for the configuration */
68 static const char*      InfoFile        = 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 InfoWarning (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", InfoFile, InfoErrorLine, Buf);
95 }
96
97
98
99 void InfoError (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", InfoFile, InfoErrorLine, 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     /* Read from the file */
124     C = getc (InputFile);
125
126     /* Count columns */
127     if (C != EOF) {
128         ++InputCol;
129     }
130
131     /* Count lines */
132     if (C == '\n') {
133         ++InputLine;
134         InputCol = 0;
135     }
136 }
137
138
139
140 static unsigned DigitVal (int C)
141 /* Return the value for a numeric digit */
142 {
143     if (IsDigit (C)) {
144         return C - '0';
145     } else {
146         return toupper (C) - 'A' + 10;
147     }
148 }
149
150
151
152 void InfoNextTok (void)
153 /* Read the next token from the input stream */
154 {
155     unsigned I;
156
157
158 Again:
159     /* Skip whitespace */
160     while (IsSpace (C)) {
161         NextChar ();
162     }
163
164     /* Remember the current position */
165     InfoErrorLine = InputLine;
166     InfoErrorCol  = InputCol;
167
168     /* Identifier? */
169     if (C == '_' || IsAlpha (C)) {
170
171         /* Read the identifier */
172         I = 0;
173         while (C == '_' || IsAlNum (C)) {
174             if (I < CFG_MAX_IDENT_LEN) {
175                 InfoSVal [I++] = C;
176             }
177             NextChar ();
178         }
179         InfoSVal [I] = '\0';
180         InfoTok = INFOTOK_IDENT;
181         return;
182     }
183
184     /* Hex number? */
185     if (C == '$') {
186         NextChar ();
187         if (!IsXDigit (C)) {
188             InfoError ("Hex digit expected");
189         }
190         InfoIVal = 0;
191         while (IsXDigit (C)) {
192             InfoIVal = InfoIVal * 16 + DigitVal (C);
193             NextChar ();
194         }
195         InfoTok = INFOTOK_INTCON;
196         return;
197     }
198
199     /* Decimal number? */
200     if (IsDigit (C)) {
201         InfoIVal = 0;
202         while (IsDigit (C)) {
203             InfoIVal = InfoIVal * 10 + DigitVal (C);
204             NextChar ();
205         }
206         InfoTok = INFOTOK_INTCON;
207         return;
208     }
209
210     /* Other characters */
211     switch (C) {
212
213         case '{':
214             NextChar ();
215             InfoTok = INFOTOK_LCURLY;
216             break;
217
218         case '}':
219             NextChar ();
220             InfoTok = INFOTOK_RCURLY;
221             break;
222
223         case ';':
224             NextChar ();
225             InfoTok = INFOTOK_SEMI;
226             break;
227
228         case '.':
229             NextChar ();
230             InfoTok = INFOTOK_DOT;
231             break;
232
233         case ',':
234             NextChar ();
235             InfoTok = INFOTOK_COMMA;
236             break;
237
238         case '=':
239             NextChar ();
240             InfoTok = INFOTOK_EQ;
241             break;
242
243         case ':':
244             NextChar ();
245             InfoTok = INFOTOK_COLON;
246             break;
247
248         case '\"':
249             NextChar ();
250             I = 0;
251             while (C != '\"') {
252                 if (C == EOF || C == '\n') {
253                     InfoError ("Unterminated string");
254                 }
255                 if (I < CFG_MAX_IDENT_LEN) {
256                     InfoSVal [I++] = C;
257                 }
258                 NextChar ();
259             }
260             NextChar ();
261             InfoSVal [I] = '\0';
262             InfoTok = INFOTOK_STRCON;
263             break;
264
265         case '#':
266             /* Comment */
267             while (C != '\n' && C != EOF) {
268                 NextChar ();
269             }
270             if (C != EOF) {
271                 goto Again;
272             }
273             InfoTok = INFOTOK_EOF;
274             break;
275
276         case EOF:
277             InfoTok = INFOTOK_EOF;
278             break;
279
280         default:
281             InfoError ("Invalid character `%c'", C);
282
283     }
284 }
285
286
287
288 void InfoConsume (unsigned T, const char* Msg)
289 /* Skip a token, print an error message if not found */
290 {
291     if (InfoTok != T) {
292         InfoError (Msg);
293     }
294     InfoNextTok ();
295 }
296
297
298
299 void InfoConsumeLCurly (void)
300 /* Consume a left curly brace */
301 {
302     InfoConsume (INFOTOK_LCURLY, "`{' expected");
303 }
304
305
306
307 void InfoConsumeRCurly (void)
308 /* Consume a right curly brace */
309 {
310     InfoConsume (INFOTOK_RCURLY, "`}' expected");
311 }
312
313
314
315 void InfoConsumeSemi (void)
316 /* Consume a semicolon */
317 {
318     InfoConsume (INFOTOK_SEMI, "`;' expected");
319 }
320
321
322
323 void InfoConsumeColon (void)
324 /* Consume a colon */
325 {
326     InfoConsume (INFOTOK_COLON, "`:' expected");
327 }
328
329
330
331 void InfoOptionalComma (void)
332 /* Consume a comma if there is one */
333 {
334     if (InfoTok == INFOTOK_COMMA) {
335         InfoNextTok ();
336     }
337 }
338
339
340
341 void InfoOptionalAssign (void)
342 /* Consume an equal sign if there is one */
343 {
344     if (InfoTok == INFOTOK_EQ) {
345         InfoNextTok ();
346     }
347 }
348
349
350
351 void InfoAssureInt (void)
352 /* Make sure the next token is an integer */
353 {
354     if (InfoTok != INFOTOK_INTCON) {
355         InfoError ("Integer constant expected");
356     }
357 }
358
359
360
361 void InfoAssureStr (void)
362 /* Make sure the next token is a string constant */
363 {
364     if (InfoTok != INFOTOK_STRCON) {
365         InfoError ("String constant expected");
366     }
367 }
368
369
370
371 void InfoAssureIdent (void)
372 /* Make sure the next token is an identifier */
373 {
374     if (InfoTok != INFOTOK_IDENT) {
375         InfoError ("Identifier expected");
376     }
377 }
378
379
380
381 void InfoRangeCheck (long Lo, long Hi)
382 /* Check the range of InfoIVal */
383 {
384     if (InfoIVal < Lo || InfoIVal > Hi) {
385         InfoError ("Range error");
386     }
387 }
388
389
390
391 void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
392 /* Map an identifier to one of the special tokens in the table */
393 {
394     unsigned I;
395
396     /* We need an identifier */
397     if (InfoTok == INFOTOK_IDENT) {
398
399         /* Make it upper case */
400         I = 0;
401         while (InfoSVal [I]) {
402             InfoSVal [I] = toupper (InfoSVal [I]);
403             ++I;
404         }
405
406         /* Linear search */
407         for (I = 0; I < Size; ++I) {
408             if (strcmp (InfoSVal, Table [I].Ident) == 0) {
409                 InfoTok = Table [I].Tok;
410                 return;
411             }
412         }
413
414     }
415
416     /* Not found or no identifier */
417     InfoError ("%s expected", Name);
418 }
419
420
421
422 void InfoBoolToken (void)
423 /* Map an identifier or integer to a boolean token */
424 {
425     static const IdentTok Booleans [] = {
426         {   "YES",      INFOTOK_TRUE     },
427         {   "NO",       INFOTOK_FALSE    },
428         {   "TRUE",     INFOTOK_TRUE     },
429         {   "FALSE",    INFOTOK_FALSE    },
430     };
431
432     /* If we have an identifier, map it to a boolean token */
433     if (InfoTok == INFOTOK_IDENT) {
434         InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
435     } else {
436         /* We expected an integer here */
437         if (InfoTok != INFOTOK_INTCON) {
438             InfoError ("Boolean value expected");
439         }
440         InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
441     }
442 }
443
444
445
446 void InfoSetName (const char* Name)
447 /* Set a name for a config file */
448 {
449     InfoFile = Name;
450 }
451
452
453
454 const char* InfoGetName (void)
455 /* Get the name of the config file */
456 {
457     return InfoFile? InfoFile : "";
458 }
459
460
461
462 int InfoAvail ()
463 /* Return true if we have an info file given */
464 {
465     return (InfoFile != 0);
466 }
467
468
469
470 void InfoOpenInput (void)
471 /* Open the input file */
472 {
473     /* Open the file */
474     InputFile = fopen (InfoFile, "r");
475     if (InputFile == 0) {
476         Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
477     }
478
479     /* Initialize variables */
480     C         = ' ';
481     InputLine = 1;
482     InputCol  = 0;
483
484     /* Start the ball rolling ... */
485     InfoNextTok ();
486 }
487
488
489
490 void InfoCloseInput (void)
491 /* Close the input file if we have one */
492 {
493     /* Close the input file if we had one */
494     if (InputFile) {
495         (void) fclose (InputFile);
496         InputFile = 0;
497     }
498 }
499
500
501
502