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