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