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