1 /*****************************************************************************/
5 /* The scanner for the ca65 macroassembler */
9 /* (C) 1998-2013, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
66 /*****************************************************************************/
68 /*****************************************************************************/
72 /* Current input token incl. attributes */
73 Token CurTok = STATIC_TOKEN_INITIALIZER;
75 /* Struct to handle include files. */
76 typedef struct InputFile InputFile;
78 FILE* F; /* Input file descriptor */
79 FilePos Pos; /* Position in file */
80 token_t Tok; /* Last token */
81 int C; /* Last character */
82 StrBuf Line; /* The current input line */
83 int IncSearchPath; /* True if we've added a search path */
84 int BinSearchPath; /* True if we've added a search path */
85 InputFile* Next; /* Linked list of input files */
88 /* Struct to handle textual input data */
89 typedef struct InputData InputData;
91 char* Text; /* Pointer to the text data */
92 const char* Pos; /* Pointer to current position */
93 int Malloced; /* Memory was malloced */
94 token_t Tok; /* Last token */
95 int C; /* Last character */
96 InputData* Next; /* Linked list of input data */
99 /* Input source: Either file or data */
100 typedef struct CharSource CharSource;
102 /* Set of input functions */
103 typedef struct CharSourceFunctions CharSourceFunctions;
104 struct CharSourceFunctions {
105 void (*MarkStart) (CharSource*); /* Mark the start pos of a token */
106 void (*NextChar) (CharSource*); /* Read next char from input */
107 void (*Done) (CharSource*); /* Close input source */
110 /* Input source: Either file or data */
112 CharSource* Next; /* Linked list of char sources */
113 token_t Tok; /* Last token */
114 int C; /* Last character */
115 const CharSourceFunctions* Func; /* Pointer to function table */
117 InputFile File; /* File data */
118 InputData Data; /* Textual data */
122 /* Current input variables */
123 static CharSource* Source = 0; /* Current char source */
124 static unsigned FCount = 0; /* Count of input files */
125 static int C = 0; /* Current input character */
127 /* Force end of assembly */
130 /* List of dot keywords with the corresponding tokens */
132 const char* Key; /* MUST be first field */
137 { ".ADDR", TOK_ADDR },
138 { ".ADDRSIZE", TOK_ADDRSIZE },
139 { ".ALIGN", TOK_ALIGN },
140 { ".AND", TOK_BOOLAND },
141 { ".ASCIIZ", TOK_ASCIIZ },
142 { ".ASIZE", TOK_ASIZE },
143 { ".ASSERT", TOK_ASSERT },
144 { ".AUTOIMPORT", TOK_AUTOIMPORT },
145 { ".BANK", TOK_BANK },
146 { ".BANKBYTE", TOK_BANKBYTE },
147 { ".BANKBYTES", TOK_BANKBYTES },
148 { ".BITAND", TOK_AND },
149 { ".BITNOT", TOK_NOT },
150 { ".BITOR", TOK_OR },
151 { ".BITXOR", TOK_XOR },
152 { ".BLANK", TOK_BLANK },
154 { ".BYT", TOK_BYTE },
155 { ".BYTE", TOK_BYTE },
156 { ".CASE", TOK_CASE },
157 { ".CHARMAP", TOK_CHARMAP },
158 { ".CODE", TOK_CODE },
159 { ".CONCAT", TOK_CONCAT },
160 { ".CONDES", TOK_CONDES },
161 { ".CONST", TOK_CONST },
162 { ".CONSTRUCTOR", TOK_CONSTRUCTOR },
164 { ".DATA", TOK_DATA },
166 { ".DBYT", TOK_DBYT },
167 { ".DEBUGINFO", TOK_DEBUGINFO },
168 { ".DEF", TOK_DEFINED },
169 { ".DEFINE", TOK_DEFINE },
170 { ".DEFINED", TOK_DEFINED },
171 { ".DEFINEDMACRO", TOK_DEFINEDMACRO },
172 { ".DELMAC", TOK_DELMAC },
173 { ".DELMACRO", TOK_DELMAC },
174 { ".DESTRUCTOR", TOK_DESTRUCTOR },
175 { ".DWORD", TOK_DWORD },
176 { ".ELSE", TOK_ELSE },
177 { ".ELSEIF", TOK_ELSEIF },
179 { ".ENDENUM", TOK_ENDENUM },
180 { ".ENDIF", TOK_ENDIF },
181 { ".ENDMAC", TOK_ENDMACRO },
182 { ".ENDMACRO", TOK_ENDMACRO },
183 { ".ENDPROC", TOK_ENDPROC },
184 { ".ENDREP", TOK_ENDREP },
185 { ".ENDREPEAT", TOK_ENDREP },
186 { ".ENDSCOPE", TOK_ENDSCOPE },
187 { ".ENDSTRUCT", TOK_ENDSTRUCT },
188 { ".ENDUNION", TOK_ENDUNION },
189 { ".ENUM", TOK_ENUM },
190 { ".ERROR", TOK_ERROR },
191 { ".EXITMAC", TOK_EXITMACRO },
192 { ".EXITMACRO", TOK_EXITMACRO },
193 { ".EXPORT", TOK_EXPORT },
194 { ".EXPORTZP", TOK_EXPORTZP },
195 { ".FARADDR", TOK_FARADDR },
196 { ".FATAL", TOK_FATAL },
197 { ".FEATURE", TOK_FEATURE },
198 { ".FILEOPT", TOK_FILEOPT },
199 { ".FOPT", TOK_FILEOPT },
200 { ".FORCEIMPORT", TOK_FORCEIMPORT },
201 { ".FORCEWORD", TOK_FORCEWORD },
202 { ".GLOBAL", TOK_GLOBAL },
203 { ".GLOBALZP", TOK_GLOBALZP },
204 { ".HIBYTE", TOK_HIBYTE },
205 { ".HIBYTES", TOK_HIBYTES },
206 { ".HIWORD", TOK_HIWORD },
209 { ".IDENT", TOK_MAKEIDENT },
211 { ".IFBLANK", TOK_IFBLANK },
212 { ".IFCONST", TOK_IFCONST },
213 { ".IFDEF", TOK_IFDEF },
214 { ".IFNBLANK", TOK_IFNBLANK },
215 { ".IFNCONST", TOK_IFNCONST },
216 { ".IFNDEF", TOK_IFNDEF },
217 { ".IFNREF", TOK_IFNREF },
218 { ".IFP02", TOK_IFP02 },
219 { ".IFP4510", TOK_IFP4510 },
220 { ".IFP816", TOK_IFP816 },
221 { ".IFPC02", TOK_IFPC02 },
222 { ".IFPSC02", TOK_IFPSC02 },
223 { ".IFREF", TOK_IFREF },
224 { ".IMPORT", TOK_IMPORT },
225 { ".IMPORTZP", TOK_IMPORTZP },
226 { ".INCBIN", TOK_INCBIN },
227 { ".INCLUDE", TOK_INCLUDE },
228 { ".INTERRUPTOR", TOK_INTERRUPTOR },
229 { ".ISIZE", TOK_ISIZE },
230 { ".ISMNEM", TOK_ISMNEMONIC },
231 { ".ISMNEMONIC", TOK_ISMNEMONIC },
232 { ".LEFT", TOK_LEFT },
233 { ".LINECONT", TOK_LINECONT },
234 { ".LIST", TOK_LIST },
235 { ".LISTBYTES", TOK_LISTBYTES },
236 { ".LOBYTE", TOK_LOBYTE },
237 { ".LOBYTES", TOK_LOBYTES },
238 { ".LOCAL", TOK_LOCAL },
239 { ".LOCALCHAR", TOK_LOCALCHAR },
240 { ".LOWORD", TOK_LOWORD },
241 { ".MAC", TOK_MACRO },
242 { ".MACPACK", TOK_MACPACK },
243 { ".MACRO", TOK_MACRO },
244 { ".MATCH", TOK_MATCH },
249 { ".NOT", TOK_BOOLNOT },
250 { ".NULL", TOK_NULL },
251 { ".OR", TOK_BOOLOR },
255 { ".P4510", TOK_P4510 },
256 { ".P816", TOK_P816 },
257 { ".PAGELEN", TOK_PAGELENGTH },
258 { ".PAGELENGTH", TOK_PAGELENGTH },
259 { ".PARAMCOUNT", TOK_PARAMCOUNT },
260 { ".PC02", TOK_PC02 },
261 { ".POPCPU", TOK_POPCPU },
262 { ".POPSEG", TOK_POPSEG },
263 { ".PROC", TOK_PROC },
264 { ".PSC02", TOK_PSC02 },
265 { ".PUSHCPU", TOK_PUSHCPU },
266 { ".PUSHSEG", TOK_PUSHSEG },
267 { ".REF", TOK_REFERENCED },
268 { ".REFERENCED", TOK_REFERENCED },
269 { ".RELOC", TOK_RELOC },
270 { ".REPEAT", TOK_REPEAT },
272 { ".RIGHT", TOK_RIGHT },
273 { ".RODATA", TOK_RODATA },
274 { ".SCOPE", TOK_SCOPE },
275 { ".SEGMENT", TOK_SEGMENT },
277 { ".SETCPU", TOK_SETCPU },
280 { ".SIZEOF", TOK_SIZEOF },
281 { ".SMART", TOK_SMART },
282 { ".SPRINTF", TOK_SPRINTF },
283 { ".STRAT", TOK_STRAT },
284 { ".STRING", TOK_STRING },
285 { ".STRLEN", TOK_STRLEN },
286 { ".STRUCT", TOK_STRUCT },
288 { ".TCOUNT", TOK_TCOUNT },
289 { ".TIME", TOK_TIME },
290 { ".UNDEF", TOK_UNDEF },
291 { ".UNDEFINE", TOK_UNDEF },
292 { ".UNION", TOK_UNION },
293 { ".VERSION", TOK_VERSION },
294 { ".WARNING", TOK_WARNING },
295 { ".WORD", TOK_WORD },
296 { ".XMATCH", TOK_XMATCH },
297 { ".XOR", TOK_BOOLXOR },
298 { ".ZEROPAGE", TOK_ZEROPAGE },
303 /*****************************************************************************/
304 /* CharSource functions */
305 /*****************************************************************************/
309 static void UseCharSource (CharSource* S)
310 /* Initialize a new input source and start to use it. */
312 /* Remember the current input char and token */
316 /* Use the new input source */
320 /* Read the first character from the new file */
321 S->Func->NextChar (S);
323 /* Setup the next token so it will be skipped on the next call to
326 CurTok.Tok = TOK_SEP;
331 static void DoneCharSource (void)
332 /* Close the top level character source */
336 /* First, call the type specific function */
337 Source->Func->Done (Source);
339 /* Restore the old token */
340 CurTok.Tok = Source->Tok;
343 /* Remember the last stacked input source */
346 /* Delete the top level one ... */
349 /* ... and use the one before */
355 /*****************************************************************************/
356 /* InputFile functions */
357 /*****************************************************************************/
361 static void IFMarkStart (CharSource* S)
362 /* Mark the start of the next token */
364 CurTok.Pos = S->V.File.Pos;
369 static void IFNextChar (CharSource* S)
370 /* Read the next character from the input file */
372 /* Check for end of line, read the next line if needed */
373 while (SB_GetIndex (&S->V.File.Line) >= SB_GetLen (&S->V.File.Line)) {
377 /* End of current line reached, read next line */
378 SB_Clear (&S->V.File.Line);
381 int N = fgetc (S->V.File.F);
383 /* End of file. Accept files without a newline at the end */
384 if (SB_NotEmpty (&S->V.File.Line)) {
388 /* No more data - add an empty line to the listing. This
389 ** is a small hack needed to keep the PC output in sync.
391 NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount);
395 /* Check for end of line */
396 } else if (N == '\n') {
401 /* Collect other stuff */
404 /* Append data to line */
405 SB_AppendChar (&S->V.File.Line, N);
411 /* If we come here, we have a new input line. To avoid problems
412 ** with strange line terminators, remove all whitespace from the
413 ** end of the line, then add a single newline.
415 Len = SB_GetLen (&S->V.File.Line);
416 while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) {
419 SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len);
420 SB_AppendChar (&S->V.File.Line, '\n');
422 /* Terminate the string buffer */
423 SB_Terminate (&S->V.File.Line);
426 S->V.File.Pos.Line++;
428 /* Remember the new line for the listing */
429 NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount);
433 /* Set the column pointer */
434 S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line);
436 /* Return the next character from the buffer */
437 C = SB_Get (&S->V.File.Line);
442 void IFDone (CharSource* S)
443 /* Close the current input file */
445 /* We're at the end of an include file. Check if we have any
446 ** open .IFs, or any open token lists in this file. This
447 ** enforcement is artificial, using conditionals that start
448 ** in one file and end in another are uncommon, and don't
449 ** allowing these things will help finding errors.
453 /* If we've added search paths for this file, remove them */
454 if (S->V.File.IncSearchPath) {
455 PopSearchPath (IncSearchPath);
457 if (S->V.File.BinSearchPath) {
458 PopSearchPath (BinSearchPath);
461 /* Free the line buffer */
462 SB_Done (&S->V.File.Line);
464 /* Close the input file and decrement the file count. We will ignore
465 ** errors here, since we were just reading from the file.
467 (void) fclose (S->V.File.F);
473 /* Set of input file handling functions */
474 static const CharSourceFunctions IFFunc = {
482 int NewInputFile (const char* Name)
483 /* Open a new input file. Returns true if the file could be successfully opened
484 ** and false otherwise.
487 int RetCode = 0; /* Return code. Assume an error. */
491 StrBuf NameBuf; /* No need to initialize */
492 StrBuf Path = AUTO_STRBUF_INITIALIZER;
497 /* If this is the main file, just try to open it. If it's an include file,
498 ** search for it using the include path list.
502 F = fopen (Name, "r");
504 Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
507 /* We are on include level. Search for the file in the include
510 PathName = SearchFile (IncSearchPath, Name);
511 if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
512 /* Not found or cannot open, print an error and bail out */
513 Error ("Cannot open include file `%s': %s", Name, strerror (errno));
517 /* Use the path name from now on */
521 /* Stat the file and remember the values. There's a race condition here,
522 ** since we cannot use fileno() (non-standard identifier in standard
523 ** header file), and therefore not fstat. When using stat with the
524 ** file name, there's a risk that the file was deleted and recreated
525 ** while it was open. Since mtime and size are only used to check
526 ** if a file has changed in the debugger, we will ignore this problem
529 if (FileStat (Name, &Buf) != 0) {
530 Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
533 /* Add the file to the input file table and remember the index */
534 FileIdx = AddFile (SB_InitFromString (&NameBuf, Name),
535 (FCount == 0)? FT_MAIN : FT_INCLUDE,
536 Buf.st_size, (unsigned long) Buf.st_mtime);
538 /* Create a new input source variable and initialize it */
539 S = xmalloc (sizeof (*S));
542 S->V.File.Pos.Line = 0;
543 S->V.File.Pos.Col = 0;
544 S->V.File.Pos.Name = FileIdx;
545 SB_Init (&S->V.File.Line);
547 /* Push the path for this file onto the include search lists */
548 SB_CopyBuf (&Path, Name, FindName (Name) - Name);
549 SB_Terminate (&Path);
550 S->V.File.IncSearchPath = PushSearchPath (IncSearchPath, SB_GetConstBuf (&Path));
551 S->V.File.BinSearchPath = PushSearchPath (BinSearchPath, SB_GetConstBuf (&Path));
554 /* Count active input files */
557 /* Use this input source */
560 /* File successfully opened */
564 /* Free an allocated name buffer */
567 /* Return the success code */
573 /*****************************************************************************/
574 /* InputData functions */
575 /*****************************************************************************/
579 static void IDMarkStart (CharSource* S attribute ((unused)))
580 /* Mark the start of the next token */
582 /* Nothing to do here */
587 static void IDNextChar (CharSource* S)
588 /* Read the next character from the input text */
590 C = *S->V.Data.Pos++;
592 /* End of input data */
600 void IDDone (CharSource* S)
601 /* Close the current input data */
603 /* Cleanup the current stuff */
604 if (S->V.Data.Malloced) {
605 xfree (S->V.Data.Text);
611 /* Set of input data handling functions */
612 static const CharSourceFunctions IDFunc = {
620 void NewInputData (char* Text, int Malloced)
621 /* Add a chunk of input data to the input stream */
625 /* Create a new input source variable and initialize it */
626 S = xmalloc (sizeof (*S));
628 S->V.Data.Text = Text;
629 S->V.Data.Pos = Text;
630 S->V.Data.Malloced = Malloced;
632 /* Use this input source */
638 /*****************************************************************************/
639 /* Character classification functions */
640 /*****************************************************************************/
645 /* Return true if the character is a valid character for an identifier */
647 return IsAlNum (C) ||
649 (C == '@' && AtInIdents) ||
650 (C == '$' && DollarInIdents);
655 int IsIdStart (int C)
656 /* Return true if the character may start an identifier */
658 return IsAlpha (C) || C == '_';
663 /*****************************************************************************/
665 /*****************************************************************************/
669 static unsigned DigitVal (unsigned char C)
670 /* Convert a digit into it's numerical representation */
675 return toupper (C) - 'A' + 10;
681 static void NextChar (void)
682 /* Read the next character from the input file */
684 Source->Func->NextChar (Source);
689 void LocaseSVal (void)
690 /* Make SVal lower case */
692 SB_ToLower (&CurTok.SVal);
697 void UpcaseSVal (void)
698 /* Make SVal upper case */
700 SB_ToUpper (&CurTok.SVal);
705 static int CmpDotKeyword (const void* K1, const void* K2)
706 /* Compare function for the dot keyword search */
708 return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
713 static token_t FindDotKeyword (void)
714 /* Find the dot keyword in SVal. Return the corresponding token if found,
715 ** return TOK_NONE if not found.
719 struct DotKeyword* R;
722 K.Key = SB_GetConstBuf (&CurTok.SVal);
725 /* If we aren't in ignore case mode, we have to uppercase the keyword */
730 /* Search for the keyword */
731 R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
732 sizeof (DotKeywords [0]), CmpDotKeyword);
735 /* By default, disable any somewhat experiemental DotKeyword. */
740 /* Disallow .ADDRSIZE function by default */
759 static void ReadIdent (void)
760 /* Read an identifier from the current input position into Ident. Filling SVal
761 ** starts at the current position with the next character in C. It is assumed
762 ** that any characters already filled in are ok, and the character in C is
766 /* Read the identifier */
768 SB_AppendChar (&CurTok.SVal, C);
770 } while (IsIdChar (C));
771 SB_Terminate (&CurTok.SVal);
773 /* If we should ignore case, convert the identifier to upper case */
781 static void ReadStringConst (int StringTerm)
782 /* Read a string constant into SVal. */
784 /* Skip the leading string terminator */
787 /* Read the string */
789 if (C == StringTerm) {
792 if (C == '\n' || C == EOF) {
793 Error ("Newline in string constant");
797 /* Append the char to the string */
798 SB_AppendChar (&CurTok.SVal, C);
800 /* Skip the character */
804 /* Skip the trailing terminator */
807 /* Terminate the string */
808 SB_Terminate (&CurTok.SVal);
813 static int Sweet16Reg (const StrBuf* Id)
814 /* Check if the given identifier is a sweet16 register. Return -1 if this is
815 ** not the case, return the register number otherwise.
821 if (SB_GetLen (Id) < 2) {
824 if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
827 if (!IsDigit (SB_AtUnchecked (Id, 1))) {
831 if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
832 /* Invalid register */
836 /* The register number is valid */
842 void NextRawTok (void)
843 /* Read the next raw token from the input stream */
847 /* If we've a forced end of assembly, don't read further */
849 CurTok.Tok = TOK_EOF;
854 /* Check if we have tokens from another input source */
855 if (InputFromStack ()) {
856 if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) {
857 /* This is a define style macro - expand it */
865 /* Skip whitespace, remember if we had some */
866 if ((CurTok.WS = IsBlank (C)) != 0) {
869 } while (IsBlank (C));
872 /* Mark the file position of the next token */
873 Source->Func->MarkStart (Source);
875 /* Clear the string attribute */
876 SB_Clear (&CurTok.SVal);
878 /* Generate line info for the current token */
881 /* Hex number or PC symbol? */
885 /* Hex digit must follow or DollarIsPC must be enabled */
891 Error ("Hexadecimal digit expected");
895 /* Read the number */
898 if (UnderlineInNumbers && C == '_') {
903 Error ("Number may not end with underline");
907 if (CurTok.IVal & 0xF0000000) {
908 Error ("Overflow in hexadecimal number");
911 CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C);
918 /* This is an integer constant */
919 CurTok.Tok = TOK_INTCON;
927 /* 0 or 1 must follow */
929 Error ("Binary digit expected");
932 /* Read the number */
935 if (UnderlineInNumbers && C == '_') {
940 Error ("Number may not end with underline");
944 if (CurTok.IVal & 0x80000000) {
945 Error ("Overflow in binary number");
948 CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C);
955 /* This is an integer constant */
956 CurTok.Tok = TOK_INTCON;
970 /* Ignore leading zeros */
975 /* Read the number into Buf counting the digits */
978 if (UnderlineInNumbers && C == '_') {
983 Error ("Number may not end with underline");
987 /* Buf is big enough to allow any decimal and hex number to
988 ** overflow, so ignore excess digits here, they will be detected
989 ** when we convert the value.
991 if (Digits < sizeof (Buf)) {
1000 /* Allow zilog/intel style hex numbers with a 'h' suffix */
1001 if (C == 'h' || C == 'H') {
1004 Max = 0xFFFFFFFFUL / 16;
1007 Max = 0xFFFFFFFFUL / 10;
1010 /* Convert the number using the given base */
1012 for (I = 0; I < Digits; ++I) {
1013 if (CurTok.IVal > Max) {
1014 Error ("Number out of range");
1018 DVal = DigitVal (Buf[I]);
1020 Error ("Invalid digits in number");
1024 CurTok.IVal = (CurTok.IVal * Base) + DVal;
1027 /* This is an integer constant */
1028 CurTok.Tok = TOK_INTCON;
1032 /* Control command? */
1035 /* Remember and skip the dot */
1038 /* Check if it's just a dot */
1039 if (!IsIdStart (C)) {
1042 CurTok.Tok = TOK_DOT;
1046 /* Read the remainder of the identifier */
1047 SB_AppendChar (&CurTok.SVal, '.');
1050 /* Dot keyword, search for it */
1051 CurTok.Tok = FindDotKeyword ();
1052 if (CurTok.Tok == TOK_NONE) {
1055 if (!LeadingDotInIdents) {
1056 /* Invalid pseudo instruction */
1057 Error ("`%m%p' is not a recognized control command", &CurTok.SVal);
1061 /* An identifier with a dot. Check if it's a define style
1064 if ((M = FindDefine (&CurTok.SVal)) != 0) {
1065 /* This is a define style macro - expand it */
1070 /* Just an identifier with a dot */
1071 CurTok.Tok = TOK_IDENT;
1078 /* Indirect op for sweet16 cpu. Must check this before checking for local
1079 ** symbols, because these may also use the '@' symbol.
1081 if (CPU == CPU_SWEET16 && C == '@') {
1083 CurTok.Tok = TOK_AT;
1088 if (C == LocalStart) {
1090 /* Read the identifier. */
1093 /* Start character alone is not enough */
1094 if (SB_GetLen (&CurTok.SVal) == 1) {
1095 Error ("Invalid cheap local symbol");
1099 /* A local identifier */
1100 CurTok.Tok = TOK_LOCAL_IDENT;
1105 /* Identifier or keyword? */
1106 if (IsIdStart (C)) {
1108 /* Read the identifier */
1111 /* Check for special names. Bail out if we have identified the type of
1112 ** the token. Go on if the token is an identifier.
1114 switch (SB_GetLen (&CurTok.SVal)) {
1116 switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) {
1121 CurTok.Tok = TOK_OVERRIDE_ABS;
1130 CurTok.Tok = TOK_OVERRIDE_FAR;
1136 if ((CPU == CPU_4510) || (CPU == CPU_65816)) {
1153 CurTok.Tok = TOK_OVERRIDE_ZP;
1156 if (CPU == CPU_4510) {
1168 if ((CPU == CPU_4510) &&
1169 (toupper (SB_AtUnchecked (&CurTok.SVal, 0)) == 'S') &&
1170 (toupper (SB_AtUnchecked (&CurTok.SVal, 1)) == 'P')) {
1177 if (CPU == CPU_SWEET16 &&
1178 (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) {
1180 /* A sweet16 register number in sweet16 mode */
1181 CurTok.Tok = TOK_REG;
1186 /* Check for define style macro */
1187 if ((M = FindDefine (&CurTok.SVal)) != 0) {
1188 /* Macro - expand it */
1193 CurTok.Tok = TOK_IDENT;
1198 /* Ok, let's do the switch */
1204 CurTok.Tok = TOK_PLUS;
1209 CurTok.Tok = TOK_MINUS;
1215 CurTok.Tok = TOK_DIV;
1216 } else if (CComments) {
1217 /* Remember the position, then skip the '*' */
1218 Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
1219 GetFullLineInfo (&LineInfos);
1224 LIError (&LineInfos, "Unterminated comment");
1225 ReleaseFullLineInfo (&LineInfos);
1226 DoneCollection (&LineInfos);
1234 ReleaseFullLineInfo (&LineInfos);
1235 DoneCollection (&LineInfos);
1242 CurTok.Tok = TOK_MUL;
1247 CurTok.Tok = TOK_XOR;
1254 CurTok.Tok = TOK_BOOLAND;
1256 CurTok.Tok = TOK_AND;
1264 CurTok.Tok = TOK_BOOLOR;
1266 CurTok.Tok = TOK_OR;
1276 CurTok.Tok = TOK_NAMESPACE;
1285 CurTok.Tok = TOK_ULABEL;
1294 CurTok.Tok = TOK_ULABEL;
1299 CurTok.Tok = TOK_ASSIGN;
1303 CurTok.Tok = TOK_COLON;
1310 CurTok.Tok = TOK_COMMA;
1315 while (C != '\n' && C != EOF) {
1322 CurTok.Tok = TOK_HASH;
1327 CurTok.Tok = TOK_LPAREN;
1332 CurTok.Tok = TOK_RPAREN;
1337 CurTok.Tok = TOK_LBRACK;
1342 CurTok.Tok = TOK_RBRACK;
1347 CurTok.Tok = TOK_LCURLY;
1352 CurTok.Tok = TOK_RCURLY;
1359 CurTok.Tok = TOK_LE;
1360 } else if (C == '<') {
1362 CurTok.Tok = TOK_SHL;
1363 } else if (C == '>') {
1365 CurTok.Tok = TOK_NE;
1367 CurTok.Tok = TOK_LT;
1373 CurTok.Tok = TOK_EQ;
1378 CurTok.Tok = TOK_BOOLNOT;
1385 CurTok.Tok = TOK_GE;
1386 } else if (C == '>') {
1388 CurTok.Tok = TOK_SHR;
1390 CurTok.Tok = TOK_GT;
1396 CurTok.Tok = TOK_NOT;
1400 /* Hack: If we allow ' as terminating character for strings, read
1401 ** the following stuff as a string, and check for a one character
1404 if (LooseStringTerm) {
1405 ReadStringConst ('\'');
1406 if (SB_GetLen (&CurTok.SVal) == 1) {
1407 CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0);
1408 CurTok.Tok = TOK_CHARCON;
1410 CurTok.Tok = TOK_STRCON;
1413 /* Always a character constant */
1415 if (C == EOF || IsControl (C)) {
1416 Error ("Illegal character constant");
1420 CurTok.Tok = TOK_CHARCON;
1423 if (!MissingCharTerm) {
1424 Error ("Illegal character constant");
1433 ReadStringConst ('\"');
1434 CurTok.Tok = TOK_STRCON;
1438 /* Line continuation? */
1441 /* Next char should be a LF, if not, will result in an error later */
1443 /* Ignore the '\n' */
1447 /* Make it clear what the problem is: */
1448 Error ("EOL expected.");
1455 CurTok.Tok = TOK_SEP;
1460 /* In case of the main file, do not close it, but return EOF. */
1461 if (Source && Source->Next) {
1465 CurTok.Tok = TOK_EOF;
1470 /* If we go here, we could not identify the current character. Skip it
1473 Error ("Invalid input character: 0x%02X", C & 0xFF);
1480 int GetSubKey (const char* const* Keys, unsigned Count)
1481 /* Search for a subkey in a table of keywords. The current token must be an
1482 ** identifier and all keys must be in upper case. The identifier will be
1483 ** uppercased in the process. The function returns the index of the keyword,
1484 ** or -1 if the keyword was not found.
1489 /* Must have an identifier */
1490 PRECONDITION (CurTok.Tok == TOK_IDENT);
1492 /* If we aren't in ignore case mode, we have to uppercase the identifier */
1497 /* Do a linear search (a binary search is not worth the effort) */
1498 for (I = 0; I < Count; ++I) {
1499 if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) {
1511 unsigned char ParseAddrSize (void)
1512 /* Check if the next token is a keyword that denotes an address size specifier.
1513 ** If so, return the corresponding address size constant, otherwise output an
1514 ** error message and return ADDR_SIZE_DEFAULT.
1517 unsigned char AddrSize;
1519 /* Check for an identifier */
1520 if (CurTok.Tok != TOK_IDENT) {
1521 Error ("Address size specifier expected");
1522 return ADDR_SIZE_DEFAULT;
1525 /* Convert the attribute */
1526 AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal));
1527 if (AddrSize == ADDR_SIZE_INVALID) {
1528 Error ("Address size specifier expected");
1529 AddrSize = ADDR_SIZE_DEFAULT;
1538 void InitScanner (const char* InFile)
1539 /* Initialize the scanner, open the given input file */
1541 /* Open the input file */
1542 NewInputFile (InFile);
1547 void DoneScanner (void)
1548 /* Release scanner resources */