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