]> git.sur5r.net Git - cc65/blob - src/ca65/scanner.c
Added the .TCOUNT function
[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     { "STRING",         TOK_STRING      },
223     { "SUNPLUS",        TOK_SUNPLUS     },
224     { "TCOUNT",         TOK_TCOUNT      },
225     { "WORD",           TOK_WORD        },
226     { "XMATCH",         TOK_XMATCH      },
227     { "XOR",            TOK_BXOR        },
228     { "ZEROPAGE",       TOK_ZEROPAGE    },
229 };
230
231
232
233 /*****************************************************************************/
234 /*                                 Forwards                                  */
235 /*****************************************************************************/
236
237
238
239 static void NextChar (void);
240 /* Read the next character from the input file */
241
242
243
244 /*****************************************************************************/
245 /*                    Character classification functions                     */
246 /*****************************************************************************/
247
248
249
250 static int IsBlank (int C)
251 /* Return true if the character is a blank or tab */
252 {
253     return (C == ' ' || C == '\t');
254 }
255
256
257
258 static int IsDigit (int C)
259 /* Return true if the character is a digit */
260 {
261     return isdigit (C);
262 }
263
264
265
266 static int IsXDigit (int C)
267 /* Return true if the character is a hexadecimal digit */
268 {
269     return isxdigit (C);
270 }
271
272
273
274 static int IsDDigit (int C)
275 /* Return true if the character is a dual digit */
276 {
277     return (C == '0' || C == '1');
278 }
279
280
281
282 static int IsIdChar (int C)
283 /* Return true if the character is a valid character for an identifier */
284 {
285     return isalnum (C)                  ||
286            (C == '_')                   ||
287            (C == '@' && AtInIdents)     ||
288            (C == '$' && DollarInIdents);
289 }
290
291
292
293 static int IsIdStart (int C)
294 /* Return true if the character may start an identifier */
295 {
296     return isalpha (C) || C == '_';
297 }
298
299
300
301 /*****************************************************************************/
302 /*                                   Code                                    */
303 /*****************************************************************************/
304
305
306
307 const char* GetFileName (unsigned char Name)
308 /* Get the name of a file where the name index is known */
309 {
310     PRECONDITION (Name <= FileCount);
311     if (Name == 0) {
312         /* Name was defined outside any file scope, use the name of the first
313          * file instead. Errors are then reported with a file position of
314          * line zero in the first file.
315          */
316         if (FileCount == 0) {
317             /* No files defined until now */
318             return "(outside file scope)";
319         } else {
320             return Files [0].Name;
321         }
322     } else {
323         return Files [Name-1].Name;
324     }
325 }
326
327
328
329 void NewInputFile (const char* Name)
330 /* Open a new input file */
331 {
332     InputFile* I;
333     FILE* F;
334
335     /* Check for nested include overflow */
336     if (FileCount >= MAX_INPUT_FILES) {
337         Fatal (FAT_MAX_INPUT_FILES);
338     }
339
340     /* First try to open the file */
341     F = fopen (Name, "r");
342     if (F == 0) {
343
344         char* PathName;
345
346         /* Error (fatal error if this is the main file) */
347         if (ICount == 0) {
348             Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno));
349         }
350
351         /* We are on include level. Search for the file in the include
352          * directories.
353          */
354         PathName = FindInclude (Name);
355         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
356             /* Not found or cannot open, print an error and bail out */
357             Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
358         }
359
360         /* Free the allocated memory */
361         xfree (PathName);
362
363     }
364
365     /* check again if we do now have an open file */
366     if (F != 0) {
367
368         /* Stat the file and remember the values */
369         struct stat Buf;
370         if (fstat (fileno (F), &Buf) != 0) {
371             Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno));
372         }
373         Files [FileCount].MTime = Buf.st_mtime;
374         Files [FileCount].Size  = Buf.st_size;
375         Files [FileCount].Name  = xstrdup (Name);
376         ++FileCount;
377
378         /* Create a new state variable and initialize it */
379         I           = xmalloc (sizeof (*I));
380         I->F        = F;
381         I->Pos.Line = 0;
382         I->Pos.Col  = 0;
383         I->Pos.Name = FileCount;
384         I->Tok      = Tok;
385         I->C        = C;
386         I->Line[0]  = '\0';
387
388         /* Use the new file */
389         I->Next     = IFile;
390         IFile       = I;
391         ++ICount;
392
393         /* Prime the pump */
394         NextChar ();
395     }
396 }
397
398
399
400 void DoneInputFile (void)
401 /* Close the current input file */
402 {
403     InputFile* I;
404
405     /* Restore the old token */
406     Tok = IFile->Tok;
407     C   = IFile->C;
408
409     /* Save a pointer to the current struct, then set it back */
410     I     = IFile;
411     IFile = I->Next;
412
413     /* Cleanup the current stuff */
414     fclose (I->F);
415     xfree (I);
416     --ICount;
417 }
418
419
420
421 void NewInputData (const char* Data, int Malloced)
422 /* Add a chunk of input data to the input stream */
423 {
424     InputData* I;
425
426     /* Create a new state variable and initialize it */
427     I           = xmalloc (sizeof (*I));
428     I->Data     = Data;
429     I->Pos      = Data;
430     I->Malloced = Malloced;
431     I->Tok      = Tok;
432     I->C        = C;
433
434     /* Use the new data */
435     I->Next     = IData;
436     IData       = I;
437
438     /* Prime the pump */
439     NextChar ();
440 }
441
442
443
444 static void DoneInputData (void)
445 /* End the current input data stream */
446 {
447     InputData* I;
448
449     /* Restore the old token */
450     Tok = IData->Tok;
451     C   = IData->C;
452
453     /* Save a pointer to the current struct, then set it back */
454     I     = IData;
455     IData = I->Next;
456
457     /* Cleanup the current stuff */
458     if (I->Malloced) {
459         xfree (I->Data);
460     }
461     xfree (I);
462 }
463
464
465
466 static unsigned DigitVal (unsigned char C)
467 /* Convert a digit into it's numerical representation */
468 {
469     if (IsDigit (C)) {
470         return C - '0';
471     } else {
472         return toupper (C) - 'A' + 10;
473     }
474 }
475
476
477
478 static void NextChar (void)
479 /* Read the next character from the input file */
480 {
481     /* If we have an input data structure, read from there */
482     if (IData) {
483
484         C = *IData->Pos++;
485         if (C == '\0') {
486             /* End of input data, will set to last file char */
487             DoneInputData ();
488         }
489
490     } else {
491
492         /* Check for end of line, read the next line if needed */
493         while (IFile->Line [IFile->Pos.Col] == '\0') {
494
495             /* End of current line reached, read next line */
496             if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) {
497                 /* End of file. Add an empty line to the listing. This is a
498                  * small hack needed to keep the PC output in sync.
499                  */
500                 NewListingLine ("", IFile->Pos.Name, ICount);
501                 C = EOF;
502                 return;
503             }
504
505             /* One more line */
506             IFile->Pos.Line++;
507             IFile->Pos.Col = 0;
508
509             /* Remember the new line for the listing */
510             NewListingLine (IFile->Line, IFile->Pos.Name, ICount);
511
512         }
513
514         /* Return the next character from the file */
515         C = IFile->Line [IFile->Pos.Col++];
516
517     }
518 }
519
520
521
522 void UpcaseSVal (void)
523 /* Make SVal upper case */
524 {
525     unsigned I = 0;
526     while (SVal [I]) {
527         SVal [I] = toupper (SVal [I]);
528         ++I;
529     }
530 }
531
532
533
534 static int CmpDotKeyword (const void* K1, const void* K2)
535 /* Compare function for the dot keyword search */
536 {
537     return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
538 }
539
540
541
542 static unsigned char FindDotKeyword (void)
543 /* Find the dot keyword in SVal. Return the corresponding token if found,
544  * return TOK_NONE if not found.
545  */
546 {
547     static const struct DotKeyword K = { SVal, 0 };
548     struct DotKeyword* R;
549
550     /* If we aren't in ignore case mode, we have to uppercase the keyword */
551     if (!IgnoreCase) {
552         UpcaseSVal ();
553     }
554
555     /* Search for the keyword */
556     R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
557                  sizeof (DotKeywords [0]), CmpDotKeyword);
558     if (R != 0) {
559         return R->Tok;
560     } else {
561         return TOK_NONE;
562     }
563 }
564
565
566
567 static void ReadIdent (void)
568 /* Read an identifier from the current input position into Ident. It is
569  * assumed that the first character has already been checked.
570  */
571 {
572     /* Read the identifier */
573     unsigned I = 0;
574     do {
575         if (I < MAX_STR_LEN) {
576             SVal [I++] = C;
577         }
578         NextChar ();
579     } while (IsIdChar (C));
580     SVal [I] = '\0';
581
582     /* If we should ignore case, convert the identifier to upper case */
583     if (IgnoreCase) {
584         UpcaseSVal ();
585     }
586 }
587
588
589
590 static unsigned ReadStringConst (int StringTerm)
591 /* Read a string constant into SVal. Check for maximum string length and all
592  * other stuff. The length of the string is returned.
593  */
594 {
595     unsigned I;
596
597     /* Skip the leading string terminator */
598     NextChar ();
599
600     /* Read the string */
601     I = 0;
602     while (1) {
603         if (C == StringTerm) {
604             break;
605         }
606         if (C == '\n' || C == EOF) {
607             Error (ERR_NEWLINE_IN_STRING);
608             break;
609         }
610
611         /* Check for string length, print an error message once */
612         if (I == MAX_STR_LEN) {
613             Error (ERR_STRING_TOO_LONG);
614         } else if (I < MAX_STR_LEN) {
615             SVal [I] = C;
616         }
617         ++I;
618
619         /* Skip the character */
620         NextChar ();
621     }
622
623     /* Skip the trailing terminator */
624     NextChar ();
625
626     /* Terminate the string */
627     if (I >= MAX_STR_LEN) {
628         I = MAX_STR_LEN;
629     }
630     SVal [I] = '\0';
631
632     /* Return the length of the string */
633     return I;
634 }
635
636
637
638 void NextRawTok (void)
639 /* Read the next raw token from the input stream */
640 {
641     /* If we've a forced end of assembly, don't read further */
642     if (ForcedEnd) {
643         Tok = TOK_EOF;
644         return;
645     }
646
647 Restart:
648     /* Check if we have tokens from another input source */
649     if (InputFromStack ()) {
650         return;
651     }
652
653 Again:
654     /* Skip whitespace, remember if we had some */
655     if ((WS = IsBlank (C)) != 0) {
656         do {
657             NextChar ();
658         } while (IsBlank (C));
659     }
660
661     /* If we're reading from the file, update the location from where the
662      * next token will be read. If we're reading from input data, keep the
663      * current position.
664      */
665     if (IData == 0) {
666         CurPos = IFile->Pos;
667     }
668
669     /* Hex number or PC symbol? */
670     if (C == '$') {
671         NextChar ();
672
673         /* Hex digit must follow or DollarIsPC must be enabled */
674         if (!IsXDigit (C)) {
675             if (DollarIsPC) {
676                 Tok = TOK_PC;
677                 return;
678             } else {
679                 Error (ERR_HEX_DIGIT_EXPECTED);
680             }
681         }
682
683         /* Read the number */
684         IVal = 0;
685         while (IsXDigit (C)) {
686             if (IVal & 0xF0000000) {
687                 Error (ERR_NUM_OVERFLOW);
688                 IVal = 0;
689             }
690             IVal = (IVal << 4) + DigitVal (C);
691             NextChar ();
692         }
693
694         /* This is an integer constant */
695         Tok = TOK_INTCON;
696         return;
697     }
698
699     /* Dual number? */
700     if (C == '%') {
701         NextChar ();
702
703         /* 0 or 1 must follow */
704         if (!IsDDigit (C)) {
705             Error (ERR_01_EXPECTED);
706         }
707
708         /* Read the number */
709         IVal = 0;
710         while (IsDDigit (C)) {
711             if (IVal & 0x80000000) {
712                 Error (ERR_NUM_OVERFLOW);
713                 IVal = 0;
714             }
715             IVal = (IVal << 1) + DigitVal (C);
716             NextChar ();
717         }
718
719         /* This is an integer constant */
720         Tok = TOK_INTCON;
721         return;
722     }
723
724     /* Decimal number? */
725     if (IsDigit (C)) {
726
727         /* Read the number */
728         IVal = 0;
729         while (IsDigit (C)) {
730             if (IVal > (0xFFFFFFFF / 10)) {
731                 Error (ERR_NUM_OVERFLOW);
732                 IVal = 0;
733             }
734             IVal = (IVal * 10) + DigitVal (C);
735             NextChar ();
736         }
737
738         /* This is an integer constant */
739         Tok = TOK_INTCON;
740         return;
741     }
742
743     /* Control command? */
744     if (C == '.') {
745
746         NextChar ();
747
748         if (!IsIdStart (C)) {
749             Error (ERR_PSEUDO_EXPECTED);
750             /* Try to read an identifier */
751             goto Again;
752         }
753
754         /* Read the identifier */
755         ReadIdent ();
756
757         /* Search the keyword */
758         Tok = FindDotKeyword ();
759         if (Tok == TOK_NONE) {
760             /* Not found */
761             Error (ERR_PSEUDO_EXPECTED);
762             goto Again;
763         }
764         return;
765     }
766
767     /* Local symbol? */
768     if (C == LocalStart) {
769
770         /* Read the identifier */
771         ReadIdent ();
772
773         /* Start character alone is not enough */
774         if (SVal [1] == '\0') {
775             Error (ERR_IDENT_EXPECTED);
776             goto Again;
777         }
778
779         /* An identifier */
780         Tok = TOK_IDENT;
781         return;
782     }
783
784
785     /* Identifier or keyword? */
786     if (IsIdStart (C)) {
787
788         /* Read the identifier */
789         ReadIdent ();
790
791         /* Check for special names */
792         if (SVal [1] == '\0') {
793             switch (toupper (SVal [0])) {
794
795                 case 'A':
796                     Tok = TOK_A;
797                     return;
798
799                 case 'X':
800                     Tok = TOK_X;
801                     return;
802
803                 case 'Y':
804                     Tok = TOK_Y;
805                     return;
806
807                 case 'S':
808                     Tok = TOK_S;
809                     return;
810
811                 default:
812                     Tok = TOK_IDENT;
813                     return;
814             }
815         }
816
817         /* Search for an opcode */
818         IVal = FindInstruction (SVal);
819         if (IVal >= 0) {
820             /* This is a mnemonic */
821             Tok = TOK_MNEMO;
822         } else if (IsDefine (SVal)) {
823             /* This is a define style macro - expand it */
824             MacExpandStart ();
825             goto Restart;
826         } else {
827             /* An identifier */
828             Tok = TOK_IDENT;
829         }
830         return;
831     }
832
833     /* Ok, let's do the switch */
834 CharAgain:
835     switch (C) {
836
837         case '+':
838             NextChar ();
839             Tok = TOK_PLUS;
840             return;
841
842         case '-':
843             NextChar ();
844             Tok = TOK_MINUS;
845             return;
846
847         case '/':
848             NextChar ();
849             Tok = TOK_DIV;
850             return;
851
852         case '*':
853             NextChar ();
854             Tok = TOK_MUL;
855             return;
856
857         case '^':
858             NextChar ();
859             Tok = TOK_XOR;
860             return;
861
862         case '&':
863             NextChar ();
864             if (C == '&') {
865                 NextChar ();
866                 Tok = TOK_BAND;
867             } else {
868                 Tok = TOK_AND;
869             }
870             return;
871
872         case '|':
873             NextChar ();
874             if (C == '|') {
875                 NextChar ();
876                 Tok = TOK_BOR;
877             } else {
878                 Tok = TOK_OR;
879             }
880             return;
881
882         case ':':
883             NextChar ();
884             switch (C) {
885
886                 case ':':
887                     NextChar ();
888                     Tok = TOK_NAMESPACE;
889                     break;
890
891                 case '-':
892                     IVal = 0;
893                     do {
894                         --IVal;
895                         NextChar ();
896                     } while (C == '-');
897                     Tok = TOK_ULABEL;
898                     break;
899
900                 case '+':
901                     IVal = 0;
902                     do {
903                         ++IVal;
904                         NextChar ();
905                     } while (C == '+');
906                     Tok = TOK_ULABEL;
907                     break;
908
909                 default:
910                     Tok = TOK_COLON;
911                     break;
912             }
913             return;
914
915         case ',':
916             NextChar ();
917             Tok = TOK_COMMA;
918             return;
919
920         case ';':
921             NextChar ();
922             while (C != '\n' && C != EOF) {
923                 NextChar ();
924             }
925             goto CharAgain;
926
927         case '#':
928             NextChar ();
929             Tok = TOK_HASH;
930             return;
931
932         case '(':
933             NextChar ();
934             Tok = TOK_LPAREN;
935             return;
936
937         case ')':
938             NextChar ();
939             Tok = TOK_RPAREN;
940             return;
941
942         case '[':
943             NextChar ();
944             Tok = TOK_LBRACK;
945             return;
946
947         case ']':
948             NextChar ();
949             Tok = TOK_RBRACK;
950             return;
951
952         case '<':
953             NextChar ();
954             if (C == '=') {
955                 NextChar ();
956                 Tok = TOK_LE;
957             } else if (C == '<') {
958                 NextChar ();
959                 Tok = TOK_SHL;
960             } else if (C == '>') {
961                 NextChar ();
962                 Tok = TOK_NE;
963             } else {
964                 Tok = TOK_LT;
965             }
966             return;
967
968         case '=':
969             NextChar ();
970             Tok = TOK_EQ;
971             return;
972
973         case '!':
974             NextChar ();
975             Tok = TOK_BNOT;
976             return;
977
978         case '>':
979             NextChar ();
980             if (C == '=') {
981                 NextChar ();
982                 Tok = TOK_GE;
983             } else if (C == '>') {
984                 NextChar ();
985                 Tok = TOK_SHR;
986             } else {
987                 Tok = TOK_GT;
988             }
989             return;
990
991         case '~':
992             NextChar ();
993             Tok = TOK_NOT;
994             return;
995
996         case '\'':
997             /* Hack: If we allow ' as terminating character for strings, read
998              * the following stuff as a string, and check for a one character
999              * string later.
1000              */
1001             if (LooseStringTerm) {
1002                 if (ReadStringConst ('\'') == 1) {
1003                     IVal = SVal[0];
1004                     Tok = TOK_CHARCON;
1005                 } else {
1006                     Tok = TOK_STRCON;
1007                 }
1008             } else {
1009                 /* Always a character constant */
1010                 NextChar ();
1011                 if (C == '\n' || C == EOF) {
1012                     Error (ERR_ILLEGAL_CHARCON);
1013                     goto CharAgain;
1014                 }
1015                 IVal = C;
1016                 Tok = TOK_CHARCON;
1017                 NextChar ();
1018                 if (C != '\'') {
1019                     Error (ERR_ILLEGAL_CHARCON);
1020                 } else {
1021                     NextChar ();
1022                 }
1023             }
1024             return;
1025
1026         case '\"':
1027             ReadStringConst ('\"');
1028             Tok = TOK_STRCON;
1029             return;
1030
1031         case '\\':
1032             /* Line continuation? */
1033             if (LineCont) {
1034                 NextChar ();
1035                 if (C == '\n') {
1036                     /* Handle as white space */
1037                     NextChar ();
1038                     C = ' ';
1039                     goto Again;
1040                 }
1041             }
1042             break;
1043
1044         case '\n':
1045             NextChar ();
1046             Tok = TOK_SEP;
1047             return;
1048
1049         case EOF:
1050             /* Check if we have any open .IFs in this file */
1051             CheckOpenIfs ();
1052             /* Check if we have any open token lists in this file */
1053             CheckInputStack ();
1054
1055             /* If this was an include file, then close it and handle like a
1056              * separator. Do not close the main file, but return EOF.
1057              */
1058             if (ICount > 1) {
1059                 DoneInputFile ();
1060             } else {
1061                 Tok = TOK_EOF;
1062             }
1063             return;
1064
1065     }
1066
1067     /* If we go here, we could not identify the current character. Skip it
1068      * and try again.
1069      */
1070     Error (ERR_INVALID_CHAR, C & 0xFF);
1071     NextChar ();
1072     goto Again;
1073 }
1074
1075
1076
1077 int TokHasSVal (enum Token Tok)
1078 /* Return true if the given token has an attached SVal */
1079 {
1080     return (Tok == TOK_IDENT || Tok == TOK_STRCON);
1081 }
1082
1083
1084
1085 int TokHasIVal (enum Token Tok)
1086 /* Return true if the given token has an attached IVal */
1087 {
1088     return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_MNEMO);
1089 }
1090
1091
1092
1093 int GetSubKey (const char** Keys, unsigned Count)
1094 /* Search for a subkey in a table of keywords. The current token must be an
1095  * identifier and all keys must be in upper case. The identifier will be
1096  * uppercased in the process. The function returns the index of the keyword,
1097  * or -1 if the keyword was not found.
1098  */
1099 {
1100     unsigned I;
1101
1102     /* Must have an identifier */
1103     PRECONDITION (Tok == TOK_IDENT);
1104
1105     /* If we aren't in ignore case mode, we have to uppercase the identifier */
1106     if (!IgnoreCase) {
1107         UpcaseSVal ();
1108     }
1109
1110     /* Do a linear search (a binary search is not worth the effort) */
1111     for (I = 0; I < Count; ++I) {
1112         if (strcmp (SVal, Keys [I]) == 0) {
1113             /* Found it */
1114             return I;
1115         }
1116     }
1117
1118     /* Not found */
1119     return -1;
1120 }
1121
1122
1123
1124 void WriteFiles (void)
1125 /* Write the list of input files to the object file */
1126 {
1127     unsigned I;
1128
1129     /* Tell the obj file module that we're about to start the file list */
1130     ObjStartFiles ();
1131
1132     /* Write the file count */
1133     ObjWrite8 (FileCount);
1134
1135     /* Write the file data */
1136     for (I = 0; I < FileCount; ++I) {
1137         ObjWrite32 (Files [I].MTime);
1138         ObjWrite32 (Files [I].Size);
1139         ObjWriteStr (Files [I].Name);
1140     }
1141
1142     /* Done writing files */
1143     ObjEndFiles ();
1144 }
1145
1146
1147
1148 void InitScanner (const char* InFile)
1149 /* Initialize the scanner, open the given input file */
1150 {
1151     /* Open the input file */
1152     NewInputFile (InFile);
1153 }
1154
1155
1156
1157 void DoneScanner (void)
1158 /* Release scanner resources */
1159 {
1160     DoneInputFile ();
1161 }
1162
1163
1164