1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* scanner.c */ |
4 | /* */ |
5 | /* The scanner for the ca65 macroassembler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2013, 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 | |
42 | /* common */ |
43 | #include "addrsize.h" |
44 | #include "attrib.h" |
45 | #include "chartype.h" |
46 | #include "check.h" |
47 | #include "filestat.h" |
48 | #include "fname.h" |
49 | #include "xmalloc.h" |
50 | |
51 | /* ca65 */ |
52 | #include "condasm.h" |
53 | #include "error.h" |
54 | #include "filetab.h" |
55 | #include "global.h" |
56 | #include "incpath.h" |
57 | #include "instr.h" |
58 | #include "istack.h" |
59 | #include "listing.h" |
60 | #include "macro.h" |
61 | #include "toklist.h" |
62 | #include "scanner.h" |
63 | |
64 | |
65 | |
66 | /*****************************************************************************/ |
67 | /* Data */ |
68 | /*****************************************************************************/ |
69 | |
70 | |
71 | |
72 | /* Current input token incl. attributes */ |
73 | Token CurTok = STATIC_TOKEN_INITIALIZER; |
74 | |
75 | /* Struct to handle include files. */ |
76 | typedef struct InputFile InputFile; |
77 | struct 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 */ |
86 | }; |
87 | |
88 | /* Struct to handle textual input data */ |
89 | typedef struct InputData InputData; |
90 | struct 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 */ |
97 | }; |
98 | |
99 | /* Input source: Either file or data */ |
100 | typedef struct CharSource CharSource; |
101 | |
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 */ |
108 | }; |
109 | |
110 | /* Input source: Either file or data */ |
111 | struct CharSource { |
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 */ |
116 | union { |
117 | InputFile File; /* File data */ |
118 | InputData Data; /* Textual data */ |
119 | } V; |
120 | }; |
121 | |
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 */ |
126 | |
127 | /* Force end of assembly */ |
128 | int ForcedEnd = 0; |
129 | |
130 | /* List of dot keywords with the corresponding tokens */ |
131 | struct DotKeyword { |
132 | const char* Key; /* MUST be first field */ |
133 | token_t Tok; |
134 | } DotKeywords [] = { |
135 | { ".A16" , TOK_A16 }, |
136 | { ".A8" , TOK_A8 }, |
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 }, |
153 | { ".BSS" , TOK_BSS }, |
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 }, |
163 | { ".CPU" , TOK_CPU }, |
164 | { ".DATA" , TOK_DATA }, |
165 | { ".DBG" , TOK_DBG }, |
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 }, |
178 | { ".END" , TOK_END }, |
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 }, |
207 | { ".I16" , TOK_I16 }, |
208 | { ".I8" , TOK_I8 }, |
209 | { ".IDENT" , TOK_MAKEIDENT }, |
210 | { ".IF" , TOK_IF }, |
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 }, |
245 | { ".MAX" , TOK_MAX }, |
246 | { ".MID" , TOK_MID }, |
247 | { ".MIN" , TOK_MIN }, |
248 | { ".MOD" , TOK_MOD }, |
249 | { ".NOT" , TOK_BOOLNOT }, |
250 | { ".NULL" , TOK_NULL }, |
251 | { ".OR" , TOK_BOOLOR }, |
252 | { ".ORG" , TOK_ORG }, |
253 | { ".OUT" , TOK_OUT }, |
254 | { ".P02" , TOK_P02 }, |
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 }, |
271 | { ".RES" , TOK_RES }, |
272 | { ".RIGHT" , TOK_RIGHT }, |
273 | { ".RODATA" , TOK_RODATA }, |
274 | { ".SCOPE" , TOK_SCOPE }, |
275 | { ".SEGMENT" , TOK_SEGMENT }, |
276 | { ".SET" , TOK_SET }, |
277 | { ".SETCPU" , TOK_SETCPU }, |
278 | { ".SHL" , TOK_SHL }, |
279 | { ".SHR" , TOK_SHR }, |
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 }, |
287 | { ".TAG" , TOK_TAG }, |
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 }, |
299 | }; |
300 | |
301 | |
302 | |
303 | /*****************************************************************************/ |
304 | /* CharSource functions */ |
305 | /*****************************************************************************/ |
306 | |
307 | |
308 | |
309 | static void UseCharSource (CharSource* S) |
310 | /* Initialize a new input source and start to use it. */ |
311 | { |
312 | /* Remember the current input char and token */ |
313 | S->Tok = CurTok.Tok; |
314 | S->C = C; |
315 | |
316 | /* Use the new input source */ |
317 | S->Next = Source; |
318 | Source = S; |
319 | |
320 | /* Read the first character from the new file */ |
321 | S->Func->NextChar (S); |
322 | |
323 | /* Setup the next token so it will be skipped on the next call to |
324 | ** NextRawTok(). |
325 | */ |
326 | CurTok.Tok = TOK_SEP; |
327 | } |
328 | |
329 | |
330 | |
331 | static void DoneCharSource (void) |
332 | /* Close the top level character source */ |
333 | { |
334 | CharSource* S; |
335 | |
336 | /* First, call the type specific function */ |
337 | Source->Func->Done (Source); |
338 | |
339 | /* Restore the old token */ |
340 | CurTok.Tok = Source->Tok; |
341 | C = Source->C; |
342 | |
343 | /* Remember the last stacked input source */ |
344 | S = Source->Next; |
345 | |
346 | /* Delete the top level one ... */ |
347 | xfree (Source); |
348 | |
349 | /* ... and use the one before */ |
350 | Source = S; |
351 | } |
352 | |
353 | |
354 | |
355 | /*****************************************************************************/ |
356 | /* InputFile functions */ |
357 | /*****************************************************************************/ |
358 | |
359 | |
360 | |
361 | static void IFMarkStart (CharSource* S) |
362 | /* Mark the start of the next token */ |
363 | { |
364 | CurTok.Pos = S->V.File.Pos; |
365 | } |
366 | |
367 | |
368 | |
369 | static void IFNextChar (CharSource* S) |
370 | /* Read the next character from the input file */ |
371 | { |
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)) { |
374 | |
375 | unsigned Len; |
376 | |
377 | /* End of current line reached, read next line */ |
378 | SB_Clear (&S->V.File.Line); |
379 | while (1) { |
380 | |
381 | int N = fgetc (S->V.File.F); |
382 | if (N == EOF) { |
383 | /* End of file. Accept files without a newline at the end */ |
384 | if (SB_NotEmpty (&S->V.File.Line)) { |
385 | break; |
386 | } |
387 | |
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. |
390 | */ |
391 | NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount); |
392 | C = EOF; |
393 | return; |
394 | |
395 | /* Check for end of line */ |
396 | } else if (N == '\n') { |
397 | |
398 | /* End of line */ |
399 | break; |
400 | |
401 | /* Collect other stuff */ |
402 | } else { |
403 | |
404 | /* Append data to line */ |
405 | SB_AppendChar (&S->V.File.Line, N); |
406 | |
407 | } |
408 | } |
409 | |
410 | |
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. |
414 | */ |
415 | Len = SB_GetLen (&S->V.File.Line); |
416 | while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) { |
417 | --Len; |
418 | } |
419 | SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len); |
420 | SB_AppendChar (&S->V.File.Line, '\n'); |
421 | |
422 | /* Terminate the string buffer */ |
423 | SB_Terminate (&S->V.File.Line); |
424 | |
425 | /* One more line */ |
426 | S->V.File.Pos.Line++; |
427 | |
428 | /* Remember the new line for the listing */ |
429 | NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount); |
430 | |
431 | } |
432 | |
433 | /* Set the column pointer */ |
434 | S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line); |
435 | |
436 | /* Return the next character from the buffer */ |
437 | C = SB_Get (&S->V.File.Line); |
438 | } |
439 | |
440 | |
441 | |
442 | void IFDone (CharSource* S) |
443 | /* Close the current input file */ |
444 | { |
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. |
450 | */ |
451 | CheckOpenIfs (); |
452 | |
453 | /* If we've added search paths for this file, remove them */ |
454 | if (S->V.File.IncSearchPath) { |
455 | PopSearchPath (IncSearchPath); |
456 | } |
457 | if (S->V.File.BinSearchPath) { |
458 | PopSearchPath (BinSearchPath); |
459 | } |
460 | |
461 | /* Free the line buffer */ |
462 | SB_Done (&S->V.File.Line); |
463 | |
464 | /* Close the input file and decrement the file count. We will ignore |
465 | ** errors here, since we were just reading from the file. |
466 | */ |
467 | (void) fclose (S->V.File.F); |
468 | --FCount; |
469 | } |
470 | |
471 | |
472 | |
473 | /* Set of input file handling functions */ |
474 | static const CharSourceFunctions IFFunc = { |
475 | IFMarkStart, |
476 | IFNextChar, |
477 | IFDone |
478 | }; |
479 | |
480 | |
481 | |
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. |
485 | */ |
486 | { |
487 | int RetCode = 0; /* Return code. Assume an error. */ |
488 | char* PathName = 0; |
489 | FILE* F; |
490 | struct stat Buf; |
491 | StrBuf NameBuf; /* No need to initialize */ |
492 | StrBuf Path = AUTO_STRBUF_INITIALIZER; |
493 | unsigned FileIdx; |
494 | CharSource* S; |
495 | |
496 | |
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. |
499 | */ |
500 | if (FCount == 0) { |
501 | /* Main file */ |
502 | F = fopen (Name, "r" ); |
503 | if (F == 0) { |
504 | Fatal ("Cannot open input file '%s': %s" , Name, strerror (errno)); |
505 | } |
506 | } else { |
507 | /* We are on include level. Search for the file in the include |
508 | ** directories. |
509 | */ |
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)); |
514 | goto ExitPoint; |
515 | } |
516 | |
517 | /* Use the path name from now on */ |
518 | Name = PathName; |
519 | } |
520 | |
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 |
527 | ** here. |
528 | */ |
529 | if (FileStat (Name, &Buf) != 0) { |
530 | Fatal ("Cannot stat input file '%s': %s" , Name, strerror (errno)); |
531 | } |
532 | |
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); |
537 | |
538 | /* Create a new input source variable and initialize it */ |
539 | S = xmalloc (sizeof (*S)); |
540 | S->Func = &IFFunc; |
541 | S->V.File.F = F; |
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); |
546 | |
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)); |
552 | SB_Done (&Path); |
553 | |
554 | /* Count active input files */ |
555 | ++FCount; |
556 | |
557 | /* Use this input source */ |
558 | UseCharSource (S); |
559 | |
560 | /* File successfully opened */ |
561 | RetCode = 1; |
562 | |
563 | ExitPoint: |
564 | /* Free an allocated name buffer */ |
565 | xfree (PathName); |
566 | |
567 | /* Return the success code */ |
568 | return RetCode; |
569 | } |
570 | |
571 | |
572 | |
573 | /*****************************************************************************/ |
574 | /* InputData functions */ |
575 | /*****************************************************************************/ |
576 | |
577 | |
578 | |
579 | static void IDMarkStart (CharSource* S attribute ((unused))) |
580 | /* Mark the start of the next token */ |
581 | { |
582 | /* Nothing to do here */ |
583 | } |
584 | |
585 | |
586 | |
587 | static void IDNextChar (CharSource* S) |
588 | /* Read the next character from the input text */ |
589 | { |
590 | C = *S->V.Data.Pos++; |
591 | if (C == '\0') { |
592 | /* End of input data */ |
593 | --S->V.Data.Pos; |
594 | C = EOF; |
595 | } |
596 | } |
597 | |
598 | |
599 | |
600 | void IDDone (CharSource* S) |
601 | /* Close the current input data */ |
602 | { |
603 | /* Cleanup the current stuff */ |
604 | if (S->V.Data.Malloced) { |
605 | xfree (S->V.Data.Text); |
606 | } |
607 | } |
608 | |
609 | |
610 | |
611 | /* Set of input data handling functions */ |
612 | static const CharSourceFunctions IDFunc = { |
613 | IDMarkStart, |
614 | IDNextChar, |
615 | IDDone |
616 | }; |
617 | |
618 | |
619 | |
620 | void NewInputData (char* Text, int Malloced) |
621 | /* Add a chunk of input data to the input stream */ |
622 | { |
623 | CharSource* S; |
624 | |
625 | /* Create a new input source variable and initialize it */ |
626 | S = xmalloc (sizeof (*S)); |
627 | S->Func = &IDFunc; |
628 | S->V.Data.Text = Text; |
629 | S->V.Data.Pos = Text; |
630 | S->V.Data.Malloced = Malloced; |
631 | |
632 | /* Use this input source */ |
633 | UseCharSource (S); |
634 | } |
635 | |
636 | |
637 | |
638 | /*****************************************************************************/ |
639 | /* Character classification functions */ |
640 | /*****************************************************************************/ |
641 | |
642 | |
643 | |
644 | int IsIdChar (int C) |
645 | /* Return true if the character is a valid character for an identifier */ |
646 | { |
647 | return IsAlNum (C) || |
648 | (C == '_') || |
649 | (C == '@' && AtInIdents) || |
650 | (C == '$' && DollarInIdents); |
651 | } |
652 | |
653 | |
654 | |
655 | int IsIdStart (int C) |
656 | /* Return true if the character may start an identifier */ |
657 | { |
658 | return IsAlpha (C) || C == '_'; |
659 | } |
660 | |
661 | |
662 | |
663 | /*****************************************************************************/ |
664 | /* Code */ |
665 | /*****************************************************************************/ |
666 | |
667 | |
668 | |
669 | static unsigned DigitVal (unsigned char C) |
670 | /* Convert a digit into it's numerical representation */ |
671 | { |
672 | if (IsDigit (C)) { |
673 | return C - '0'; |
674 | } else { |
675 | return toupper (C) - 'A' + 10; |
676 | } |
677 | } |
678 | |
679 | |
680 | |
681 | static void NextChar (void) |
682 | /* Read the next character from the input file */ |
683 | { |
684 | Source->Func->NextChar (Source); |
685 | } |
686 | |
687 | |
688 | |
689 | void LocaseSVal (void) |
690 | /* Make SVal lower case */ |
691 | { |
692 | SB_ToLower (&CurTok.SVal); |
693 | } |
694 | |
695 | |
696 | |
697 | void UpcaseSVal (void) |
698 | /* Make SVal upper case */ |
699 | { |
700 | SB_ToUpper (&CurTok.SVal); |
701 | } |
702 | |
703 | |
704 | |
705 | static int CmpDotKeyword (const void* K1, const void* K2) |
706 | /* Compare function for the dot keyword search */ |
707 | { |
708 | return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key); |
709 | } |
710 | |
711 | |
712 | |
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. |
716 | */ |
717 | { |
718 | struct DotKeyword K; |
719 | struct DotKeyword* R; |
720 | |
721 | /* Initialize K */ |
722 | K.Key = SB_GetConstBuf (&CurTok.SVal); |
723 | K.Tok = 0; |
724 | |
725 | /* If we aren't in ignore case mode, we have to uppercase the keyword */ |
726 | if (!IgnoreCase) { |
727 | UpcaseSVal (); |
728 | } |
729 | |
730 | /* Search for the keyword */ |
731 | R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]), |
732 | sizeof (DotKeywords [0]), CmpDotKeyword); |
733 | if (R != 0) { |
734 | |
735 | /* By default, disable any somewhat experiemental DotKeyword. */ |
736 | |
737 | switch (R->Tok) { |
738 | |
739 | case TOK_ADDRSIZE: |
740 | /* Disallow .ADDRSIZE function by default */ |
741 | if (AddrSize == 0) { |
742 | return TOK_NONE; |
743 | } |
744 | break; |
745 | |
746 | default: |
747 | break; |
748 | } |
749 | |
750 | return R->Tok; |
751 | |
752 | } else { |
753 | return TOK_NONE; |
754 | } |
755 | } |
756 | |
757 | |
758 | |
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 |
763 | ** checked. |
764 | */ |
765 | { |
766 | /* Read the identifier */ |
767 | do { |
768 | SB_AppendChar (&CurTok.SVal, C); |
769 | NextChar (); |
770 | } while (IsIdChar (C)); |
771 | SB_Terminate (&CurTok.SVal); |
772 | |
773 | /* If we should ignore case, convert the identifier to upper case */ |
774 | if (IgnoreCase) { |
775 | UpcaseSVal (); |
776 | } |
777 | } |
778 | |
779 | |
780 | |
781 | static void ReadStringConst (int StringTerm) |
782 | /* Read a string constant into SVal. */ |
783 | { |
784 | /* Skip the leading string terminator */ |
785 | NextChar (); |
786 | |
787 | /* Read the string */ |
788 | while (1) { |
789 | if (C == StringTerm) { |
790 | break; |
791 | } |
792 | if (C == '\n' || C == EOF) { |
793 | Error ("Newline in string constant" ); |
794 | break; |
795 | } |
796 | |
797 | if (C == '\\' && StringEscapes) { |
798 | NextChar (); |
799 | |
800 | switch (C) { |
801 | case EOF: |
802 | Error ("Unterminated escape sequence in string constant" ); |
803 | break; |
804 | case '\\': |
805 | case '\'': |
806 | case '"': |
807 | break; |
808 | case 't': |
809 | C = '\x09'; |
810 | break; |
811 | case 'r': |
812 | C = '\x0D'; |
813 | break; |
814 | case 'n': |
815 | C = '\x0A'; |
816 | break; |
817 | case 'x': |
818 | NextChar (); |
819 | if (IsXDigit (C)) { |
820 | char high_nibble = DigitVal (C) << 4; |
821 | NextChar (); |
822 | if (IsXDigit (C)) { |
823 | C = high_nibble | DigitVal (C); |
824 | break; |
825 | } |
826 | } |
827 | /* FALLTHROUGH */ |
828 | default: |
829 | Error ("Unsupported escape sequence in string constant" ); |
830 | break; |
831 | } |
832 | } |
833 | |
834 | /* Append the char to the string */ |
835 | SB_AppendChar (&CurTok.SVal, C); |
836 | |
837 | /* Skip the character */ |
838 | NextChar (); |
839 | } |
840 | |
841 | /* Skip the trailing terminator */ |
842 | NextChar (); |
843 | |
844 | /* Terminate the string */ |
845 | SB_Terminate (&CurTok.SVal); |
846 | } |
847 | |
848 | |
849 | |
850 | static int Sweet16Reg (const StrBuf* Id) |
851 | /* Check if the given identifier is a sweet16 register. Return -1 if this is |
852 | ** not the case, return the register number otherwise. |
853 | */ |
854 | { |
855 | unsigned RegNum; |
856 | char Check; |
857 | |
858 | if (SB_GetLen (Id) < 2) { |
859 | return -1; |
860 | } |
861 | if (toupper (SB_AtUnchecked (Id, 0)) != 'R') { |
862 | return -1; |
863 | } |
864 | if (!IsDigit (SB_AtUnchecked (Id, 1))) { |
865 | return -1; |
866 | } |
867 | |
868 | if (sscanf (SB_GetConstBuf (Id)+1, "%u%c" , &RegNum, &Check) != 1 || RegNum > 15) { |
869 | /* Invalid register */ |
870 | return -1; |
871 | } |
872 | |
873 | /* The register number is valid */ |
874 | return (int) RegNum; |
875 | } |
876 | |
877 | |
878 | |
879 | void (void) |
880 | /* Read the next raw token from the input stream */ |
881 | { |
882 | Macro* M; |
883 | |
884 | /* If we've a forced end of assembly, don't read further */ |
885 | if (ForcedEnd) { |
886 | CurTok.Tok = TOK_EOF; |
887 | return; |
888 | } |
889 | |
890 | Restart: |
891 | /* Check if we have tokens from another input source */ |
892 | if (InputFromStack ()) { |
893 | if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) { |
894 | /* This is a define style macro - expand it */ |
895 | MacExpandStart (M); |
896 | goto Restart; |
897 | } |
898 | return; |
899 | } |
900 | |
901 | Again: |
902 | /* Skip whitespace, remember if we had some */ |
903 | if ((CurTok.WS = IsBlank (C)) != 0) { |
904 | do { |
905 | NextChar (); |
906 | } while (IsBlank (C)); |
907 | } |
908 | |
909 | /* Mark the file position of the next token */ |
910 | Source->Func->MarkStart (Source); |
911 | |
912 | /* Clear the string attribute */ |
913 | SB_Clear (&CurTok.SVal); |
914 | |
915 | /* Generate line info for the current token */ |
916 | NewAsmLine (); |
917 | |
918 | /* Hex number or PC symbol? */ |
919 | if (C == '$') { |
920 | NextChar (); |
921 | |
922 | /* Hex digit must follow or DollarIsPC must be enabled */ |
923 | if (!IsXDigit (C)) { |
924 | if (DollarIsPC) { |
925 | CurTok.Tok = TOK_PC; |
926 | return; |
927 | } else { |
928 | Error ("Hexadecimal digit expected" ); |
929 | } |
930 | } |
931 | |
932 | /* Read the number */ |
933 | CurTok.IVal = 0; |
934 | while (1) { |
935 | if (UnderlineInNumbers && C == '_') { |
936 | while (C == '_') { |
937 | NextChar (); |
938 | } |
939 | if (!IsXDigit (C)) { |
940 | Error ("Number may not end with underline" ); |
941 | } |
942 | } |
943 | if (IsXDigit (C)) { |
944 | if (CurTok.IVal & 0xF0000000) { |
945 | Error ("Overflow in hexadecimal number" ); |
946 | CurTok.IVal = 0; |
947 | } |
948 | CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C); |
949 | NextChar (); |
950 | } else { |
951 | break; |
952 | } |
953 | } |
954 | |
955 | /* This is an integer constant */ |
956 | CurTok.Tok = TOK_INTCON; |
957 | return; |
958 | } |
959 | |
960 | /* Binary number? */ |
961 | if (C == '%') { |
962 | NextChar (); |
963 | |
964 | /* 0 or 1 must follow */ |
965 | if (!IsBDigit (C)) { |
966 | Error ("Binary digit expected" ); |
967 | } |
968 | |
969 | /* Read the number */ |
970 | CurTok.IVal = 0; |
971 | while (1) { |
972 | if (UnderlineInNumbers && C == '_') { |
973 | while (C == '_') { |
974 | NextChar (); |
975 | } |
976 | if (!IsBDigit (C)) { |
977 | Error ("Number may not end with underline" ); |
978 | } |
979 | } |
980 | if (IsBDigit (C)) { |
981 | if (CurTok.IVal & 0x80000000) { |
982 | Error ("Overflow in binary number" ); |
983 | CurTok.IVal = 0; |
984 | } |
985 | CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C); |
986 | NextChar (); |
987 | } else { |
988 | break; |
989 | } |
990 | } |
991 | |
992 | /* This is an integer constant */ |
993 | CurTok.Tok = TOK_INTCON; |
994 | return; |
995 | } |
996 | |
997 | /* Number? */ |
998 | if (IsDigit (C)) { |
999 | |
1000 | char Buf[16]; |
1001 | unsigned Digits; |
1002 | unsigned Base; |
1003 | unsigned I; |
1004 | long Max; |
1005 | unsigned DVal; |
1006 | |
1007 | /* Ignore leading zeros */ |
1008 | while (C == '0') { |
1009 | NextChar (); |
1010 | } |
1011 | |
1012 | /* Read the number into Buf counting the digits */ |
1013 | Digits = 0; |
1014 | while (1) { |
1015 | if (UnderlineInNumbers && C == '_') { |
1016 | while (C == '_') { |
1017 | NextChar (); |
1018 | } |
1019 | if (!IsXDigit (C)) { |
1020 | Error ("Number may not end with underline" ); |
1021 | } |
1022 | } |
1023 | if (IsXDigit (C)) { |
1024 | /* Buf is big enough to allow any decimal and hex number to |
1025 | ** overflow, so ignore excess digits here, they will be detected |
1026 | ** when we convert the value. |
1027 | */ |
1028 | if (Digits < sizeof (Buf)) { |
1029 | Buf[Digits++] = C; |
1030 | } |
1031 | NextChar (); |
1032 | } else { |
1033 | break; |
1034 | } |
1035 | } |
1036 | |
1037 | /* Allow zilog/intel style hex numbers with a 'h' suffix */ |
1038 | if (C == 'h' || C == 'H') { |
1039 | NextChar (); |
1040 | Base = 16; |
1041 | Max = 0xFFFFFFFFUL / 16; |
1042 | } else { |
1043 | Base = 10; |
1044 | Max = 0xFFFFFFFFUL / 10; |
1045 | } |
1046 | |
1047 | /* Convert the number using the given base */ |
1048 | CurTok.IVal = 0; |
1049 | for (I = 0; I < Digits; ++I) { |
1050 | if (CurTok.IVal > Max) { |
1051 | Error ("Number out of range" ); |
1052 | CurTok.IVal = 0; |
1053 | break; |
1054 | } |
1055 | DVal = DigitVal (Buf[I]); |
1056 | if (DVal >= Base) { |
1057 | Error ("Invalid digits in number" ); |
1058 | CurTok.IVal = 0; |
1059 | break; |
1060 | } |
1061 | CurTok.IVal = (CurTok.IVal * Base) + DVal; |
1062 | } |
1063 | |
1064 | /* This is an integer constant */ |
1065 | CurTok.Tok = TOK_INTCON; |
1066 | return; |
1067 | } |
1068 | |
1069 | /* Control command? */ |
1070 | if (C == '.') { |
1071 | |
1072 | /* Remember and skip the dot */ |
1073 | NextChar (); |
1074 | |
1075 | /* Check if it's just a dot */ |
1076 | if (!IsIdStart (C)) { |
1077 | |
1078 | /* Just a dot */ |
1079 | CurTok.Tok = TOK_DOT; |
1080 | |
1081 | } else { |
1082 | |
1083 | /* Read the remainder of the identifier */ |
1084 | SB_AppendChar (&CurTok.SVal, '.'); |
1085 | ReadIdent (); |
1086 | |
1087 | /* Dot keyword, search for it */ |
1088 | CurTok.Tok = FindDotKeyword (); |
1089 | if (CurTok.Tok == TOK_NONE) { |
1090 | |
1091 | /* Not found */ |
1092 | if (!LeadingDotInIdents) { |
1093 | /* Invalid pseudo instruction */ |
1094 | Error ("'%m%p' is not a recognized control command" , &CurTok.SVal); |
1095 | goto Again; |
1096 | } |
1097 | |
1098 | /* An identifier with a dot. Check if it's a define style |
1099 | ** macro. |
1100 | */ |
1101 | if ((M = FindDefine (&CurTok.SVal)) != 0) { |
1102 | /* This is a define style macro - expand it */ |
1103 | MacExpandStart (M); |
1104 | goto Restart; |
1105 | } |
1106 | |
1107 | /* Just an identifier with a dot */ |
1108 | CurTok.Tok = TOK_IDENT; |
1109 | } |
1110 | |
1111 | } |
1112 | return; |
1113 | } |
1114 | |
1115 | /* Indirect op for sweet16 cpu. Must check this before checking for local |
1116 | ** symbols, because these may also use the '@' symbol. |
1117 | */ |
1118 | if (CPU == CPU_SWEET16 && C == '@') { |
1119 | NextChar (); |
1120 | CurTok.Tok = TOK_AT; |
1121 | return; |
1122 | } |
1123 | |
1124 | /* Local symbol? */ |
1125 | if (C == LocalStart) { |
1126 | |
1127 | /* Read the identifier. */ |
1128 | ReadIdent (); |
1129 | |
1130 | /* Start character alone is not enough */ |
1131 | if (SB_GetLen (&CurTok.SVal) == 1) { |
1132 | Error ("Invalid cheap local symbol" ); |
1133 | goto Again; |
1134 | } |
1135 | |
1136 | /* A local identifier */ |
1137 | CurTok.Tok = TOK_LOCAL_IDENT; |
1138 | return; |
1139 | } |
1140 | |
1141 | |
1142 | /* Identifier or keyword? */ |
1143 | if (IsIdStart (C)) { |
1144 | |
1145 | /* Read the identifier */ |
1146 | ReadIdent (); |
1147 | |
1148 | /* Check for special names. Bail out if we have identified the type of |
1149 | ** the token. Go on if the token is an identifier. |
1150 | */ |
1151 | switch (SB_GetLen (&CurTok.SVal)) { |
1152 | case 1: |
1153 | switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) { |
1154 | |
1155 | case 'A': |
1156 | if (C == ':') { |
1157 | NextChar (); |
1158 | CurTok.Tok = TOK_OVERRIDE_ABS; |
1159 | } else { |
1160 | CurTok.Tok = TOK_A; |
1161 | } |
1162 | return; |
1163 | |
1164 | case 'F': |
1165 | if (C == ':') { |
1166 | NextChar (); |
1167 | CurTok.Tok = TOK_OVERRIDE_FAR; |
1168 | return; |
1169 | } |
1170 | break; |
1171 | |
1172 | case 'S': |
1173 | if ((CPU == CPU_4510) || (CPU == CPU_65816)) { |
1174 | CurTok.Tok = TOK_S; |
1175 | return; |
1176 | } |
1177 | break; |
1178 | |
1179 | case 'X': |
1180 | CurTok.Tok = TOK_X; |
1181 | return; |
1182 | |
1183 | case 'Y': |
1184 | CurTok.Tok = TOK_Y; |
1185 | return; |
1186 | |
1187 | case 'Z': |
1188 | if (C == ':') { |
1189 | NextChar (); |
1190 | CurTok.Tok = TOK_OVERRIDE_ZP; |
1191 | return; |
1192 | } else { |
1193 | if (CPU == CPU_4510) { |
1194 | CurTok.Tok = TOK_Z; |
1195 | return; |
1196 | } |
1197 | } |
1198 | break; |
1199 | |
1200 | default: |
1201 | break; |
1202 | } |
1203 | break; |
1204 | case 2: |
1205 | if ((CPU == CPU_4510) && |
1206 | (toupper (SB_AtUnchecked (&CurTok.SVal, 0)) == 'S') && |
1207 | (toupper (SB_AtUnchecked (&CurTok.SVal, 1)) == 'P')) { |
1208 | |
1209 | CurTok.Tok = TOK_S; |
1210 | return; |
1211 | } |
1212 | /* FALL THROUGH */ |
1213 | default: |
1214 | if (CPU == CPU_SWEET16 && |
1215 | (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) { |
1216 | |
1217 | /* A sweet16 register number in sweet16 mode */ |
1218 | CurTok.Tok = TOK_REG; |
1219 | return; |
1220 | } |
1221 | } |
1222 | |
1223 | /* Check for define style macro */ |
1224 | if ((M = FindDefine (&CurTok.SVal)) != 0) { |
1225 | /* Macro - expand it */ |
1226 | MacExpandStart (M); |
1227 | goto Restart; |
1228 | } else { |
1229 | /* An identifier */ |
1230 | CurTok.Tok = TOK_IDENT; |
1231 | } |
1232 | return; |
1233 | } |
1234 | |
1235 | /* Ok, let's do the switch */ |
1236 | CharAgain: |
1237 | switch (C) { |
1238 | |
1239 | case '+': |
1240 | NextChar (); |
1241 | CurTok.Tok = TOK_PLUS; |
1242 | return; |
1243 | |
1244 | case '-': |
1245 | NextChar (); |
1246 | CurTok.Tok = TOK_MINUS; |
1247 | return; |
1248 | |
1249 | case '/': |
1250 | NextChar (); |
1251 | if (C != '*') { |
1252 | CurTok.Tok = TOK_DIV; |
1253 | } else if (CComments) { |
1254 | /* Remember the position, then skip the '*' */ |
1255 | Collection LineInfos = STATIC_COLLECTION_INITIALIZER; |
1256 | GetFullLineInfo (&LineInfos); |
1257 | NextChar (); |
1258 | do { |
1259 | while (C != '*') { |
1260 | if (C == EOF) { |
1261 | LIError (&LineInfos, "Unterminated comment" ); |
1262 | ReleaseFullLineInfo (&LineInfos); |
1263 | DoneCollection (&LineInfos); |
1264 | goto CharAgain; |
1265 | } |
1266 | NextChar (); |
1267 | } |
1268 | NextChar (); |
1269 | } while (C != '/'); |
1270 | NextChar (); |
1271 | ReleaseFullLineInfo (&LineInfos); |
1272 | DoneCollection (&LineInfos); |
1273 | goto Again; |
1274 | } |
1275 | return; |
1276 | |
1277 | case '*': |
1278 | NextChar (); |
1279 | CurTok.Tok = TOK_MUL; |
1280 | return; |
1281 | |
1282 | case '^': |
1283 | NextChar (); |
1284 | CurTok.Tok = TOK_XOR; |
1285 | return; |
1286 | |
1287 | case '&': |
1288 | NextChar (); |
1289 | if (C == '&') { |
1290 | NextChar (); |
1291 | CurTok.Tok = TOK_BOOLAND; |
1292 | } else { |
1293 | CurTok.Tok = TOK_AND; |
1294 | } |
1295 | return; |
1296 | |
1297 | case '|': |
1298 | NextChar (); |
1299 | if (C == '|') { |
1300 | NextChar (); |
1301 | CurTok.Tok = TOK_BOOLOR; |
1302 | } else { |
1303 | CurTok.Tok = TOK_OR; |
1304 | } |
1305 | return; |
1306 | |
1307 | case ':': |
1308 | NextChar (); |
1309 | switch (C) { |
1310 | |
1311 | case ':': |
1312 | NextChar (); |
1313 | CurTok.Tok = TOK_NAMESPACE; |
1314 | break; |
1315 | |
1316 | case '-': |
1317 | CurTok.IVal = 0; |
1318 | do { |
1319 | --CurTok.IVal; |
1320 | NextChar (); |
1321 | } while (C == '-'); |
1322 | CurTok.Tok = TOK_ULABEL; |
1323 | break; |
1324 | |
1325 | case '+': |
1326 | CurTok.IVal = 0; |
1327 | do { |
1328 | ++CurTok.IVal; |
1329 | NextChar (); |
1330 | } while (C == '+'); |
1331 | CurTok.Tok = TOK_ULABEL; |
1332 | break; |
1333 | |
1334 | case '=': |
1335 | NextChar (); |
1336 | CurTok.Tok = TOK_ASSIGN; |
1337 | break; |
1338 | |
1339 | default: |
1340 | CurTok.Tok = TOK_COLON; |
1341 | break; |
1342 | } |
1343 | return; |
1344 | |
1345 | case ',': |
1346 | NextChar (); |
1347 | CurTok.Tok = TOK_COMMA; |
1348 | return; |
1349 | |
1350 | case ';': |
1351 | NextChar (); |
1352 | while (C != '\n' && C != EOF) { |
1353 | NextChar (); |
1354 | } |
1355 | goto CharAgain; |
1356 | |
1357 | case '#': |
1358 | NextChar (); |
1359 | CurTok.Tok = TOK_HASH; |
1360 | return; |
1361 | |
1362 | case '(': |
1363 | NextChar (); |
1364 | CurTok.Tok = TOK_LPAREN; |
1365 | return; |
1366 | |
1367 | case ')': |
1368 | NextChar (); |
1369 | CurTok.Tok = TOK_RPAREN; |
1370 | return; |
1371 | |
1372 | case '[': |
1373 | NextChar (); |
1374 | CurTok.Tok = TOK_LBRACK; |
1375 | return; |
1376 | |
1377 | case ']': |
1378 | NextChar (); |
1379 | CurTok.Tok = TOK_RBRACK; |
1380 | return; |
1381 | |
1382 | case '{': |
1383 | NextChar (); |
1384 | CurTok.Tok = TOK_LCURLY; |
1385 | return; |
1386 | |
1387 | case '}': |
1388 | NextChar (); |
1389 | CurTok.Tok = TOK_RCURLY; |
1390 | return; |
1391 | |
1392 | case '<': |
1393 | NextChar (); |
1394 | if (C == '=') { |
1395 | NextChar (); |
1396 | CurTok.Tok = TOK_LE; |
1397 | } else if (C == '<') { |
1398 | NextChar (); |
1399 | CurTok.Tok = TOK_SHL; |
1400 | } else if (C == '>') { |
1401 | NextChar (); |
1402 | CurTok.Tok = TOK_NE; |
1403 | } else { |
1404 | CurTok.Tok = TOK_LT; |
1405 | } |
1406 | return; |
1407 | |
1408 | case '=': |
1409 | NextChar (); |
1410 | CurTok.Tok = TOK_EQ; |
1411 | return; |
1412 | |
1413 | case '!': |
1414 | NextChar (); |
1415 | CurTok.Tok = TOK_BOOLNOT; |
1416 | return; |
1417 | |
1418 | case '>': |
1419 | NextChar (); |
1420 | if (C == '=') { |
1421 | NextChar (); |
1422 | CurTok.Tok = TOK_GE; |
1423 | } else if (C == '>') { |
1424 | NextChar (); |
1425 | CurTok.Tok = TOK_SHR; |
1426 | } else { |
1427 | CurTok.Tok = TOK_GT; |
1428 | } |
1429 | return; |
1430 | |
1431 | case '~': |
1432 | NextChar (); |
1433 | CurTok.Tok = TOK_NOT; |
1434 | return; |
1435 | |
1436 | case '\'': |
1437 | /* Hack: If we allow ' as terminating character for strings, read |
1438 | ** the following stuff as a string, and check for a one character |
1439 | ** string later. |
1440 | */ |
1441 | if (LooseStringTerm) { |
1442 | ReadStringConst ('\''); |
1443 | if (SB_GetLen (&CurTok.SVal) == 1) { |
1444 | CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); |
1445 | CurTok.Tok = TOK_CHARCON; |
1446 | } else { |
1447 | CurTok.Tok = TOK_STRCON; |
1448 | } |
1449 | } else { |
1450 | /* Always a character constant */ |
1451 | NextChar (); |
1452 | if (C == EOF || IsControl (C)) { |
1453 | Error ("Illegal character constant" ); |
1454 | goto CharAgain; |
1455 | } |
1456 | CurTok.IVal = C; |
1457 | CurTok.Tok = TOK_CHARCON; |
1458 | NextChar (); |
1459 | if (C != '\'') { |
1460 | if (!MissingCharTerm) { |
1461 | Error ("Illegal character constant" ); |
1462 | } |
1463 | } else { |
1464 | NextChar (); |
1465 | } |
1466 | } |
1467 | return; |
1468 | |
1469 | case '\"': |
1470 | ReadStringConst ('\"'); |
1471 | CurTok.Tok = TOK_STRCON; |
1472 | return; |
1473 | |
1474 | case '\\': |
1475 | /* Line continuation? */ |
1476 | if (LineCont) { |
1477 | NextChar (); |
1478 | /* Next char should be a LF, if not, will result in an error later */ |
1479 | if (C == '\n') { |
1480 | /* Ignore the '\n' */ |
1481 | NextChar (); |
1482 | goto Again; |
1483 | } else { |
1484 | /* Make it clear what the problem is: */ |
1485 | Error ("EOL expected." ); |
1486 | } |
1487 | } |
1488 | break; |
1489 | |
1490 | case '\n': |
1491 | NextChar (); |
1492 | CurTok.Tok = TOK_SEP; |
1493 | return; |
1494 | |
1495 | case EOF: |
1496 | CheckInputStack (); |
1497 | /* In case of the main file, do not close it, but return EOF. */ |
1498 | if (Source && Source->Next) { |
1499 | DoneCharSource (); |
1500 | goto Again; |
1501 | } else { |
1502 | CurTok.Tok = TOK_EOF; |
1503 | } |
1504 | return; |
1505 | } |
1506 | |
1507 | /* If we go here, we could not identify the current character. Skip it |
1508 | ** and try again. |
1509 | */ |
1510 | Error ("Invalid input character: 0x%02X" , C & 0xFF); |
1511 | NextChar (); |
1512 | goto Again; |
1513 | } |
1514 | |
1515 | |
1516 | |
1517 | int GetSubKey (const char* const* Keys, unsigned Count) |
1518 | /* Search for a subkey in a table of keywords. The current token must be an |
1519 | ** identifier and all keys must be in upper case. The identifier will be |
1520 | ** uppercased in the process. The function returns the index of the keyword, |
1521 | ** or -1 if the keyword was not found. |
1522 | */ |
1523 | { |
1524 | unsigned I; |
1525 | |
1526 | /* Must have an identifier */ |
1527 | PRECONDITION (CurTok.Tok == TOK_IDENT); |
1528 | |
1529 | /* If we aren't in ignore case mode, we have to uppercase the identifier */ |
1530 | if (!IgnoreCase) { |
1531 | UpcaseSVal (); |
1532 | } |
1533 | |
1534 | /* Do a linear search (a binary search is not worth the effort) */ |
1535 | for (I = 0; I < Count; ++I) { |
1536 | if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) { |
1537 | /* Found it */ |
1538 | return I; |
1539 | } |
1540 | } |
1541 | |
1542 | /* Not found */ |
1543 | return -1; |
1544 | } |
1545 | |
1546 | |
1547 | |
1548 | unsigned char ParseAddrSize (void) |
1549 | /* Check if the next token is a keyword that denotes an address size specifier. |
1550 | ** If so, return the corresponding address size constant, otherwise output an |
1551 | ** error message and return ADDR_SIZE_DEFAULT. |
1552 | */ |
1553 | { |
1554 | unsigned char AddrSize; |
1555 | |
1556 | /* Check for an identifier */ |
1557 | if (CurTok.Tok != TOK_IDENT) { |
1558 | Error ("Address size specifier expected" ); |
1559 | return ADDR_SIZE_DEFAULT; |
1560 | } |
1561 | |
1562 | /* Convert the attribute */ |
1563 | AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal)); |
1564 | if (AddrSize == ADDR_SIZE_INVALID) { |
1565 | Error ("Address size specifier expected" ); |
1566 | AddrSize = ADDR_SIZE_DEFAULT; |
1567 | } |
1568 | |
1569 | /* Done */ |
1570 | return AddrSize; |
1571 | } |
1572 | |
1573 | |
1574 | |
1575 | void InitScanner (const char* InFile) |
1576 | /* Initialize the scanner, open the given input file */ |
1577 | { |
1578 | /* Open the input file */ |
1579 | NewInputFile (InFile); |
1580 | } |
1581 | |
1582 | |
1583 | |
1584 | void DoneScanner (void) |
1585 | /* Release scanner resources */ |
1586 | { |
1587 | DoneCharSource (); |
1588 | } |
1589 | |