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