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