]> git.sur5r.net Git - cc65/blob - src/da65/scanner.c
Support for preprocessing info file via cpp or m4.
[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-2005 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 #include "xmalloc.h"
45 #include "strbuf.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 unsigned        InfoTok;
62 char            InfoSVal [CFG_MAX_IDENT_LEN+1];
63 long            InfoIVal;
64
65 /* Error location */
66 unsigned                InfoErrorLine;
67 unsigned                InfoErrorCol;
68
69 /* Input sources for the configuration */
70 static const char*      InfoFile        = 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 static char*            InputSrcName    = 0;
78
79 /* Options */
80 unsigned char           InfoSyncLines   = 0;
81
82
83 /*****************************************************************************/
84 /*                              Error handling                               */
85 /*****************************************************************************/
86
87
88
89 void InfoWarning (const char* Format, ...)
90 /* Print a warning message adding file name and line number of the config file */
91 {
92     char Buf [512];
93     va_list ap;
94
95     va_start (ap, Format);
96     xvsprintf (Buf, sizeof (Buf), Format, ap);
97     va_end (ap);
98
99     fprintf (stderr, "%s(%u): Warning: %s\n",
100             InputSrcName, InfoErrorLine, Buf);
101 }
102
103
104
105 void InfoError (const char* Format, ...)
106 /* Print an error message adding file name and line number of the config file */
107 {
108     char Buf [512];
109     va_list ap;
110
111     va_start (ap, Format);
112     xvsprintf (Buf, sizeof (Buf), Format, ap);
113     va_end (ap);
114
115     fprintf (stderr, "%s(%u): Error: %s\n",
116             InputSrcName, InfoErrorLine, Buf);
117     exit (EXIT_FAILURE);
118 }
119
120
121
122 /*****************************************************************************/
123 /*                                   Code                                    */
124 /*****************************************************************************/
125
126
127
128 static void NextChar (void)
129 /* Read the next character from the input file */
130 {
131     /* Read from the file */
132     C = getc (InputFile);
133
134     /* Count columns */
135     if (C != EOF) {
136         ++InputCol;
137     }
138
139     /* Count lines */
140     if (C == '\n') {
141         ++InputLine;
142         InputCol = 0;
143     }
144 }
145
146
147
148 static unsigned DigitVal (int C)
149 /* Return the value for a numeric digit */
150 {
151     if (IsDigit (C)) {
152         return C - '0';
153     } else {
154         return toupper (C) - 'A' + 10;
155     }
156 }
157
158
159
160 static void SkipBlanks (int SingleLine)
161 {
162     while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
163         NextChar ();
164     }
165 }
166
167 static long GetDecimalToken ()
168 {
169     long Value = 0;
170
171     while (C != EOF && IsDigit (C)) {
172         Value = Value * 10 + DigitVal (C);
173         NextChar ();
174     }
175     return Value;
176 }
177
178 static int GetEncodedChar (char *Buf, unsigned *IPtr, unsigned Size)
179 {
180     char Decoded = 0;
181     int Count;
182
183     if (C == EOF) {
184         return -1;
185     } else if (C != '\\') {
186         Decoded = C;
187         NextChar ();
188         goto Store;
189     }
190     NextChar (); /* consume '\\' */
191     if (C == EOF) {
192         return -1;
193     } else if (IsODigit (C)) {
194         Count = 3;
195         do {
196             Decoded = Decoded * 8 + DigitVal (C);
197             NextChar ();
198             --Count;
199         } while (Count > 0 && C != EOF && IsODigit (C));
200     } else if (C == 'x') {
201         NextChar (); /* consume 'x' */
202         Count = 2;
203         while (Count > 0 && C != EOF && IsXDigit (C)) {
204             Decoded = Decoded * 16 + DigitVal (C);
205             NextChar ();
206             --Count;
207         }
208     } else {
209         switch (C) {
210             case '"': case '\'': case '\\':
211                         Decoded = C;        break;
212             case 't':   Decoded = '\t';     break;
213             case 'r':   Decoded = '\r';     break;
214             case 'n':   Decoded = '\n';     break;
215             default:    return -1;
216         }
217         NextChar ();
218     }
219 Store:
220     if (*IPtr < Size - 1) {
221         Buf [(*IPtr)++] = Decoded;
222     }
223     Buf [*IPtr] = 0;
224     return 0;
225 }
226
227 static void LineMarkerOrComment ()
228 /* Handle a line beginning with '#'. Possible interpretations are:
229  * - #line <lineno> ["<filename>"]          (C preprocessor input)
230  * - # <lineno> "<filename>" [<flag>]...    (gcc preprocessor output)
231  * - #<comment>
232  */
233 {
234     unsigned long LineNo = 0;
235     int LineDirective = 0;
236     StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
237     
238     /* Skip the first "# " */
239     NextChar ();
240     SkipBlanks (1);
241
242     /* Check "line" */
243     if (C == 'l') {
244         char MaybeLine [6];
245         unsigned I;
246         for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
247             MaybeLine [I] = C;
248             NextChar ();
249         }
250         MaybeLine [I] = 0;
251         if (strcmp (MaybeLine, "line") != 0) {
252             goto NotMarker;
253         }
254         LineDirective = 1;
255         SkipBlanks (1);
256     }
257
258     /* Get line number */
259     if (C == EOF || !IsDigit (C)) {
260         goto NotMarker;
261     }
262     LineNo = GetDecimalToken ();
263     SkipBlanks (1);
264
265     /* Get the source file name */
266     if (C != '\"') {
267         /* The source file name is missing */
268         if (LineDirective && C == '\n') {
269             /* got #line <lineno> */
270             NextChar ();
271             InputLine = LineNo;
272             goto Last;
273         } else {
274             goto NotMarker;
275         }
276     }
277     NextChar ();
278     while (C != EOF && C != '\n' && C != '\"') {
279         char DecodeBuf [2];
280         unsigned I = 0;
281         if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
282             goto BadMarker;
283         }
284         SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
285     }
286     if (C != '\"') {
287         goto BadMarker;
288     }
289     NextChar ();
290
291     /* Ignore until the end of line */
292     while (C != EOF && C != '\n') {
293         NextChar ();
294     }
295
296     /* Accepted a line marker */
297     SB_Terminate (&SrcNameBuf);
298     xfree (InputSrcName);
299     InputSrcName = SB_GetBuf (&SrcNameBuf);
300     SB_Init (&SrcNameBuf);
301     InputLine = (unsigned)LineNo;
302     NextChar ();
303     goto Last;
304
305 BadMarker:
306     InfoWarning ("Bad line marker");
307 NotMarker:
308     while (C != EOF && C != '\n') {
309         NextChar ();
310     }
311     NextChar ();
312 Last:
313     SB_Done (&SrcNameBuf);
314 }
315
316 void InfoNextTok (void)
317 /* Read the next token from the input stream */
318 {
319     unsigned I;
320     char DecodeBuf [2];
321
322 Again:
323     /* Skip whitespace */
324     SkipBlanks (0);
325
326     /* Remember the current position */
327     InfoErrorLine = InputLine;
328     InfoErrorCol  = InputCol;
329
330     /* Identifier? */
331     if (C == '_' || IsAlpha (C)) {
332
333         /* Read the identifier */
334         I = 0;
335         while (C == '_' || IsAlNum (C)) {
336             if (I < CFG_MAX_IDENT_LEN) {
337                 InfoSVal [I++] = C;
338             }
339             NextChar ();
340         }
341         InfoSVal [I] = '\0';
342         InfoTok = INFOTOK_IDENT;
343         return;
344     }
345
346     /* Hex number? */
347     if (C == '$') {
348         NextChar ();
349         if (!IsXDigit (C)) {
350             InfoError ("Hex digit expected");
351         }
352         InfoIVal = 0;
353         while (IsXDigit (C)) {
354             InfoIVal = InfoIVal * 16 + DigitVal (C);
355             NextChar ();
356         }
357         InfoTok = INFOTOK_INTCON;
358         return;
359     }
360
361     /* Decimal number? */
362     if (IsDigit (C)) {
363         InfoIVal = GetDecimalToken ();
364         InfoTok = INFOTOK_INTCON;
365         return;
366     }
367
368     /* Other characters */
369     switch (C) {
370
371         case '{':
372             NextChar ();
373             InfoTok = INFOTOK_LCURLY;
374             break;
375
376         case '}':
377             NextChar ();
378             InfoTok = INFOTOK_RCURLY;
379             break;
380
381         case ';':
382             NextChar ();
383             InfoTok = INFOTOK_SEMI;
384             break;
385
386         case '.':
387             NextChar ();
388             InfoTok = INFOTOK_DOT;
389             break;
390
391         case ',':
392             NextChar ();
393             InfoTok = INFOTOK_COMMA;
394             break;
395
396         case '=':
397             NextChar ();
398             InfoTok = INFOTOK_EQ;
399             break;
400
401         case ':':
402             NextChar ();
403             InfoTok = INFOTOK_COLON;
404             break;
405
406         case '\"':
407             NextChar ();
408             I = 0;
409             while (C != EOF && C != '\"') {
410                 if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
411                     if (C == EOF) {
412                         InfoError ("Unterminated string");
413                     } else  {
414                         InfoError ("Invalid escape char: %c", C);
415                     }
416                 }
417             }
418             if (C != '\"') {
419                 InfoError ("Unterminated string");
420             }
421             NextChar ();
422             InfoTok = INFOTOK_STRCON;
423             break;
424
425         case '\'':
426             NextChar ();
427             if (C == EOF || IsControl (C) || C == '\'') {
428                 InfoError ("Invalid character constant");
429             }
430             if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
431                 InfoError ("Invalid character constant");
432             }
433             InfoIVal = DecodeBuf [0];
434             if (C != '\'') {
435                 InfoError ("Unterminated character constant");
436             }
437             NextChar ();
438             InfoTok = INFOTOK_CHARCON;
439             break;
440
441         case '#':
442             /* # lineno "sourcefile" or # comment */
443             if (InfoSyncLines && InputCol == 1) {
444                 LineMarkerOrComment ();
445             } else {
446                 do {
447                     NextChar ();
448                 } while (C != EOF && C != '\n');
449                 NextChar ();
450             }
451             if (C != EOF) {
452                 goto Again;
453             }
454             InfoTok = INFOTOK_EOF;
455             break;
456
457         case '/':
458             /* C++ style comment */
459             NextChar ();
460             if (C != '/') {
461                 InfoError ("Invalid token `/'");
462             }
463             do {
464                 NextChar ();
465             } while (C != '\n' && C != EOF);
466             if (C != EOF) {
467                 goto Again;
468             }
469             InfoTok = INFOTOK_EOF;
470             break;
471
472         case EOF:
473             InfoTok = INFOTOK_EOF;
474             break;
475
476         default:
477             InfoError ("Invalid character `%c'", C);
478
479     }
480 }
481
482
483
484 void InfoConsume (unsigned T, const char* Msg)
485 /* Skip a token, print an error message if not found */
486 {
487     if (InfoTok != T) {
488         InfoError (Msg);
489     }
490     InfoNextTok ();
491 }
492
493
494
495 void InfoConsumeLCurly (void)
496 /* Consume a left curly brace */
497 {
498     InfoConsume (INFOTOK_LCURLY, "`{' expected");
499 }
500
501
502
503 void InfoConsumeRCurly (void)
504 /* Consume a right curly brace */
505 {
506     InfoConsume (INFOTOK_RCURLY, "`}' expected");
507 }
508
509
510
511 void InfoConsumeSemi (void)
512 /* Consume a semicolon */
513 {
514     InfoConsume (INFOTOK_SEMI, "`;' expected");
515 }
516
517
518
519 void InfoConsumeColon (void)
520 /* Consume a colon */
521 {
522     InfoConsume (INFOTOK_COLON, "`:' expected");
523 }
524
525
526
527 void InfoOptionalComma (void)
528 /* Consume a comma if there is one */
529 {
530     if (InfoTok == INFOTOK_COMMA) {
531         InfoNextTok ();
532     }
533 }
534
535
536
537 void InfoOptionalAssign (void)
538 /* Consume an equal sign if there is one */
539 {
540     if (InfoTok == INFOTOK_EQ) {
541         InfoNextTok ();
542     }
543 }
544
545
546
547 void InfoAssureInt (void)
548 /* Make sure the next token is an integer */
549 {
550     if (InfoTok != INFOTOK_INTCON) {
551         InfoError ("Integer constant expected");
552     }
553 }
554
555
556
557 void InfoAssureStr (void)
558 /* Make sure the next token is a string constant */
559 {
560     if (InfoTok != INFOTOK_STRCON) {
561         InfoError ("String constant expected");
562     }
563 }
564
565
566
567 void InfoAssureChar (void)
568 /* Make sure the next token is a char constant */
569 {
570     if (InfoTok != INFOTOK_STRCON) {
571         InfoError ("Character constant expected");
572     }
573 }
574
575
576
577 void InfoAssureIdent (void)
578 /* Make sure the next token is an identifier */
579 {
580     if (InfoTok != INFOTOK_IDENT) {
581         InfoError ("Identifier expected");
582     }
583 }
584
585
586
587 void InfoRangeCheck (long Lo, long Hi)
588 /* Check the range of InfoIVal */
589 {
590     if (InfoIVal < Lo || InfoIVal > Hi) {
591         InfoError ("Range error");
592     }
593 }
594
595
596
597 void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
598 /* Map an identifier to one of the special tokens in the table */
599 {
600     unsigned I;
601
602     /* We need an identifier */
603     if (InfoTok == INFOTOK_IDENT) {
604
605         /* Make it upper case */
606         I = 0;
607         while (InfoSVal [I]) {
608             InfoSVal [I] = toupper (InfoSVal [I]);
609             ++I;
610         }
611
612         /* Linear search */
613         for (I = 0; I < Size; ++I) {
614             if (strcmp (InfoSVal, Table [I].Ident) == 0) {
615                 InfoTok = Table [I].Tok;
616                 return;
617             }
618         }
619
620     }
621
622     /* Not found or no identifier */
623     InfoError ("%s expected", Name);
624 }
625
626
627
628 void InfoBoolToken (void)
629 /* Map an identifier or integer to a boolean token */
630 {
631     static const IdentTok Booleans [] = {
632         {   "YES",      INFOTOK_TRUE     },
633         {   "NO",       INFOTOK_FALSE    },
634         {   "TRUE",     INFOTOK_TRUE     },
635         {   "FALSE",    INFOTOK_FALSE    },
636         {   "ON",       INFOTOK_TRUE     },
637         {   "OFF",      INFOTOK_FALSE    },
638     };
639
640     /* If we have an identifier, map it to a boolean token */
641     if (InfoTok == INFOTOK_IDENT) {
642         InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
643     } else {
644         /* We expected an integer here */
645         if (InfoTok != INFOTOK_INTCON) {
646             InfoError ("Boolean value expected");
647         }
648         InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
649     }
650 }
651
652
653
654 void InfoSetName (const char* Name)
655 /* Set a name for a config file */
656 {
657     InfoFile = Name;
658     xfree(InputSrcName);
659     InputSrcName = xstrdup(Name);
660 }
661
662
663
664 #ifdef unused
665 const char* InfoGetName (void)
666 /* Get the name of the config file */
667 {
668     return InfoFile? InfoFile : "";
669 }
670 #endif /* unused */
671
672
673
674 int InfoAvail ()
675 /* Return true if we have an info file given */
676 {
677     return (InfoFile != 0);
678 }
679
680
681
682 void InfoOpenInput (void)
683 /* Open the input file */
684 {
685     /* Open the file */
686     InputFile = fopen (InfoFile, "r");
687     if (InputFile == 0) {
688         Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
689     }
690
691     /* Initialize variables */
692     C         = ' ';
693     InputLine = 1;
694     InputCol  = 0;
695
696     /* Start the ball rolling ... */
697     InfoNextTok ();
698 }
699
700
701
702 void InfoCloseInput (void)
703 /* Close the input file if we have one */
704 {
705     /* Close the input file if we had one */
706     if (InputFile) {
707         (void) fclose (InputFile);
708         InputFile = 0;
709     }
710 }