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