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