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