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