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