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