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