]> git.sur5r.net Git - cc65/blob - src/ca65/scanner.c
Added string handling functions: .STRLEN and .STRAT
[cc65] / src / ca65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                  The scanner for the ca65 macroassembler                  */
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 <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42
43 #include "../common/fname.h"
44 #include "../common/xmalloc.h"
45
46 #include "condasm.h"
47 #include "error.h"
48 #include "global.h"
49 #include "incpath.h"
50 #include "instr.h"
51 #include "istack.h"
52 #include "listing.h"
53 #include "macro.h"
54 #include "objfile.h"
55 #include "toklist.h"
56 #include "scanner.h"
57
58
59
60 /*****************************************************************************/
61 /*                                   Data                                    */
62 /*****************************************************************************/
63
64
65
66 enum Token Tok = TOK_NONE;              /* Current token */
67 int WS;                                 /* Flag: Whitespace before token */
68 long IVal;                              /* Integer token attribute */
69 char SVal [MAX_STR_LEN+1];              /* String token attribute */
70
71 FilePos CurPos = { 0, 0, 0 };           /* Name and position in current file */
72
73
74
75 /* Struct to handle include files. Note: The length of the input line may
76  * not exceed 255+1, since the column is stored in the file position struct
77  * as a character. Increasing this value means changing the FilePos struct,
78  * and the read and write routines in the assembler and linker.
79  */
80 typedef struct InputFile_ InputFile;
81 struct InputFile_ {
82     FILE*           F;                  /* Input file descriptor */
83     FilePos         Pos;                /* Position in file */
84     enum Token      Tok;                /* Last token */
85     int             C;                  /* Last character */
86     char            Line[256];          /* The current input line */
87     InputFile*      Next;               /* Linked list of input files */
88 };
89
90 /* Struct to handle textual input data */
91 typedef struct InputData_ InputData;
92 struct InputData_ {
93     const char*     Data;               /* Pointer to the data */
94     const char*     Pos;                /* Pointer to current position */
95     int             Malloced;           /* Memory was malloced */
96     enum Token      Tok;                /* Last token */
97     int             C;                  /* Last character */
98     InputData*      Next;               /* Linked list of input data */
99 };
100
101 /* List of input files */
102 static struct {
103     unsigned long  MTime;               /* Time of last modification */
104     unsigned long  Size;                /* Size of file */
105     const char*    Name;                /* Name of file */
106 } Files [MAX_INPUT_FILES];
107 static unsigned    FileCount = 0;
108
109 /* Current input variables */
110 static InputFile* IFile         = 0;    /* Current input file */
111 static InputData* IData         = 0;    /* Current input memory data */
112 static unsigned   ICount        = 0;    /* Count of input files */
113 static int        C             = 0;    /* Current input character */
114
115 /* Force end of assembly */
116 int               ForcedEnd = 0;
117
118 /* List of dot keywords with the corresponding tokens */
119 struct DotKeyword {
120     const char* Key;                    /* MUST be first field */
121     enum Token  Tok;
122 } DotKeywords [] = {
123     { "A16",            TOK_A16         },
124     { "A8",             TOK_A8          },
125     { "ADDR",           TOK_ADDR        },
126     { "ALIGN",          TOK_ALIGN       },
127     { "AND",            TOK_BAND        },
128     { "ASCIIZ",         TOK_ASCIIZ      },
129     { "AUTOIMPORT",     TOK_AUTOIMPORT  },
130     { "BITAND",         TOK_AND         },
131     { "BITNOT",         TOK_NOT         },
132     { "BITOR",          TOK_OR          },
133     { "BITXOR",         TOK_XOR         },
134     { "BLANK",          TOK_BLANK       },
135     { "BSS",            TOK_BSS         },
136     { "BYTE",           TOK_BYTE        },
137     { "CASE",           TOK_CASE        },
138     { "CODE",           TOK_CODE        },
139     { "CONCAT",         TOK_CONCAT      },
140     { "CONST",          TOK_CONST       },
141     { "CPU",            TOK_CPU         },
142     { "DATA",           TOK_DATA        },
143     { "DBYT",           TOK_DBYT        },
144     { "DEBUGINFO",      TOK_DEBUGINFO   },
145     { "DEF",            TOK_DEFINED     },
146     { "DEFINE",         TOK_DEFINE      },
147     { "DEFINED",        TOK_DEFINED     },
148     { "DWORD",          TOK_DWORD       },
149     { "ELSE",           TOK_ELSE        },
150     { "ELSEIF",         TOK_ELSEIF      },
151     { "END",            TOK_END         },
152     { "ENDIF",          TOK_ENDIF       },
153     { "ENDMAC",         TOK_ENDMACRO    },
154     { "ENDMACRO",       TOK_ENDMACRO    },
155     { "ENDPROC",        TOK_ENDPROC     },
156     { "ENDREP",         TOK_ENDREP      },
157     { "ENDREPEAT",      TOK_ENDREP      },
158     { "ERROR",          TOK_ERROR       },
159     { "EXITMAC",        TOK_EXITMACRO   },
160     { "EXITMACRO",      TOK_EXITMACRO   },
161     { "EXPORT",         TOK_EXPORT      },
162     { "EXPORTZP",       TOK_EXPORTZP    },
163     { "FARADDR",        TOK_FARADDR     },
164     { "FEATURE",        TOK_FEATURE     },
165     { "FILEOPT",        TOK_FILEOPT     },
166     { "FOPT",           TOK_FILEOPT     },
167     { "GLOBAL",         TOK_GLOBAL      },
168     { "GLOBALZP",       TOK_GLOBALZP    },
169     { "I16",            TOK_I16         },
170     { "I8",             TOK_I8          },
171     { "IF",             TOK_IF          },
172     { "IFBLANK",        TOK_IFBLANK     },
173     { "IFCONST",        TOK_IFCONST     },
174     { "IFDEF",          TOK_IFDEF       },
175     { "IFNBLANK",       TOK_IFNBLANK    },
176     { "IFNCONST",       TOK_IFNCONST    },
177     { "IFNDEF",         TOK_IFNDEF      },
178     { "IFNREF",         TOK_IFNREF      },
179     { "IFP02",          TOK_IFP02       },
180     { "IFP816",         TOK_IFP816      },
181     { "IFPC02",         TOK_IFPC02      },
182     { "IFREF",          TOK_IFREF       },
183     { "IMPORT",         TOK_IMPORT      },
184     { "IMPORTZP",       TOK_IMPORTZP    },
185     { "INCBIN",         TOK_INCBIN      },
186     { "INCLUDE",        TOK_INCLUDE     },
187     { "LEFT",           TOK_LEFT        },
188     { "LINECONT",       TOK_LINECONT    },
189     { "LIST",           TOK_LIST        },
190     { "LISTBYTES",      TOK_LISTBYTES   },
191     { "LOCAL",          TOK_LOCAL       },
192     { "LOCALCHAR",      TOK_LOCALCHAR   },
193     { "MAC",            TOK_MACRO       },
194     { "MACPACK",        TOK_MACPACK     },
195     { "MACRO",          TOK_MACRO       },
196     { "MATCH",          TOK_MATCH       },
197     { "MID",            TOK_MID         },
198     { "MOD",            TOK_MOD         },
199     { "NOT",            TOK_BNOT        },
200     { "NULL",           TOK_NULL        },
201     { "OR",             TOK_BOR         },
202     { "ORG",            TOK_ORG         },
203     { "OUT",            TOK_OUT         },
204     { "P02",            TOK_P02         },
205     { "P816",           TOK_P816        },
206     { "PAGELEN",        TOK_PAGELENGTH  },
207     { "PAGELENGTH",     TOK_PAGELENGTH  },
208     { "PARAMCOUNT",     TOK_PARAMCOUNT  },
209     { "PC02",           TOK_PC02        },
210     { "PROC",           TOK_PROC        },
211     { "REF",            TOK_REFERENCED  },
212     { "REFERENCED",     TOK_REFERENCED  },
213     { "RELOC",          TOK_RELOC       },
214     { "REPEAT",         TOK_REPEAT      },
215     { "RES",            TOK_RES         },
216     { "RIGHT",          TOK_RIGHT       },
217     { "RODATA",         TOK_RODATA      },
218     { "SEGMENT",        TOK_SEGMENT     },
219     { "SHL",            TOK_SHL         },
220     { "SHR",            TOK_SHR         },
221     { "SMART",          TOK_SMART       },
222     { "STRAT",          TOK_STRAT       },
223     { "STRING",         TOK_STRING      },
224     { "STRLEN",         TOK_STRLEN      },
225     { "SUNPLUS",        TOK_SUNPLUS     },
226     { "TCOUNT",         TOK_TCOUNT      },
227     { "WORD",           TOK_WORD        },
228     { "XMATCH",         TOK_XMATCH      },
229     { "XOR",            TOK_BXOR        },
230     { "ZEROPAGE",       TOK_ZEROPAGE    },
231 };
232
233
234
235 /*****************************************************************************/
236 /*                                 Forwards                                  */
237 /*****************************************************************************/
238
239
240
241 static void NextChar (void);
242 /* Read the next character from the input file */
243
244
245
246 /*****************************************************************************/
247 /*                    Character classification functions                     */
248 /*****************************************************************************/
249
250
251
252 static int IsBlank (int C)
253 /* Return true if the character is a blank or tab */
254 {
255     return (C == ' ' || C == '\t');
256 }
257
258
259
260 static int IsDigit (int C)
261 /* Return true if the character is a digit */
262 {
263     return isdigit (C);
264 }
265
266
267
268 static int IsXDigit (int C)
269 /* Return true if the character is a hexadecimal digit */
270 {
271     return isxdigit (C);
272 }
273
274
275
276 static int IsDDigit (int C)
277 /* Return true if the character is a dual digit */
278 {
279     return (C == '0' || C == '1');
280 }
281
282
283
284 static int IsIdChar (int C)
285 /* Return true if the character is a valid character for an identifier */
286 {
287     return isalnum (C)                  ||
288            (C == '_')                   ||
289            (C == '@' && AtInIdents)     ||
290            (C == '$' && DollarInIdents);
291 }
292
293
294
295 static int IsIdStart (int C)
296 /* Return true if the character may start an identifier */
297 {
298     return isalpha (C) || C == '_';
299 }
300
301
302
303 /*****************************************************************************/
304 /*                                   Code                                    */
305 /*****************************************************************************/
306
307
308
309 const char* GetFileName (unsigned char Name)
310 /* Get the name of a file where the name index is known */
311 {
312     PRECONDITION (Name <= FileCount);
313     if (Name == 0) {
314         /* Name was defined outside any file scope, use the name of the first
315          * file instead. Errors are then reported with a file position of
316          * line zero in the first file.
317          */
318         if (FileCount == 0) {
319             /* No files defined until now */
320             return "(outside file scope)";
321         } else {
322             return Files [0].Name;
323         }
324     } else {
325         return Files [Name-1].Name;
326     }
327 }
328
329
330
331 void NewInputFile (const char* Name)
332 /* Open a new input file */
333 {
334     InputFile* I;
335     FILE* F;
336
337     /* Check for nested include overflow */
338     if (FileCount >= MAX_INPUT_FILES) {
339         Fatal (FAT_MAX_INPUT_FILES);
340     }
341
342     /* First try to open the file */
343     F = fopen (Name, "r");
344     if (F == 0) {
345
346         char* PathName;
347
348         /* Error (fatal error if this is the main file) */
349         if (ICount == 0) {
350             Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno));
351         }
352
353         /* We are on include level. Search for the file in the include
354          * directories.
355          */
356         PathName = FindInclude (Name);
357         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
358             /* Not found or cannot open, print an error and bail out */
359             Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
360         }
361
362         /* Free the allocated memory */
363         xfree (PathName);
364
365     }
366
367     /* check again if we do now have an open file */
368     if (F != 0) {
369
370         /* Stat the file and remember the values */
371         struct stat Buf;
372         if (fstat (fileno (F), &Buf) != 0) {
373             Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno));
374         }
375         Files [FileCount].MTime = Buf.st_mtime;
376         Files [FileCount].Size  = Buf.st_size;
377         Files [FileCount].Name  = xstrdup (Name);
378         ++FileCount;
379
380         /* Create a new state variable and initialize it */
381         I           = xmalloc (sizeof (*I));
382         I->F        = F;
383         I->Pos.Line = 0;
384         I->Pos.Col  = 0;
385         I->Pos.Name = FileCount;
386         I->Tok      = Tok;
387         I->C        = C;
388         I->Line[0]  = '\0';
389
390         /* Use the new file */
391         I->Next     = IFile;
392         IFile       = I;
393         ++ICount;
394
395         /* Prime the pump */
396         NextChar ();
397     }
398 }
399
400
401
402 void DoneInputFile (void)
403 /* Close the current input file */
404 {
405     InputFile* I;
406
407     /* Restore the old token */
408     Tok = IFile->Tok;
409     C   = IFile->C;
410
411     /* Save a pointer to the current struct, then set it back */
412     I     = IFile;
413     IFile = I->Next;
414
415     /* Cleanup the current stuff */
416     fclose (I->F);
417     xfree (I);
418     --ICount;
419 }
420
421
422
423 void NewInputData (const char* Data, int Malloced)
424 /* Add a chunk of input data to the input stream */
425 {
426     InputData* I;
427
428     /* Create a new state variable and initialize it */
429     I           = xmalloc (sizeof (*I));
430     I->Data     = Data;
431     I->Pos      = Data;
432     I->Malloced = Malloced;
433     I->Tok      = Tok;
434     I->C        = C;
435
436     /* Use the new data */
437     I->Next     = IData;
438     IData       = I;
439
440     /* Prime the pump */
441     NextChar ();
442 }
443
444
445
446 static void DoneInputData (void)
447 /* End the current input data stream */
448 {
449     InputData* I;
450
451     /* Restore the old token */
452     Tok = IData->Tok;
453     C   = IData->C;
454
455     /* Save a pointer to the current struct, then set it back */
456     I     = IData;
457     IData = I->Next;
458
459     /* Cleanup the current stuff */
460     if (I->Malloced) {
461         xfree (I->Data);
462     }
463     xfree (I);
464 }
465
466
467
468 static unsigned DigitVal (unsigned char C)
469 /* Convert a digit into it's numerical representation */
470 {
471     if (IsDigit (C)) {
472         return C - '0';
473     } else {
474         return toupper (C) - 'A' + 10;
475     }
476 }
477
478
479
480 static void NextChar (void)
481 /* Read the next character from the input file */
482 {
483     /* If we have an input data structure, read from there */
484     if (IData) {
485
486         C = *IData->Pos++;
487         if (C == '\0') {
488             /* End of input data, will set to last file char */
489             DoneInputData ();
490         }
491
492     } else {
493
494         /* Check for end of line, read the next line if needed */
495         while (IFile->Line [IFile->Pos.Col] == '\0') {
496
497             /* End of current line reached, read next line */
498             if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) {
499                 /* End of file. Add an empty line to the listing. This is a
500                  * small hack needed to keep the PC output in sync.
501                  */
502                 NewListingLine ("", IFile->Pos.Name, ICount);
503                 C = EOF;
504                 return;
505             }
506
507             /* One more line */
508             IFile->Pos.Line++;
509             IFile->Pos.Col = 0;
510
511             /* Remember the new line for the listing */
512             NewListingLine (IFile->Line, IFile->Pos.Name, ICount);
513
514         }
515
516         /* Return the next character from the file */
517         C = IFile->Line [IFile->Pos.Col++];
518
519     }
520 }
521
522
523
524 void UpcaseSVal (void)
525 /* Make SVal upper case */
526 {
527     unsigned I = 0;
528     while (SVal [I]) {
529         SVal [I] = toupper (SVal [I]);
530         ++I;
531     }
532 }
533
534
535
536 static int CmpDotKeyword (const void* K1, const void* K2)
537 /* Compare function for the dot keyword search */
538 {
539     return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
540 }
541
542
543
544 static unsigned char FindDotKeyword (void)
545 /* Find the dot keyword in SVal. Return the corresponding token if found,
546  * return TOK_NONE if not found.
547  */
548 {
549     static const struct DotKeyword K = { SVal, 0 };
550     struct DotKeyword* R;
551
552     /* If we aren't in ignore case mode, we have to uppercase the keyword */
553     if (!IgnoreCase) {
554         UpcaseSVal ();
555     }
556
557     /* Search for the keyword */
558     R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
559                  sizeof (DotKeywords [0]), CmpDotKeyword);
560     if (R != 0) {
561         return R->Tok;
562     } else {
563         return TOK_NONE;
564     }
565 }
566
567
568
569 static void ReadIdent (void)
570 /* Read an identifier from the current input position into Ident. It is
571  * assumed that the first character has already been checked.
572  */
573 {
574     /* Read the identifier */
575     unsigned I = 0;
576     do {
577         if (I < MAX_STR_LEN) {
578             SVal [I++] = C;
579         }
580         NextChar ();
581     } while (IsIdChar (C));
582     SVal [I] = '\0';
583
584     /* If we should ignore case, convert the identifier to upper case */
585     if (IgnoreCase) {
586         UpcaseSVal ();
587     }
588 }
589
590
591
592 static unsigned ReadStringConst (int StringTerm)
593 /* Read a string constant into SVal. Check for maximum string length and all
594  * other stuff. The length of the string is returned.
595  */
596 {
597     unsigned I;
598
599     /* Skip the leading string terminator */
600     NextChar ();
601
602     /* Read the string */
603     I = 0;
604     while (1) {
605         if (C == StringTerm) {
606             break;
607         }
608         if (C == '\n' || C == EOF) {
609             Error (ERR_NEWLINE_IN_STRING);
610             break;
611         }
612
613         /* Check for string length, print an error message once */
614         if (I == MAX_STR_LEN) {
615             Error (ERR_STRING_TOO_LONG);
616         } else if (I < MAX_STR_LEN) {
617             SVal [I] = C;
618         }
619         ++I;
620
621         /* Skip the character */
622         NextChar ();
623     }
624
625     /* Skip the trailing terminator */
626     NextChar ();
627
628     /* Terminate the string */
629     if (I >= MAX_STR_LEN) {
630         I = MAX_STR_LEN;
631     }
632     SVal [I] = '\0';
633
634     /* Return the length of the string */
635     return I;
636 }
637
638
639
640 void NextRawTok (void)
641 /* Read the next raw token from the input stream */
642 {
643     /* If we've a forced end of assembly, don't read further */
644     if (ForcedEnd) {
645         Tok = TOK_EOF;
646         return;
647     }
648
649 Restart:
650     /* Check if we have tokens from another input source */
651     if (InputFromStack ()) {
652         return;
653     }
654
655 Again:
656     /* Skip whitespace, remember if we had some */
657     if ((WS = IsBlank (C)) != 0) {
658         do {
659             NextChar ();
660         } while (IsBlank (C));
661     }
662
663     /* If we're reading from the file, update the location from where the
664      * next token will be read. If we're reading from input data, keep the
665      * current position.
666      */
667     if (IData == 0) {
668         CurPos = IFile->Pos;
669     }
670
671     /* Hex number or PC symbol? */
672     if (C == '$') {
673         NextChar ();
674
675         /* Hex digit must follow or DollarIsPC must be enabled */
676         if (!IsXDigit (C)) {
677             if (DollarIsPC) {
678                 Tok = TOK_PC;
679                 return;
680             } else {
681                 Error (ERR_HEX_DIGIT_EXPECTED);
682             }
683         }
684
685         /* Read the number */
686         IVal = 0;
687         while (IsXDigit (C)) {
688             if (IVal & 0xF0000000) {
689                 Error (ERR_NUM_OVERFLOW);
690                 IVal = 0;
691             }
692             IVal = (IVal << 4) + DigitVal (C);
693             NextChar ();
694         }
695
696         /* This is an integer constant */
697         Tok = TOK_INTCON;
698         return;
699     }
700
701     /* Dual number? */
702     if (C == '%') {
703         NextChar ();
704
705         /* 0 or 1 must follow */
706         if (!IsDDigit (C)) {
707             Error (ERR_01_EXPECTED);
708         }
709
710         /* Read the number */
711         IVal = 0;
712         while (IsDDigit (C)) {
713             if (IVal & 0x80000000) {
714                 Error (ERR_NUM_OVERFLOW);
715                 IVal = 0;
716             }
717             IVal = (IVal << 1) + DigitVal (C);
718             NextChar ();
719         }
720
721         /* This is an integer constant */
722         Tok = TOK_INTCON;
723         return;
724     }
725
726     /* Decimal number? */
727     if (IsDigit (C)) {
728
729         /* Read the number */
730         IVal = 0;
731         while (IsDigit (C)) {
732             if (IVal > (0xFFFFFFFF / 10)) {
733                 Error (ERR_NUM_OVERFLOW);
734                 IVal = 0;
735             }
736             IVal = (IVal * 10) + DigitVal (C);
737             NextChar ();
738         }
739
740         /* This is an integer constant */
741         Tok = TOK_INTCON;
742         return;
743     }
744
745     /* Control command? */
746     if (C == '.') {
747
748         NextChar ();
749
750         if (!IsIdStart (C)) {
751             Error (ERR_PSEUDO_EXPECTED);
752             /* Try to read an identifier */
753             goto Again;
754         }
755
756         /* Read the identifier */
757         ReadIdent ();
758
759         /* Search the keyword */
760         Tok = FindDotKeyword ();
761         if (Tok == TOK_NONE) {
762             /* Not found */
763             Error (ERR_PSEUDO_EXPECTED);
764             goto Again;
765         }
766         return;
767     }
768
769     /* Local symbol? */
770     if (C == LocalStart) {
771
772         /* Read the identifier */
773         ReadIdent ();
774
775         /* Start character alone is not enough */
776         if (SVal [1] == '\0') {
777             Error (ERR_IDENT_EXPECTED);
778             goto Again;
779         }
780
781         /* An identifier */
782         Tok = TOK_IDENT;
783         return;
784     }
785
786
787     /* Identifier or keyword? */
788     if (IsIdStart (C)) {
789
790         /* Read the identifier */
791         ReadIdent ();
792
793         /* Check for special names */
794         if (SVal [1] == '\0') {
795             switch (toupper (SVal [0])) {
796
797                 case 'A':
798                     Tok = TOK_A;
799                     return;
800
801                 case 'X':
802                     Tok = TOK_X;
803                     return;
804
805                 case 'Y':
806                     Tok = TOK_Y;
807                     return;
808
809                 case 'S':
810                     Tok = TOK_S;
811                     return;
812
813                 default:
814                     Tok = TOK_IDENT;
815                     return;
816             }
817         }
818
819         /* Search for an opcode */
820         IVal = FindInstruction (SVal);
821         if (IVal >= 0) {
822             /* This is a mnemonic */
823             Tok = TOK_MNEMO;
824         } else if (IsDefine (SVal)) {
825             /* This is a define style macro - expand it */
826             MacExpandStart ();
827             goto Restart;
828         } else {
829             /* An identifier */
830             Tok = TOK_IDENT;
831         }
832         return;
833     }
834
835     /* Ok, let's do the switch */
836 CharAgain:
837     switch (C) {
838
839         case '+':
840             NextChar ();
841             Tok = TOK_PLUS;
842             return;
843
844         case '-':
845             NextChar ();
846             Tok = TOK_MINUS;
847             return;
848
849         case '/':
850             NextChar ();
851             Tok = TOK_DIV;
852             return;
853
854         case '*':
855             NextChar ();
856             Tok = TOK_MUL;
857             return;
858
859         case '^':
860             NextChar ();
861             Tok = TOK_XOR;
862             return;
863
864         case '&':
865             NextChar ();
866             if (C == '&') {
867                 NextChar ();
868                 Tok = TOK_BAND;
869             } else {
870                 Tok = TOK_AND;
871             }
872             return;
873
874         case '|':
875             NextChar ();
876             if (C == '|') {
877                 NextChar ();
878                 Tok = TOK_BOR;
879             } else {
880                 Tok = TOK_OR;
881             }
882             return;
883
884         case ':':
885             NextChar ();
886             switch (C) {
887
888                 case ':':
889                     NextChar ();
890                     Tok = TOK_NAMESPACE;
891                     break;
892
893                 case '-':
894                     IVal = 0;
895                     do {
896                         --IVal;
897                         NextChar ();
898                     } while (C == '-');
899                     Tok = TOK_ULABEL;
900                     break;
901
902                 case '+':
903                     IVal = 0;
904                     do {
905                         ++IVal;
906                         NextChar ();
907                     } while (C == '+');
908                     Tok = TOK_ULABEL;
909                     break;
910
911                 default:
912                     Tok = TOK_COLON;
913                     break;
914             }
915             return;
916
917         case ',':
918             NextChar ();
919             Tok = TOK_COMMA;
920             return;
921
922         case ';':
923             NextChar ();
924             while (C != '\n' && C != EOF) {
925                 NextChar ();
926             }
927             goto CharAgain;
928
929         case '#':
930             NextChar ();
931             Tok = TOK_HASH;
932             return;
933
934         case '(':
935             NextChar ();
936             Tok = TOK_LPAREN;
937             return;
938
939         case ')':
940             NextChar ();
941             Tok = TOK_RPAREN;
942             return;
943
944         case '[':
945             NextChar ();
946             Tok = TOK_LBRACK;
947             return;
948
949         case ']':
950             NextChar ();
951             Tok = TOK_RBRACK;
952             return;
953
954         case '<':
955             NextChar ();
956             if (C == '=') {
957                 NextChar ();
958                 Tok = TOK_LE;
959             } else if (C == '<') {
960                 NextChar ();
961                 Tok = TOK_SHL;
962             } else if (C == '>') {
963                 NextChar ();
964                 Tok = TOK_NE;
965             } else {
966                 Tok = TOK_LT;
967             }
968             return;
969
970         case '=':
971             NextChar ();
972             Tok = TOK_EQ;
973             return;
974
975         case '!':
976             NextChar ();
977             Tok = TOK_BNOT;
978             return;
979
980         case '>':
981             NextChar ();
982             if (C == '=') {
983                 NextChar ();
984                 Tok = TOK_GE;
985             } else if (C == '>') {
986                 NextChar ();
987                 Tok = TOK_SHR;
988             } else {
989                 Tok = TOK_GT;
990             }
991             return;
992
993         case '~':
994             NextChar ();
995             Tok = TOK_NOT;
996             return;
997
998         case '\'':
999             /* Hack: If we allow ' as terminating character for strings, read
1000              * the following stuff as a string, and check for a one character
1001              * string later.
1002              */
1003             if (LooseStringTerm) {
1004                 if (ReadStringConst ('\'') == 1) {
1005                     IVal = SVal[0];
1006                     Tok = TOK_CHARCON;
1007                 } else {
1008                     Tok = TOK_STRCON;
1009                 }
1010             } else {
1011                 /* Always a character constant */
1012                 NextChar ();
1013                 if (C == '\n' || C == EOF) {
1014                     Error (ERR_ILLEGAL_CHARCON);
1015                     goto CharAgain;
1016                 }
1017                 IVal = C;
1018                 Tok = TOK_CHARCON;
1019                 NextChar ();
1020                 if (C != '\'') {
1021                     Error (ERR_ILLEGAL_CHARCON);
1022                 } else {
1023                     NextChar ();
1024                 }
1025             }
1026             return;
1027
1028         case '\"':
1029             ReadStringConst ('\"');
1030             Tok = TOK_STRCON;
1031             return;
1032
1033         case '\\':
1034             /* Line continuation? */
1035             if (LineCont) {
1036                 NextChar ();
1037                 if (C == '\n') {
1038                     /* Handle as white space */
1039                     NextChar ();
1040                     C = ' ';
1041                     goto Again;
1042                 }
1043             }
1044             break;
1045
1046         case '\n':
1047             NextChar ();
1048             Tok = TOK_SEP;
1049             return;
1050
1051         case EOF:
1052             /* Check if we have any open .IFs in this file */
1053             CheckOpenIfs ();
1054             /* Check if we have any open token lists in this file */
1055             CheckInputStack ();
1056
1057             /* If this was an include file, then close it and handle like a
1058              * separator. Do not close the main file, but return EOF.
1059              */
1060             if (ICount > 1) {
1061                 DoneInputFile ();
1062             } else {
1063                 Tok = TOK_EOF;
1064             }
1065             return;
1066
1067     }
1068
1069     /* If we go here, we could not identify the current character. Skip it
1070      * and try again.
1071      */
1072     Error (ERR_INVALID_CHAR, C & 0xFF);
1073     NextChar ();
1074     goto Again;
1075 }
1076
1077
1078
1079 int TokHasSVal (enum Token Tok)
1080 /* Return true if the given token has an attached SVal */
1081 {
1082     return (Tok == TOK_IDENT || Tok == TOK_STRCON);
1083 }
1084
1085
1086
1087 int TokHasIVal (enum Token Tok)
1088 /* Return true if the given token has an attached IVal */
1089 {
1090     return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_MNEMO);
1091 }
1092
1093
1094
1095 int GetSubKey (const char** Keys, unsigned Count)
1096 /* Search for a subkey in a table of keywords. The current token must be an
1097  * identifier and all keys must be in upper case. The identifier will be
1098  * uppercased in the process. The function returns the index of the keyword,
1099  * or -1 if the keyword was not found.
1100  */
1101 {
1102     unsigned I;
1103
1104     /* Must have an identifier */
1105     PRECONDITION (Tok == TOK_IDENT);
1106
1107     /* If we aren't in ignore case mode, we have to uppercase the identifier */
1108     if (!IgnoreCase) {
1109         UpcaseSVal ();
1110     }
1111
1112     /* Do a linear search (a binary search is not worth the effort) */
1113     for (I = 0; I < Count; ++I) {
1114         if (strcmp (SVal, Keys [I]) == 0) {
1115             /* Found it */
1116             return I;
1117         }
1118     }
1119
1120     /* Not found */
1121     return -1;
1122 }
1123
1124
1125
1126 void WriteFiles (void)
1127 /* Write the list of input files to the object file */
1128 {
1129     unsigned I;
1130
1131     /* Tell the obj file module that we're about to start the file list */
1132     ObjStartFiles ();
1133
1134     /* Write the file count */
1135     ObjWrite8 (FileCount);
1136
1137     /* Write the file data */
1138     for (I = 0; I < FileCount; ++I) {
1139         ObjWrite32 (Files [I].MTime);
1140         ObjWrite32 (Files [I].Size);
1141         ObjWriteStr (Files [I].Name);
1142     }
1143
1144     /* Done writing files */
1145     ObjEndFiles ();
1146 }
1147
1148
1149
1150 void InitScanner (const char* InFile)
1151 /* Initialize the scanner, open the given input file */
1152 {
1153     /* Open the input file */
1154     NewInputFile (InFile);
1155 }
1156
1157
1158
1159 void DoneScanner (void)
1160 /* Release scanner resources */
1161 {
1162     DoneInputFile ();
1163 }
1164
1165
1166