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