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