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