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