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