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