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