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