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