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