]> git.sur5r.net Git - cc65/blob - src/ca65/scanner.c
First implementation of .UNDEF for deleting a macro.
[cc65] / src / ca65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                  The scanner for the ca65 macroassembler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2011, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
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/types.h>          /* EMX needs this */
42 #include <sys/stat.h>
43
44 /* common */
45 #include "addrsize.h"
46 #include "attrib.h"
47 #include "chartype.h"
48 #include "check.h"
49 #include "fname.h"
50 #include "xmalloc.h"
51
52 /* ca65 */
53 #include "condasm.h"
54 #include "error.h"
55 #include "filetab.h"
56 #include "global.h"
57 #include "incpath.h"
58 #include "instr.h"
59 #include "istack.h"
60 #include "listing.h"
61 #include "macro.h"
62 #include "toklist.h"
63 #include "scanner.h"
64
65
66
67 /*****************************************************************************/
68 /*                                   Data                                    */
69 /*****************************************************************************/
70
71
72
73 /* Current input token incl. attributes */
74 Token CurTok = STATIC_TOKEN_INITIALIZER;
75
76 /* Struct to handle include files. */
77 typedef struct InputFile InputFile;
78 struct InputFile {
79     FILE*           F;                  /* Input file descriptor */
80     FilePos         Pos;                /* Position in file */
81     token_t         Tok;                /* Last token */
82     int             C;                  /* Last character */
83     char            Line[256];          /* The current input line */
84     int             IncSearchPath;      /* True if we've added a search path */
85     int             BinSearchPath;      /* True if we've added a search path */
86     InputFile*      Next;               /* Linked list of input files */
87 };
88
89 /* Struct to handle textual input data */
90 typedef struct InputData InputData;
91 struct InputData {
92     char*           Text;               /* Pointer to the text data */
93     const char*     Pos;                /* Pointer to current position */
94     int             Malloced;           /* Memory was malloced */
95     token_t         Tok;                /* Last token */
96     int             C;                  /* Last character */
97     InputData*      Next;               /* Linked list of input data */
98 };
99
100 /* Input source: Either file or data */
101 typedef struct CharSource CharSource;
102
103 /* Set of input functions */
104 typedef struct CharSourceFunctions CharSourceFunctions;
105 struct CharSourceFunctions {
106     void (*MarkStart) (CharSource*);    /* Mark the start pos of a token */
107     void (*NextChar) (CharSource*);     /* Read next char from input */
108     void (*Done) (CharSource*);         /* Close input source */
109 };
110
111 /* Input source: Either file or data */
112 struct CharSource {
113     CharSource*                 Next;   /* Linked list of char sources */
114     token_t                     Tok;    /* Last token */
115     int                         C;      /* Last character */
116     const CharSourceFunctions*  Func;   /* Pointer to function table */
117     union {
118         InputFile               File;   /* File data */
119         InputData               Data;   /* Textual data */
120     }                           V;
121 };
122
123 /* Current input variables */
124 static CharSource* Source       = 0;    /* Current char source */
125 static unsigned     FCount      = 0;    /* Count of input files */
126 static int          C           = 0;    /* Current input character */
127
128 /* Force end of assembly */
129 int               ForcedEnd     = 0;
130
131 /* List of dot keywords with the corresponding tokens */
132 struct DotKeyword {
133     const char* Key;                    /* MUST be first field */
134     token_t     Tok;
135 } DotKeywords [] = {
136     { ".A16",           TOK_A16         },
137     { ".A8",            TOK_A8          },
138     { ".ADDR",          TOK_ADDR        },
139     { ".ALIGN",         TOK_ALIGN       },
140     { ".AND",           TOK_BOOLAND     },
141     { ".ASCIIZ",        TOK_ASCIIZ      },
142     { ".ASSERT",        TOK_ASSERT      },
143     { ".AUTOIMPORT",    TOK_AUTOIMPORT  },
144     { ".BANKBYTE",      TOK_BANKBYTE    },
145     { ".BANKBYTES",     TOK_BANKBYTES   },
146     { ".BITAND",        TOK_AND         },
147     { ".BITNOT",        TOK_NOT         },
148     { ".BITOR",         TOK_OR          },
149     { ".BITXOR",        TOK_XOR         },
150     { ".BLANK",         TOK_BLANK       },
151     { ".BSS",           TOK_BSS         },
152     { ".BYT",           TOK_BYTE        },
153     { ".BYTE",          TOK_BYTE        },
154     { ".CASE",          TOK_CASE        },
155     { ".CHARMAP",       TOK_CHARMAP     },
156     { ".CODE",          TOK_CODE        },
157     { ".CONCAT",        TOK_CONCAT      },
158     { ".CONDES",        TOK_CONDES      },
159     { ".CONST",         TOK_CONST       },
160     { ".CONSTRUCTOR",   TOK_CONSTRUCTOR },
161     { ".CPU",           TOK_CPU         },
162     { ".DATA",          TOK_DATA        },
163     { ".DBG",           TOK_DBG         },
164     { ".DBYT",          TOK_DBYT        },
165     { ".DEBUGINFO",     TOK_DEBUGINFO   },
166     { ".DEF",           TOK_DEFINED     },
167     { ".DEFINE",        TOK_DEFINE      },
168     { ".DEFINED",       TOK_DEFINED     },
169     { ".DESTRUCTOR",    TOK_DESTRUCTOR  },
170     { ".DWORD",         TOK_DWORD       },
171     { ".ELSE",          TOK_ELSE        },
172     { ".ELSEIF",        TOK_ELSEIF      },
173     { ".END",           TOK_END         },
174     { ".ENDENUM",       TOK_ENDENUM     },
175     { ".ENDIF",         TOK_ENDIF       },
176     { ".ENDMAC",        TOK_ENDMACRO    },
177     { ".ENDMACRO",      TOK_ENDMACRO    },
178     { ".ENDPROC",       TOK_ENDPROC     },
179     { ".ENDREP",        TOK_ENDREP      },
180     { ".ENDREPEAT",     TOK_ENDREP      },
181     { ".ENDSCOPE",      TOK_ENDSCOPE    },
182     { ".ENDSTRUCT",     TOK_ENDSTRUCT   },
183     { ".ENDUNION",      TOK_ENDUNION    },
184     { ".ENUM",          TOK_ENUM        },
185     { ".ERROR",         TOK_ERROR       },
186     { ".EXITMAC",       TOK_EXITMACRO   },
187     { ".EXITMACRO",     TOK_EXITMACRO   },
188     { ".EXPORT",        TOK_EXPORT      },
189     { ".EXPORTZP",      TOK_EXPORTZP    },
190     { ".FARADDR",       TOK_FARADDR     },
191     { ".FATAL",         TOK_FATAL       },
192     { ".FEATURE",       TOK_FEATURE     },
193     { ".FILEOPT",       TOK_FILEOPT     },
194     { ".FOPT",          TOK_FILEOPT     },
195     { ".FORCEIMPORT",   TOK_FORCEIMPORT },
196     { ".FORCEWORD",     TOK_FORCEWORD   },
197     { ".GLOBAL",        TOK_GLOBAL      },
198     { ".GLOBALZP",      TOK_GLOBALZP    },
199     { ".HIBYTE",        TOK_HIBYTE      },
200     { ".HIBYTES",       TOK_HIBYTES     },
201     { ".HIWORD",        TOK_HIWORD      },
202     { ".I16",           TOK_I16         },
203     { ".I8",            TOK_I8          },
204     { ".IDENT",         TOK_MAKEIDENT   },
205     { ".IF",            TOK_IF          },
206     { ".IFBLANK",       TOK_IFBLANK     },
207     { ".IFCONST",       TOK_IFCONST     },
208     { ".IFDEF",         TOK_IFDEF       },
209     { ".IFNBLANK",      TOK_IFNBLANK    },
210     { ".IFNCONST",      TOK_IFNCONST    },
211     { ".IFNDEF",        TOK_IFNDEF      },
212     { ".IFNREF",        TOK_IFNREF      },
213     { ".IFP02",         TOK_IFP02       },
214     { ".IFP816",        TOK_IFP816      },
215     { ".IFPC02",        TOK_IFPC02      },
216     { ".IFPSC02",       TOK_IFPSC02     },
217     { ".IFREF",         TOK_IFREF       },
218     { ".IMPORT",        TOK_IMPORT      },
219     { ".IMPORTZP",      TOK_IMPORTZP    },
220     { ".INCBIN",        TOK_INCBIN      },
221     { ".INCLUDE",       TOK_INCLUDE     },
222     { ".INTERRUPTOR",   TOK_INTERRUPTOR },
223     { ".LEFT",          TOK_LEFT        },
224     { ".LINECONT",      TOK_LINECONT    },
225     { ".LIST",          TOK_LIST        },
226     { ".LISTBYTES",     TOK_LISTBYTES   },
227     { ".LOBYTE",        TOK_LOBYTE      },
228     { ".LOBYTES",       TOK_LOBYTES     },
229     { ".LOCAL",         TOK_LOCAL       },
230     { ".LOCALCHAR",     TOK_LOCALCHAR   },
231     { ".LOWORD",        TOK_LOWORD      },
232     { ".MAC",           TOK_MACRO       },
233     { ".MACPACK",       TOK_MACPACK     },
234     { ".MACRO",         TOK_MACRO       },
235     { ".MATCH",         TOK_MATCH       },
236     { ".MAX",           TOK_MAX         },
237     { ".MID",           TOK_MID         },
238     { ".MIN",           TOK_MIN         },
239     { ".MOD",           TOK_MOD         },
240     { ".NOT",           TOK_BOOLNOT     },
241     { ".NULL",          TOK_NULL        },
242     { ".OR",            TOK_BOOLOR      },
243     { ".ORG",           TOK_ORG         },
244     { ".OUT",           TOK_OUT         },
245     { ".P02",           TOK_P02         },
246     { ".P816",          TOK_P816        },
247     { ".PAGELEN",       TOK_PAGELENGTH  },
248     { ".PAGELENGTH",    TOK_PAGELENGTH  },
249     { ".PARAMCOUNT",    TOK_PARAMCOUNT  },
250     { ".PC02",          TOK_PC02        },
251     { ".POPCPU",        TOK_POPCPU      },
252     { ".POPSEG",        TOK_POPSEG      },
253     { ".PROC",          TOK_PROC        },
254     { ".PSC02",         TOK_PSC02       },
255     { ".PUSHCPU",       TOK_PUSHCPU     },
256     { ".PUSHSEG",       TOK_PUSHSEG     },
257     { ".REF",           TOK_REFERENCED  },
258     { ".REFERENCED",    TOK_REFERENCED  },
259     { ".RELOC",         TOK_RELOC       },
260     { ".REPEAT",        TOK_REPEAT      },
261     { ".RES",           TOK_RES         },
262     { ".RIGHT",         TOK_RIGHT       },
263     { ".RODATA",        TOK_RODATA      },
264     { ".SCOPE",         TOK_SCOPE       },
265     { ".SEGMENT",       TOK_SEGMENT     },
266     { ".SET",           TOK_SET         },
267     { ".SETCPU",        TOK_SETCPU      },
268     { ".SHL",           TOK_SHL         },
269     { ".SHR",           TOK_SHR         },
270     { ".SIZEOF",        TOK_SIZEOF      },
271     { ".SMART",         TOK_SMART       },
272     { ".SPRINTF",       TOK_SPRINTF     },
273     { ".STRAT",         TOK_STRAT       },
274     { ".STRING",        TOK_STRING      },
275     { ".STRLEN",        TOK_STRLEN      },
276     { ".STRUCT",        TOK_STRUCT      },
277     { ".SUNPLUS",       TOK_SUNPLUS     },
278     { ".TAG",           TOK_TAG         },
279     { ".TCOUNT",        TOK_TCOUNT      },
280     { ".TIME",          TOK_TIME        },
281     { ".UNDEF",         TOK_UNDEF       },
282     { ".UNDEFINE",      TOK_UNDEF       },
283     { ".UNION",         TOK_UNION       },
284     { ".VERSION",       TOK_VERSION     },
285     { ".WARNING",       TOK_WARNING     },
286     { ".WORD",          TOK_WORD        },
287     { ".XMATCH",        TOK_XMATCH      },
288     { ".XOR",           TOK_BOOLXOR     },
289     { ".ZEROPAGE",      TOK_ZEROPAGE    },
290 };
291
292
293
294 /*****************************************************************************/
295 /*                            CharSource functions                           */
296 /*****************************************************************************/
297
298
299
300 static void UseCharSource (CharSource* S)
301 /* Initialize a new input source and start to use it. */
302 {
303     /* Remember the current input char and token */
304     S->Tok      = CurTok.Tok;
305     S->C        = C;
306
307     /* Use the new input source */
308     S->Next     = Source;
309     Source      = S;
310
311     /* Read the first character from the new file */
312     S->Func->NextChar (S);
313
314     /* Setup the next token so it will be skipped on the next call to
315      * NextRawTok().
316      */
317     CurTok.Tok = TOK_SEP;
318 }
319
320
321
322 static void DoneCharSource (void)
323 /* Close the top level character source */
324 {
325     CharSource* S;
326
327     /* First, call the type specific function */
328     Source->Func->Done (Source);
329
330     /* Restore the old token */
331     CurTok.Tok = Source->Tok;
332     C   = Source->C;
333
334     /* Remember the last stacked input source */
335     S = Source->Next;
336
337     /* Delete the top level one ... */
338     xfree (Source);
339
340     /* ... and use the one before */
341     Source = S;
342 }
343
344
345
346 /*****************************************************************************/
347 /*                            InputFile functions                            */
348 /*****************************************************************************/
349
350
351
352 static void IFMarkStart (CharSource* S)
353 /* Mark the start of the next token */
354 {
355     CurTok.Pos = S->V.File.Pos;
356 }
357
358
359
360 static void IFNextChar (CharSource* S)
361 /* Read the next character from the input file */
362 {
363     /* Check for end of line, read the next line if needed */
364     while (S->V.File.Line [S->V.File.Pos.Col] == '\0') {
365
366         unsigned Len, Removed;
367
368         /* End of current line reached, read next line */
369         if (fgets (S->V.File.Line, sizeof (S->V.File.Line), S->V.File.F) == 0) {
370             /* End of file. Add an empty line to the listing. This is a
371              * small hack needed to keep the PC output in sync.
372              */
373             NewListingLine ("", S->V.File.Pos.Name, FCount);
374             C = EOF;
375             return;
376         }
377
378         /* For better handling of files with unusual line endings (DOS
379          * files that are accidently translated on Unix for example),
380          * first remove all whitespace at the end, then add a single
381          * newline.
382          */
383         Len = strlen (S->V.File.Line);
384         Removed = 0;
385         while (Len > 0 && IsSpace (S->V.File.Line[Len-1])) {
386             ++Removed;
387             --Len;
388         }
389         if (Removed) {
390             S->V.File.Line[Len+0] = '\n';
391             S->V.File.Line[Len+1] = '\0';
392         }
393
394         /* One more line */
395         S->V.File.Pos.Line++;
396         S->V.File.Pos.Col = 0;
397
398         /* Remember the new line for the listing */
399         NewListingLine (S->V.File.Line, S->V.File.Pos.Name, FCount);
400
401     }
402
403     /* Return the next character from the file */
404     C = S->V.File.Line [S->V.File.Pos.Col++];
405 }
406
407
408
409 void IFDone (CharSource* S)
410 /* Close the current input file */
411 {
412     /* We're at the end of an include file. Check if we have any
413      * open .IFs, or any open token lists in this file. This
414      * enforcement is artificial, using conditionals that start
415      * in one file and end in another are uncommon, and don't
416      * allowing these things will help finding errors.
417      */
418     CheckOpenIfs ();
419
420     /* If we've added search paths for this file, remove them */
421     if (S->V.File.IncSearchPath) {
422         PopSearchPath (IncSearchPath);
423     }
424     if (S->V.File.BinSearchPath) {
425         PopSearchPath (BinSearchPath);
426     }
427
428     /* Close the input file and decrement the file count. We will ignore
429      * errors here, since we were just reading from the file.
430      */
431     (void) fclose (S->V.File.F);
432     --FCount;
433 }
434
435
436
437 /* Set of input file handling functions */
438 static const CharSourceFunctions IFFunc = {
439     IFMarkStart,
440     IFNextChar,
441     IFDone
442 };
443
444
445
446 int NewInputFile (const char* Name)
447 /* Open a new input file. Returns true if the file could be successfully opened
448  * and false otherwise.
449  */
450 {
451     int         RetCode = 0;            /* Return code. Assume an error. */
452     char*       PathName = 0;
453     FILE*       F;
454     struct stat Buf;
455     StrBuf      NameBuf;                /* No need to initialize */
456     StrBuf      Path = AUTO_STRBUF_INITIALIZER;
457     unsigned    FileIdx;
458     CharSource* S;
459
460
461     /* If this is the main file, just try to open it. If it's an include file,
462      * search for it using the include path list.
463      */
464     if (FCount == 0) {
465         /* Main file */
466         F = fopen (Name, "r");
467         if (F == 0) {
468             Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
469         }
470     } else {
471         /* We are on include level. Search for the file in the include
472          * directories.
473          */
474         PathName = SearchFile (IncSearchPath, Name);
475         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
476             /* Not found or cannot open, print an error and bail out */
477             Error ("Cannot open include file `%s': %s", Name, strerror (errno));
478             goto ExitPoint;
479         }
480
481         /* Use the path name from now on */
482         Name = PathName;
483     }
484
485     /* Stat the file and remember the values. There a race condition here,
486      * since we cannot use fileno() (non standard identifier in standard
487      * header file), and therefore not fstat. When using stat with the
488      * file name, there's a risk that the file was deleted and recreated
489      * while it was open. Since mtime and size are only used to check
490      * if a file has changed in the debugger, we will ignore this problem
491      * here.
492      */
493     if (stat (Name, &Buf) != 0) {
494         Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
495     }
496
497     /* Add the file to the input file table and remember the index */
498     FileIdx = AddFile (SB_InitFromString (&NameBuf, Name),
499                        (FCount == 0)? FT_MAIN : FT_INCLUDE,
500                        Buf.st_size, Buf.st_mtime);
501
502     /* Create a new input source variable and initialize it */
503     S                   = xmalloc (sizeof (*S));
504     S->Func             = &IFFunc;
505     S->V.File.F         = F;
506     S->V.File.Pos.Line  = 0;
507     S->V.File.Pos.Col   = 0;
508     S->V.File.Pos.Name  = FileIdx;
509     S->V.File.Line[0]   = '\0';
510
511     /* Push the path for this file onto the include search lists */
512     SB_CopyBuf (&Path, Name, FindName (Name) - Name);
513     SB_Terminate (&Path);
514     S->V.File.IncSearchPath = PushSearchPath (IncSearchPath, SB_GetConstBuf (&Path));
515     S->V.File.BinSearchPath = PushSearchPath (BinSearchPath, SB_GetConstBuf (&Path));
516     SB_Done (&Path);
517
518     /* Count active input files */
519     ++FCount;
520
521     /* Use this input source */
522     UseCharSource (S);
523
524     /* File successfully opened */
525     RetCode = 1;
526
527 ExitPoint:
528     /* Free an allocated name buffer */
529     xfree (PathName);
530
531     /* Return the success code */
532     return RetCode;
533 }
534
535
536
537 /*****************************************************************************/
538 /*                            InputData functions                            */
539 /*****************************************************************************/
540
541
542
543 static void IDMarkStart (CharSource* S attribute ((unused)))
544 /* Mark the start of the next token */
545 {
546     /* Nothing to do here */
547 }
548
549
550
551 static void IDNextChar (CharSource* S)
552 /* Read the next character from the input text */
553 {
554     C = *S->V.Data.Pos++;
555     if (C == '\0') {
556         /* End of input data */
557         --S->V.Data.Pos;
558         C = EOF;
559     }
560 }
561
562
563
564 void IDDone (CharSource* S)
565 /* Close the current input data */
566 {
567     /* Cleanup the current stuff */
568     if (S->V.Data.Malloced) {
569         xfree (S->V.Data.Text);
570     }
571 }
572
573
574
575 /* Set of input data handling functions */
576 static const CharSourceFunctions IDFunc = {
577     IDMarkStart,
578     IDNextChar,
579     IDDone
580 };
581
582
583
584 void NewInputData (char* Text, int Malloced)
585 /* Add a chunk of input data to the input stream */
586 {
587     CharSource* S;
588
589     /* Create a new input source variable and initialize it */
590     S                   = xmalloc (sizeof (*S));
591     S->Func             = &IDFunc;
592     S->V.Data.Text      = Text;
593     S->V.Data.Pos       = Text;
594     S->V.Data.Malloced  = Malloced;
595
596     /* Use this input source */
597     UseCharSource (S);
598 }
599
600
601
602 /*****************************************************************************/
603 /*                    Character classification functions                     */
604 /*****************************************************************************/
605
606
607
608 int IsIdChar (int C)
609 /* Return true if the character is a valid character for an identifier */
610 {
611     return IsAlNum (C)                  ||
612            (C == '_')                   ||
613            (C == '@' && AtInIdents)     ||
614            (C == '$' && DollarInIdents);
615 }
616
617
618
619 int IsIdStart (int C)
620 /* Return true if the character may start an identifier */
621 {
622     return IsAlpha (C) || C == '_';
623 }
624
625
626
627 /*****************************************************************************/
628 /*                                   Code                                    */
629 /*****************************************************************************/
630
631
632
633 static unsigned DigitVal (unsigned char C)
634 /* Convert a digit into it's numerical representation */
635 {
636     if (IsDigit (C)) {
637         return C - '0';
638     } else {
639         return toupper (C) - 'A' + 10;
640     }
641 }
642
643
644
645 static void NextChar (void)
646 /* Read the next character from the input file */
647 {
648     Source->Func->NextChar (Source);
649 }
650
651
652
653 void LocaseSVal (void)
654 /* Make SVal lower case */
655 {
656     SB_ToLower (&CurTok.SVal);
657 }
658
659
660
661 void UpcaseSVal (void)
662 /* Make SVal upper case */
663 {
664     SB_ToUpper (&CurTok.SVal);
665 }
666
667
668
669 static int CmpDotKeyword (const void* K1, const void* K2)
670 /* Compare function for the dot keyword search */
671 {
672     return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
673 }
674
675
676
677 static token_t FindDotKeyword (void)
678 /* Find the dot keyword in SVal. Return the corresponding token if found,
679  * return TOK_NONE if not found.
680  */
681 {
682     struct DotKeyword K;
683     struct DotKeyword* R;
684
685     /* Initialize K */
686     K.Key = SB_GetConstBuf (&CurTok.SVal);
687     K.Tok = 0;
688
689     /* If we aren't in ignore case mode, we have to uppercase the keyword */
690     if (!IgnoreCase) {
691         UpcaseSVal ();
692     }
693
694     /* Search for the keyword */
695     R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
696                  sizeof (DotKeywords [0]), CmpDotKeyword);
697     if (R != 0) {
698         return R->Tok;
699     } else {
700         return TOK_NONE;
701     }
702 }
703
704
705
706 static void ReadIdent (void)
707 /* Read an identifier from the current input position into Ident. Filling SVal
708  * starts at the current position with the next character in C. It is assumed
709  * that any characters already filled in are ok, and the character in C is
710  * checked.
711  */
712 {
713     /* Read the identifier */
714     do {
715         SB_AppendChar (&CurTok.SVal, C);
716         NextChar ();
717     } while (IsIdChar (C));
718     SB_Terminate (&CurTok.SVal);
719
720     /* If we should ignore case, convert the identifier to upper case */
721     if (IgnoreCase) {
722         UpcaseSVal ();
723     }
724 }
725
726
727
728 static void ReadStringConst (int StringTerm)
729 /* Read a string constant into SVal. */
730 {
731     /* Skip the leading string terminator */
732     NextChar ();
733
734     /* Read the string */
735     while (1) {
736         if (C == StringTerm) {
737             break;
738         }
739         if (C == '\n' || C == EOF) {
740             Error ("Newline in string constant");
741             break;
742         }
743
744         /* Append the char to the string */
745         SB_AppendChar (&CurTok.SVal, C);
746
747         /* Skip the character */
748         NextChar ();
749     }
750
751     /* Skip the trailing terminator */
752     NextChar ();
753
754     /* Terminate the string */
755     SB_Terminate (&CurTok.SVal);
756 }
757
758
759
760 static int Sweet16Reg (const StrBuf* Id)
761 /* Check if the given identifier is a sweet16 register. Return -1 if this is
762  * not the case, return the register number otherwise.
763  */
764 {
765     unsigned RegNum;
766     char Check;
767
768     if (SB_GetLen (Id) < 2) {
769         return -1;
770     }
771     if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
772         return -1;
773     }
774     if (!IsDigit (SB_AtUnchecked (Id, 1))) {
775         return -1;
776     }
777
778     if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
779         /* Invalid register */
780         return -1;
781     }
782
783     /* The register number is valid */
784     return (int) RegNum;
785 }
786
787
788
789 void NextRawTok (void)
790 /* Read the next raw token from the input stream */
791 {
792     /* If we've a forced end of assembly, don't read further */
793     if (ForcedEnd) {
794         CurTok.Tok = TOK_EOF;
795         return;
796     }
797
798 Restart:
799     /* Check if we have tokens from another input source */
800     if (InputFromStack ()) {
801         if (CurTok.Tok == TOK_IDENT && IsDefine (&CurTok.SVal)) {
802             /* This is a define style macro - expand it */
803             MacExpandStart ();
804             goto Restart;
805         }
806         return;
807     }
808
809 Again:
810     /* Skip whitespace, remember if we had some */
811     if ((CurTok.WS = IsBlank (C)) != 0) {
812         do {
813             NextChar ();
814         } while (IsBlank (C));
815     }
816
817     /* Mark the file position of the next token */
818     Source->Func->MarkStart (Source);
819
820     /* Clear the string attribute */
821     SB_Clear (&CurTok.SVal);
822
823     /* Generate line info for the current token */
824     GenLineInfo (LI_SLOT_ASM, &CurTok.Pos);
825
826     /* Hex number or PC symbol? */
827     if (C == '$') {
828         NextChar ();
829
830         /* Hex digit must follow or DollarIsPC must be enabled */
831         if (!IsXDigit (C)) {
832             if (DollarIsPC) {
833                 CurTok.Tok = TOK_PC;
834                 return;
835             } else {
836                 Error ("Hexadecimal digit expected");
837             }
838         }
839
840         /* Read the number */
841         CurTok.IVal = 0;
842         while (IsXDigit (C)) {
843             if (CurTok.IVal & 0xF0000000) {
844                 Error ("Overflow in hexadecimal number");
845                 CurTok.IVal = 0;
846             }
847             CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C);
848             NextChar ();
849         }
850
851         /* This is an integer constant */
852         CurTok.Tok = TOK_INTCON;
853         return;
854     }
855
856     /* Binary number? */
857     if (C == '%') {
858         NextChar ();
859
860         /* 0 or 1 must follow */
861         if (!IsBDigit (C)) {
862             Error ("Binary digit expected");
863         }
864
865         /* Read the number */
866         CurTok.IVal = 0;
867         while (IsBDigit (C)) {
868             if (CurTok.IVal & 0x80000000) {
869                 Error ("Overflow in binary number");
870                 CurTok.IVal = 0;
871             }
872             CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C);
873             NextChar ();
874         }
875
876         /* This is an integer constant */
877         CurTok.Tok = TOK_INTCON;
878         return;
879     }
880
881     /* Number? */
882     if (IsDigit (C)) {
883
884         char Buf[16];
885         unsigned Digits;
886         unsigned Base;
887         unsigned I;
888         long     Max;
889         unsigned DVal;
890
891         /* Ignore leading zeros */
892         while (C == '0') {
893             NextChar ();
894         }
895
896         /* Read the number into Buf counting the digits */
897         Digits = 0;
898         while (IsXDigit (C)) {
899
900             /* Buf is big enough to allow any decimal and hex number to
901              * overflow, so ignore excess digits here, they will be detected
902              * when we convert the value.
903              */
904             if (Digits < sizeof (Buf)) {
905                 Buf[Digits++] = C;
906             }
907
908             NextChar ();
909         }
910
911         /* Allow zilog/intel style hex numbers with a 'h' suffix */
912         if (C == 'h' || C == 'H') {
913             NextChar ();
914             Base = 16;
915             Max  = 0xFFFFFFFFUL / 16;
916         } else {
917             Base = 10;
918             Max  = 0xFFFFFFFFUL / 10;
919         }
920
921         /* Convert the number using the given base */
922         CurTok.IVal = 0;
923         for (I = 0; I < Digits; ++I) {
924             if (CurTok.IVal > Max) {
925                 Error ("Number out of range");
926                 CurTok.IVal = 0;
927                 break;
928             }
929             DVal = DigitVal (Buf[I]);
930             if (DVal > Base) {
931                 Error ("Invalid digits in number");
932                 CurTok.IVal = 0;
933                 break;
934             }
935             CurTok.IVal = (CurTok.IVal * Base) + DVal;
936         }
937
938         /* This is an integer constant */
939         CurTok.Tok = TOK_INTCON;
940         return;
941     }
942
943     /* Control command? */
944     if (C == '.') {
945
946         /* Remember and skip the dot */
947         NextChar ();
948
949         /* Check if it's just a dot */
950         if (!IsIdStart (C)) {
951
952             /* Just a dot */
953             CurTok.Tok = TOK_DOT;
954
955         } else {
956
957             /* Read the remainder of the identifier */
958             SB_AppendChar (&CurTok.SVal, '.');
959             ReadIdent ();
960
961             /* Dot keyword, search for it */
962             CurTok.Tok = FindDotKeyword ();
963             if (CurTok.Tok == TOK_NONE) {
964
965                 /* Not found */
966                 if (!LeadingDotInIdents) {
967                     /* Invalid pseudo instruction */
968                     Error ("`%m%p' is not a recognized control command", &CurTok.SVal);
969                     goto Again;
970                 }
971
972                 /* An identifier with a dot. Check if it's a define style
973                  * macro.
974                  */
975                 if (IsDefine (&CurTok.SVal)) {
976                     /* This is a define style macro - expand it */
977                     MacExpandStart ();
978                     goto Restart;
979                 }
980
981                 /* Just an identifier with a dot */
982                 CurTok.Tok = TOK_IDENT;
983             }
984
985         }
986         return;
987     }
988
989     /* Indirect op for sweet16 cpu. Must check this before checking for local
990      * symbols, because these may also use the '@' symbol.
991      */
992     if (CPU == CPU_SWEET16 && C == '@') {
993         NextChar ();
994         CurTok.Tok = TOK_AT;
995         return;
996     }
997
998     /* Local symbol? */
999     if (C == LocalStart) {
1000
1001         /* Read the identifier. */
1002         ReadIdent ();
1003
1004         /* Start character alone is not enough */
1005         if (SB_GetLen (&CurTok.SVal) == 1) {
1006             Error ("Invalid cheap local symbol");
1007             goto Again;
1008         }
1009
1010         /* A local identifier */
1011         CurTok.Tok = TOK_LOCAL_IDENT;
1012         return;
1013     }
1014
1015
1016     /* Identifier or keyword? */
1017     if (IsIdStart (C)) {
1018
1019         /* Read the identifier */
1020         ReadIdent ();
1021
1022         /* Check for special names. Bail out if we have identified the type of
1023          * the token. Go on if the token is an identifier.
1024          */
1025         if (SB_GetLen (&CurTok.SVal) == 1) {
1026             switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) {
1027
1028                 case 'A':
1029                     if (C == ':') {
1030                         NextChar ();
1031                         CurTok.Tok = TOK_OVERRIDE_ABS;
1032                     } else {
1033                         CurTok.Tok = TOK_A;
1034                     }
1035                     return;
1036
1037                 case 'F':
1038                     if (C == ':') {
1039                         NextChar ();
1040                         CurTok.Tok = TOK_OVERRIDE_FAR;
1041                         return;
1042                     }
1043                     break;
1044
1045                 case 'S':
1046                     if (CPU == CPU_65816) {
1047                         CurTok.Tok = TOK_S;
1048                         return;
1049                     }
1050                     break;
1051
1052                 case 'X':
1053                     CurTok.Tok = TOK_X;
1054                     return;
1055
1056                 case 'Y':
1057                     CurTok.Tok = TOK_Y;
1058                     return;
1059
1060                 case 'Z':
1061                     if (C == ':') {
1062                         NextChar ();
1063                         CurTok.Tok = TOK_OVERRIDE_ZP;
1064                         return;
1065                     }
1066                     break;
1067
1068                 default:
1069                     break;
1070             }
1071
1072         } else if (CPU == CPU_SWEET16 &&
1073                   (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) {
1074
1075             /* A sweet16 register number in sweet16 mode */
1076             CurTok.Tok = TOK_REG;
1077             return;
1078
1079         }
1080
1081         /* Check for define style macro */
1082         if (IsDefine (&CurTok.SVal)) {
1083             /* Macro - expand it */
1084             MacExpandStart ();
1085             goto Restart;
1086         } else {
1087             /* An identifier */
1088             CurTok.Tok = TOK_IDENT;
1089         }
1090         return;
1091     }
1092
1093     /* Ok, let's do the switch */
1094 CharAgain:
1095     switch (C) {
1096
1097         case '+':
1098             NextChar ();
1099             CurTok.Tok = TOK_PLUS;
1100             return;
1101
1102         case '-':
1103             NextChar ();
1104             CurTok.Tok = TOK_MINUS;
1105             return;
1106
1107         case '/':
1108             NextChar ();
1109             if (C != '*') {
1110                 CurTok.Tok = TOK_DIV;
1111             } else if (CComments) {
1112                 /* Remember the position, then skip the '*' */
1113                 Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
1114                 GetFullLineInfo (&LineInfos, 0);
1115                 NextChar ();
1116                 do {
1117                     while (C !=  '*') {
1118                         if (C == EOF) {
1119                             LIError (&LineInfos, "Unterminated comment");
1120                             DoneCollection (&LineInfos);
1121                             goto CharAgain;
1122                         }
1123                         NextChar ();
1124                     }
1125                     NextChar ();
1126                 } while (C != '/');
1127                 NextChar ();
1128                 DoneCollection (&LineInfos);
1129                 goto Again;
1130             }
1131             return;
1132
1133         case '*':
1134             NextChar ();
1135             CurTok.Tok = TOK_MUL;
1136             return;
1137
1138         case '^':
1139             NextChar ();
1140             CurTok.Tok = TOK_XOR;
1141             return;
1142
1143         case '&':
1144             NextChar ();
1145             if (C == '&') {
1146                 NextChar ();
1147                 CurTok.Tok = TOK_BOOLAND;
1148             } else {
1149                 CurTok.Tok = TOK_AND;
1150             }
1151             return;
1152
1153         case '|':
1154             NextChar ();
1155             if (C == '|') {
1156                 NextChar ();
1157                 CurTok.Tok = TOK_BOOLOR;
1158             } else {
1159                 CurTok.Tok = TOK_OR;
1160             }
1161             return;
1162
1163         case ':':
1164             NextChar ();
1165             switch (C) {
1166
1167                 case ':':
1168                     NextChar ();
1169                     CurTok.Tok = TOK_NAMESPACE;
1170                     break;
1171
1172                 case '-':
1173                     CurTok.IVal = 0;
1174                     do {
1175                         --CurTok.IVal;
1176                         NextChar ();
1177                     } while (C == '-');
1178                     CurTok.Tok = TOK_ULABEL;
1179                     break;
1180
1181                 case '+':
1182                     CurTok.IVal = 0;
1183                     do {
1184                         ++CurTok.IVal;
1185                         NextChar ();
1186                     } while (C == '+');
1187                     CurTok.Tok = TOK_ULABEL;
1188                     break;
1189
1190                 case '=':
1191                     NextChar ();
1192                     CurTok.Tok = TOK_ASSIGN;
1193                     break;
1194
1195                 default:
1196                     CurTok.Tok = TOK_COLON;
1197                     break;
1198             }
1199             return;
1200
1201         case ',':
1202             NextChar ();
1203             CurTok.Tok = TOK_COMMA;
1204             return;
1205
1206         case ';':
1207             NextChar ();
1208             while (C != '\n' && C != EOF) {
1209                 NextChar ();
1210             }
1211             goto CharAgain;
1212
1213         case '#':
1214             NextChar ();
1215             CurTok.Tok = TOK_HASH;
1216             return;
1217
1218         case '(':
1219             NextChar ();
1220             CurTok.Tok = TOK_LPAREN;
1221             return;
1222
1223         case ')':
1224             NextChar ();
1225             CurTok.Tok = TOK_RPAREN;
1226             return;
1227
1228         case '[':
1229             NextChar ();
1230             CurTok.Tok = TOK_LBRACK;
1231             return;
1232
1233         case ']':
1234             NextChar ();
1235             CurTok.Tok = TOK_RBRACK;
1236             return;
1237
1238         case '{':
1239             NextChar ();
1240             CurTok.Tok = TOK_LCURLY;
1241             return;
1242
1243         case '}':
1244             NextChar ();
1245             CurTok.Tok = TOK_RCURLY;
1246             return;
1247
1248         case '<':
1249             NextChar ();
1250             if (C == '=') {
1251                 NextChar ();
1252                 CurTok.Tok = TOK_LE;
1253             } else if (C == '<') {
1254                 NextChar ();
1255                 CurTok.Tok = TOK_SHL;
1256             } else if (C == '>') {
1257                 NextChar ();
1258                 CurTok.Tok = TOK_NE;
1259             } else {
1260                 CurTok.Tok = TOK_LT;
1261             }
1262             return;
1263
1264         case '=':
1265             NextChar ();
1266             CurTok.Tok = TOK_EQ;
1267             return;
1268
1269         case '!':
1270             NextChar ();
1271             CurTok.Tok = TOK_BOOLNOT;
1272             return;
1273
1274         case '>':
1275             NextChar ();
1276             if (C == '=') {
1277                 NextChar ();
1278                 CurTok.Tok = TOK_GE;
1279             } else if (C == '>') {
1280                 NextChar ();
1281                 CurTok.Tok = TOK_SHR;
1282             } else {
1283                 CurTok.Tok = TOK_GT;
1284             }
1285             return;
1286
1287         case '~':
1288             NextChar ();
1289             CurTok.Tok = TOK_NOT;
1290             return;
1291
1292         case '\'':
1293             /* Hack: If we allow ' as terminating character for strings, read
1294              * the following stuff as a string, and check for a one character
1295              * string later.
1296              */
1297             if (LooseStringTerm) {
1298                 ReadStringConst ('\'');
1299                 if (SB_GetLen (&CurTok.SVal) == 1) {
1300                     CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0);
1301                     CurTok.Tok = TOK_CHARCON;
1302                 } else {
1303                     CurTok.Tok = TOK_STRCON;
1304                 }
1305             } else {
1306                 /* Always a character constant */
1307                 NextChar ();
1308                 if (C == EOF || IsControl (C)) {
1309                     Error ("Illegal character constant");
1310                     goto CharAgain;
1311                 }
1312                 CurTok.IVal = C;
1313                 CurTok.Tok = TOK_CHARCON;
1314                 NextChar ();
1315                 if (C != '\'') {
1316                     if (!MissingCharTerm) {
1317                         Error ("Illegal character constant");
1318                     }
1319                 } else {
1320                     NextChar ();
1321                 }
1322             }
1323             return;
1324
1325         case '\"':
1326             ReadStringConst ('\"');
1327             CurTok.Tok = TOK_STRCON;
1328             return;
1329
1330         case '\\':
1331             /* Line continuation? */
1332             if (LineCont) {
1333                 NextChar ();
1334                 if (C == '\n') {
1335                     /* Handle as white space */
1336                     NextChar ();
1337                     C = ' ';
1338                     goto Again;
1339                 }
1340             }
1341             break;
1342
1343         case '\n':
1344             NextChar ();
1345             CurTok.Tok = TOK_SEP;
1346             return;
1347
1348         case EOF:
1349             CheckInputStack ();
1350             /* In case of the main file, do not close it, but return EOF. */
1351             if (Source && Source->Next) {
1352                 DoneCharSource ();
1353                 goto Again;
1354             } else {
1355                 CurTok.Tok = TOK_EOF;
1356             }
1357             return;
1358     }
1359
1360     /* If we go here, we could not identify the current character. Skip it
1361      * and try again.
1362      */
1363     Error ("Invalid input character: 0x%02X", C & 0xFF);
1364     NextChar ();
1365     goto Again;
1366 }
1367
1368
1369
1370 int GetSubKey (const char** Keys, unsigned Count)
1371 /* Search for a subkey in a table of keywords. The current token must be an
1372  * identifier and all keys must be in upper case. The identifier will be
1373  * uppercased in the process. The function returns the index of the keyword,
1374  * or -1 if the keyword was not found.
1375  */
1376 {
1377     unsigned I;
1378
1379     /* Must have an identifier */
1380     PRECONDITION (CurTok.Tok == TOK_IDENT);
1381
1382     /* If we aren't in ignore case mode, we have to uppercase the identifier */
1383     if (!IgnoreCase) {
1384         UpcaseSVal ();
1385     }
1386
1387     /* Do a linear search (a binary search is not worth the effort) */
1388     for (I = 0; I < Count; ++I) {
1389         if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) {
1390             /* Found it */
1391             return I;
1392         }
1393     }
1394
1395     /* Not found */
1396     return -1;
1397 }
1398
1399
1400
1401 unsigned char ParseAddrSize (void)
1402 /* Check if the next token is a keyword that denotes an address size specifier.
1403  * If so, return the corresponding address size constant, otherwise output an
1404  * error message and return ADDR_SIZE_DEFAULT.
1405  */
1406 {
1407     unsigned char AddrSize;
1408
1409     /* Check for an identifier */
1410     if (CurTok.Tok != TOK_IDENT) {
1411         Error ("Address size specifier expected");
1412         return ADDR_SIZE_DEFAULT;
1413     }
1414
1415     /* Convert the attribute */
1416     AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal));
1417     if (AddrSize == ADDR_SIZE_INVALID) {
1418         Error ("Address size specifier expected");
1419         AddrSize = ADDR_SIZE_DEFAULT;
1420     }
1421
1422     /* Done */
1423     return AddrSize;
1424 }
1425
1426
1427
1428 void InitScanner (const char* InFile)
1429 /* Initialize the scanner, open the given input file */
1430 {
1431     /* Open the input file */
1432     NewInputFile (InFile);
1433 }
1434
1435
1436
1437 void DoneScanner (void)
1438 /* Release scanner resources */
1439 {
1440     DoneCharSource ();
1441 }
1442
1443
1444
1445