1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* nexttok.c */ |
4 | /* */ |
5 | /* Get next token and handle token level functions */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2000-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 <string.h> |
38 | |
39 | /* common */ |
40 | #include "chartype.h" |
41 | #include "check.h" |
42 | #include "strbuf.h" |
43 | |
44 | /* ca65 */ |
45 | #include "condasm.h" |
46 | #include "error.h" |
47 | #include "expr.h" |
48 | #include "global.h" |
49 | #include "scanner.h" |
50 | #include "toklist.h" |
51 | #include "nexttok.h" |
52 | |
53 | |
54 | |
55 | /*****************************************************************************/ |
56 | /* Data */ |
57 | /*****************************************************************************/ |
58 | |
59 | |
60 | |
61 | static unsigned RawMode = 0; /* Raw token mode flag/counter */ |
62 | |
63 | |
64 | |
65 | /*****************************************************************************/ |
66 | /* Error handling */ |
67 | /*****************************************************************************/ |
68 | |
69 | |
70 | |
71 | static int LookAtStrCon (void) |
72 | /* Make sure the next token is a string constant. If not, print an error |
73 | ** messages skip the remainder of the line and return false. Otherwise return |
74 | ** true. |
75 | */ |
76 | { |
77 | if (CurTok.Tok != TOK_STRCON) { |
78 | Error ("String constant expected" ); |
79 | SkipUntilSep (); |
80 | return 0; |
81 | } else { |
82 | return 1; |
83 | } |
84 | } |
85 | |
86 | |
87 | |
88 | /*****************************************************************************/ |
89 | /* Code */ |
90 | /*****************************************************************************/ |
91 | |
92 | |
93 | |
94 | static TokList* CollectTokens (unsigned Start, unsigned Count) |
95 | /* Read a list of tokens that is optionally enclosed in curly braces and |
96 | ** terminated by a right paren. For all tokens starting at the one with index |
97 | ** Start, and ending at (Start+Count-1), place them into a token list, and |
98 | ** return this token list. |
99 | */ |
100 | { |
101 | |
102 | /* Create the token list */ |
103 | TokList* List = NewTokList (); |
104 | |
105 | /* Determine if the list is enclosed in curly braces. */ |
106 | token_t Term = GetTokListTerm (TOK_RPAREN); |
107 | |
108 | /* Read the token list */ |
109 | unsigned Current = 0; |
110 | while (CurTok.Tok != Term) { |
111 | |
112 | /* Check for end of line or end of input */ |
113 | if (TokIsSep (CurTok.Tok)) { |
114 | Error ("Unexpected end of line" ); |
115 | return List; |
116 | } |
117 | |
118 | /* Collect tokens in the given range */ |
119 | if (Current >= Start && Current < Start+Count) { |
120 | /* Add the current token to the list */ |
121 | AddCurTok (List); |
122 | } |
123 | |
124 | /* Get the next token */ |
125 | ++Current; |
126 | NextTok (); |
127 | } |
128 | |
129 | /* Eat the terminator token */ |
130 | NextTok (); |
131 | |
132 | /* If the list was enclosed in curly braces, we do expect now a right paren */ |
133 | if (Term == TOK_RCURLY) { |
134 | ConsumeRParen (); |
135 | } |
136 | |
137 | /* Return the list of collected tokens */ |
138 | return List; |
139 | } |
140 | |
141 | |
142 | |
143 | static void FuncConcat (void) |
144 | /* Handle the .CONCAT function */ |
145 | { |
146 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
147 | |
148 | /* Skip it */ |
149 | NextTok (); |
150 | |
151 | /* Left paren expected */ |
152 | ConsumeLParen (); |
153 | |
154 | /* Concatenate any number of strings */ |
155 | while (1) { |
156 | |
157 | /* Next token must be a string */ |
158 | if (!LookAtStrCon ()) { |
159 | SB_Done (&Buf); |
160 | return; |
161 | } |
162 | |
163 | /* Append the string */ |
164 | SB_Append (&Buf, &CurTok.SVal); |
165 | |
166 | /* Skip the string token */ |
167 | NextTok (); |
168 | |
169 | /* Comma means another argument */ |
170 | if (CurTok.Tok == TOK_COMMA) { |
171 | NextTok (); |
172 | } else { |
173 | /* Done */ |
174 | break; |
175 | } |
176 | } |
177 | |
178 | /* We expect a closing parenthesis, but will not skip it but replace it |
179 | ** by the string token just created. |
180 | */ |
181 | if (CurTok.Tok != TOK_RPAREN) { |
182 | Error ("')' expected" ); |
183 | } else { |
184 | CurTok.Tok = TOK_STRCON; |
185 | SB_Copy (&CurTok.SVal, &Buf); |
186 | SB_Terminate (&CurTok.SVal); |
187 | } |
188 | |
189 | /* Free the string buffer */ |
190 | SB_Done (&Buf); |
191 | } |
192 | |
193 | |
194 | |
195 | static void NoIdent (void) |
196 | /* Print an error message and skip the remainder of the line */ |
197 | { |
198 | Error ("Argument of .IDENT is not a valid identifier" ); |
199 | SkipUntilSep (); |
200 | } |
201 | |
202 | |
203 | |
204 | static void FuncIdent (void) |
205 | /* Handle the .IDENT function */ |
206 | { |
207 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
208 | token_t Id; |
209 | unsigned I; |
210 | |
211 | /* Skip it */ |
212 | NextTok (); |
213 | |
214 | /* Left paren expected */ |
215 | ConsumeLParen (); |
216 | |
217 | /* The function expects a string argument */ |
218 | if (!LookAtStrCon ()) { |
219 | return; |
220 | } |
221 | |
222 | /* Check that the string contains a valid identifier. While doing so, |
223 | ** determine if it is a cheap local, or global one. |
224 | */ |
225 | SB_Reset (&CurTok.SVal); |
226 | |
227 | /* Check for a cheap local symbol */ |
228 | if (SB_Peek (&CurTok.SVal) == LocalStart) { |
229 | SB_Skip (&CurTok.SVal); |
230 | Id = TOK_LOCAL_IDENT; |
231 | } else { |
232 | Id = TOK_IDENT; |
233 | } |
234 | |
235 | /* Next character must be a valid identifier start */ |
236 | if (!IsIdStart (SB_Get (&CurTok.SVal))) { |
237 | NoIdent (); |
238 | return; |
239 | } |
240 | for (I = SB_GetIndex (&CurTok.SVal); I < SB_GetLen (&CurTok.SVal); ++I) { |
241 | if (!IsIdChar (SB_AtUnchecked (&CurTok.SVal, I))) { |
242 | NoIdent (); |
243 | return; |
244 | } |
245 | } |
246 | if (IgnoreCase) { |
247 | UpcaseSVal (); |
248 | } |
249 | |
250 | /* If anything is ok, save and skip the string. Check that the next token |
251 | ** is a right paren, then replace the token by an identifier token. |
252 | */ |
253 | SB_Copy (&Buf, &CurTok.SVal); |
254 | NextTok (); |
255 | if (CurTok.Tok != TOK_RPAREN) { |
256 | Error ("')' expected" ); |
257 | } else { |
258 | CurTok.Tok = Id; |
259 | SB_Copy (&CurTok.SVal, &Buf); |
260 | SB_Terminate (&CurTok.SVal); |
261 | } |
262 | |
263 | /* Free buffer memory */ |
264 | SB_Done (&Buf); |
265 | } |
266 | |
267 | |
268 | |
269 | static void FuncLeft (void) |
270 | /* Handle the .LEFT function */ |
271 | { |
272 | long Count; |
273 | TokList* List; |
274 | |
275 | /* Skip it */ |
276 | NextTok (); |
277 | |
278 | /* Left paren expected */ |
279 | ConsumeLParen (); |
280 | |
281 | /* Count argument. Correct negative counts to zero. */ |
282 | Count = ConstExpression (); |
283 | if (Count < 0) { |
284 | Count = 0; |
285 | } |
286 | ConsumeComma (); |
287 | |
288 | /* Read the token list */ |
289 | List = CollectTokens (0, (unsigned) Count); |
290 | |
291 | /* Since we want to insert the list before the now current token, we have |
292 | ** to save the current token in some way and then skip it. To do this, we |
293 | ** will add the current token at the end of the token list (so the list |
294 | ** will never be empty), push the token list, and then skip the current |
295 | ** token. This will replace the current token by the first token from the |
296 | ** list (which will be the old current token in case the list was empty). |
297 | */ |
298 | AddCurTok (List); |
299 | |
300 | /* Insert it into the scanner feed */ |
301 | PushTokList (List, ".LEFT" ); |
302 | |
303 | /* Skip the current token */ |
304 | NextTok (); |
305 | } |
306 | |
307 | |
308 | |
309 | static void FuncMid (void) |
310 | /* Handle the .MID function */ |
311 | { |
312 | long Start; |
313 | long Count; |
314 | TokList* List; |
315 | |
316 | /* Skip it */ |
317 | NextTok (); |
318 | |
319 | /* Left paren expected */ |
320 | ConsumeLParen (); |
321 | |
322 | /* Start argument. Since the start argument can get negative with |
323 | ** expressions like ".tcount(arg)-2", we correct it to zero silently. |
324 | */ |
325 | Start = ConstExpression (); |
326 | if (Start < 0 || Start > 100) { |
327 | Start = 0; |
328 | } |
329 | ConsumeComma (); |
330 | |
331 | /* Count argument. Similar as above, we will accept negative counts and |
332 | ** correct them to zero silently. |
333 | */ |
334 | Count = ConstExpression (); |
335 | if (Count < 0) { |
336 | Count = 0; |
337 | } |
338 | ConsumeComma (); |
339 | |
340 | /* Read the token list */ |
341 | List = CollectTokens ((unsigned) Start, (unsigned) Count); |
342 | |
343 | /* Since we want to insert the list before the now current token, we have |
344 | ** to save the current token in some way and then skip it. To do this, we |
345 | ** will add the current token at the end of the token list (so the list |
346 | ** will never be empty), push the token list, and then skip the current |
347 | ** token. This will replace the current token by the first token from the |
348 | ** list (which will be the old current token in case the list was empty). |
349 | */ |
350 | AddCurTok (List); |
351 | |
352 | /* Insert it into the scanner feed */ |
353 | PushTokList (List, ".MID" ); |
354 | |
355 | /* Skip the current token */ |
356 | NextTok (); |
357 | } |
358 | |
359 | |
360 | |
361 | static void FuncRight (void) |
362 | /* Handle the .RIGHT function */ |
363 | { |
364 | long Count; |
365 | TokList* List; |
366 | |
367 | /* Skip it */ |
368 | NextTok (); |
369 | |
370 | /* Left paren expected */ |
371 | ConsumeLParen (); |
372 | |
373 | /* Count argument. Correct negative counts to zero. */ |
374 | Count = ConstExpression (); |
375 | if (Count < 0) { |
376 | Count = 0; |
377 | } |
378 | ConsumeComma (); |
379 | |
380 | /* Read the complete token list */ |
381 | List = CollectTokens (0, 9999); |
382 | |
383 | /* Delete tokens from the list until Count tokens are remaining */ |
384 | while (List->Count > (unsigned) Count) { |
385 | /* Get the first node */ |
386 | TokNode* T = List->Root; |
387 | |
388 | /* Remove it from the list */ |
389 | List->Root = List->Root->Next; |
390 | |
391 | /* Free the node */ |
392 | FreeTokNode (T); |
393 | |
394 | /* Corrent the token counter */ |
395 | List->Count--; |
396 | } |
397 | |
398 | /* Since we want to insert the list before the now current token, we have |
399 | ** to save the current token in some way and then skip it. To do this, we |
400 | ** will add the current token at the end of the token list (so the list |
401 | ** will never be empty), push the token list, and then skip the current |
402 | ** token. This will replace the current token by the first token from the |
403 | ** list (which will be the old current token in case the list was empty). |
404 | */ |
405 | AddCurTok (List); |
406 | |
407 | /* Insert it into the scanner feed */ |
408 | PushTokList (List, ".RIGHT" ); |
409 | |
410 | /* Skip the current token */ |
411 | NextTok (); |
412 | } |
413 | |
414 | |
415 | |
416 | static void InvalidFormatString (void) |
417 | /* Print an error message and skip the remainder of the line */ |
418 | { |
419 | Error ("Invalid format string" ); |
420 | SkipUntilSep (); |
421 | } |
422 | |
423 | |
424 | |
425 | static void FuncSPrintF (void) |
426 | /* Handle the .SPRINTF function */ |
427 | { |
428 | StrBuf Format = STATIC_STRBUF_INITIALIZER; /* User supplied format */ |
429 | StrBuf R = STATIC_STRBUF_INITIALIZER; /* Result string */ |
430 | StrBuf F1 = STATIC_STRBUF_INITIALIZER; /* One format spec from F */ |
431 | StrBuf R1 = STATIC_STRBUF_INITIALIZER; /* One result */ |
432 | char C; |
433 | int Done; |
434 | long IVal; /* Integer value */ |
435 | |
436 | |
437 | |
438 | /* Skip the .SPRINTF token */ |
439 | NextTok (); |
440 | |
441 | /* Left paren expected */ |
442 | ConsumeLParen (); |
443 | |
444 | /* First argument is a format string. Remember and skip it */ |
445 | if (!LookAtStrCon ()) { |
446 | return; |
447 | } |
448 | SB_Copy (&Format, &CurTok.SVal); |
449 | NextTok (); |
450 | |
451 | /* Walk over the format string, generating the function result in R */ |
452 | while (1) { |
453 | |
454 | /* Get the next char from the format string and check for EOS */ |
455 | if (SB_Peek (&Format) == '\0') { |
456 | break; |
457 | } |
458 | |
459 | /* Check for a format specifier */ |
460 | if (SB_Peek (&Format) != '%') { |
461 | /* No format specifier, just copy */ |
462 | SB_AppendChar (&R, SB_Get (&Format)); |
463 | continue; |
464 | } |
465 | SB_Skip (&Format); |
466 | if (SB_Peek (&Format) == '%') { |
467 | /* %% */ |
468 | SB_AppendChar (&R, '%'); |
469 | SB_Skip (&Format); |
470 | continue; |
471 | } |
472 | if (SB_Peek (&Format) == '\0') { |
473 | InvalidFormatString (); |
474 | break; |
475 | } |
476 | |
477 | /* Since a format specifier follows, we do expect another argument for |
478 | ** the .sprintf function. |
479 | */ |
480 | ConsumeComma (); |
481 | |
482 | /* We will copy the format spec into F1 checking for the things we |
483 | ** support, and later use xsprintf to do the actual formatting. This |
484 | ** is easier than adding another printf implementation... |
485 | */ |
486 | SB_Clear (&F1); |
487 | SB_AppendChar (&F1, '%'); |
488 | |
489 | /* Check for flags */ |
490 | Done = 0; |
491 | while ((C = SB_Peek (&Format)) != '\0' && !Done) { |
492 | switch (C) { |
493 | case '-': /* FALLTHROUGH */ |
494 | case '+': /* FALLTHROUGH */ |
495 | case ' ': /* FALLTHROUGH */ |
496 | case '#': /* FALLTHROUGH */ |
497 | case '0': SB_AppendChar (&F1, SB_Get (&Format)); break; |
498 | default: Done = 1; break; |
499 | } |
500 | } |
501 | |
502 | /* We do only support a numerical width field */ |
503 | while (IsDigit (SB_Peek (&Format))) { |
504 | SB_AppendChar (&F1, SB_Get (&Format)); |
505 | } |
506 | |
507 | /* Precision - only positive numerical fields supported */ |
508 | if (SB_Peek (&Format) == '.') { |
509 | SB_AppendChar (&F1, SB_Get (&Format)); |
510 | while (IsDigit (SB_Peek (&Format))) { |
511 | SB_AppendChar (&F1, SB_Get (&Format)); |
512 | } |
513 | } |
514 | |
515 | /* Length modifiers aren't supported, so read the conversion specs */ |
516 | switch (SB_Peek (&Format)) { |
517 | |
518 | case 'd': |
519 | case 'i': |
520 | case 'o': |
521 | case 'u': |
522 | case 'X': |
523 | case 'x': |
524 | /* Our ints are actually longs, so we use the 'l' modifier when |
525 | ** calling xsprintf later. Terminate the format string. |
526 | */ |
527 | SB_AppendChar (&F1, 'l'); |
528 | SB_AppendChar (&F1, SB_Get (&Format)); |
529 | SB_Terminate (&F1); |
530 | |
531 | /* The argument must be a constant expression */ |
532 | IVal = ConstExpression (); |
533 | |
534 | /* Format this argument according to the spec */ |
535 | SB_Printf (&R1, SB_GetConstBuf (&F1), IVal); |
536 | |
537 | /* Append the formatted argument to the result */ |
538 | SB_Append (&R, &R1); |
539 | |
540 | break; |
541 | |
542 | case 's': |
543 | /* Add the format spec and terminate the format */ |
544 | SB_AppendChar (&F1, SB_Get (&Format)); |
545 | SB_Terminate (&F1); |
546 | |
547 | /* The argument must be a string constant */ |
548 | if (!LookAtStrCon ()) { |
549 | /* Make it one */ |
550 | SB_CopyStr (&CurTok.SVal, "**undefined**" ); |
551 | } |
552 | |
553 | /* Format this argument according to the spec */ |
554 | SB_Printf (&R1, SB_GetConstBuf (&F1), SB_GetConstBuf (&CurTok.SVal)); |
555 | |
556 | /* Skip the string constant */ |
557 | NextTok (); |
558 | |
559 | /* Append the formatted argument to the result */ |
560 | SB_Append (&R, &R1); |
561 | |
562 | break; |
563 | |
564 | case 'c': |
565 | /* Add the format spec and terminate the format */ |
566 | SB_AppendChar (&F1, SB_Get (&Format)); |
567 | SB_Terminate (&F1); |
568 | |
569 | /* The argument must be a constant expression */ |
570 | IVal = ConstExpression (); |
571 | |
572 | /* Check for a valid character range */ |
573 | if (IVal <= 0 || IVal > 255) { |
574 | Error ("Char argument out of range" ); |
575 | IVal = ' '; |
576 | } |
577 | |
578 | /* Format this argument according to the spec. Be sure to pass |
579 | ** an int as the char value. |
580 | */ |
581 | SB_Printf (&R1, SB_GetConstBuf (&F1), (int) IVal); |
582 | |
583 | /* Append the formatted argument to the result */ |
584 | SB_Append (&R, &R1); |
585 | |
586 | break; |
587 | |
588 | default: |
589 | Error ("Invalid format string" ); |
590 | SB_Skip (&Format); |
591 | break; |
592 | } |
593 | |
594 | } |
595 | |
596 | /* Terminate the result string */ |
597 | SB_Terminate (&R); |
598 | |
599 | /* We expect a closing parenthesis, but will not skip it but replace it |
600 | ** by the string token just created. |
601 | */ |
602 | if (CurTok.Tok != TOK_RPAREN) { |
603 | Error ("')' expected" ); |
604 | } else { |
605 | CurTok.Tok = TOK_STRCON; |
606 | SB_Copy (&CurTok.SVal, &R); |
607 | SB_Terminate (&CurTok.SVal); |
608 | } |
609 | |
610 | |
611 | /* Delete the string buffers */ |
612 | SB_Done (&Format); |
613 | SB_Done (&R); |
614 | SB_Done (&F1); |
615 | SB_Done (&R1); |
616 | } |
617 | |
618 | |
619 | |
620 | static void FuncString (void) |
621 | /* Handle the .STRING function */ |
622 | { |
623 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
624 | |
625 | /* Skip it */ |
626 | NextTok (); |
627 | |
628 | /* Left paren expected */ |
629 | ConsumeLParen (); |
630 | |
631 | /* Accept identifiers or numeric expressions */ |
632 | if (CurTok.Tok == TOK_LOCAL_IDENT) { |
633 | /* Save the identifier, then skip it */ |
634 | SB_Copy (&Buf, &CurTok.SVal); |
635 | NextTok (); |
636 | } else if (CurTok.Tok == TOK_NAMESPACE || CurTok.Tok == TOK_IDENT) { |
637 | |
638 | /* Parse a fully qualified symbol name. We cannot use |
639 | ** ParseScopedSymName here since the name may be invalid. |
640 | */ |
641 | int NameSpace; |
642 | do { |
643 | NameSpace = (CurTok.Tok == TOK_NAMESPACE); |
644 | if (NameSpace) { |
645 | SB_AppendStr (&Buf, "::" ); |
646 | } else { |
647 | SB_Append (&Buf, &CurTok.SVal); |
648 | } |
649 | NextTok (); |
650 | } while ((NameSpace != 0 && CurTok.Tok == TOK_IDENT) || |
651 | (NameSpace == 0 && CurTok.Tok == TOK_NAMESPACE)); |
652 | |
653 | } else { |
654 | /* Numeric expression */ |
655 | long Val = ConstExpression (); |
656 | SB_Printf (&Buf, "%ld" , Val); |
657 | } |
658 | |
659 | /* We expect a closing parenthesis, but will not skip it but replace it |
660 | ** by the string token just created. |
661 | */ |
662 | if (CurTok.Tok != TOK_RPAREN) { |
663 | Error ("')' expected" ); |
664 | } else { |
665 | CurTok.Tok = TOK_STRCON; |
666 | SB_Copy (&CurTok.SVal, &Buf); |
667 | SB_Terminate (&CurTok.SVal); |
668 | } |
669 | |
670 | /* Free string memory */ |
671 | SB_Done (&Buf); |
672 | } |
673 | |
674 | |
675 | |
676 | void NextTok (void) |
677 | /* Get next token and handle token level functions */ |
678 | { |
679 | /* Get the next raw token */ |
680 | NextRawTok (); |
681 | |
682 | /* In raw mode, or when output is suppressed via conditional assembly, |
683 | ** pass the token unchanged. |
684 | */ |
685 | if (RawMode == 0 && IfCond) { |
686 | |
687 | /* Execute token handling functions */ |
688 | switch (CurTok.Tok) { |
689 | |
690 | case TOK_CONCAT: |
691 | FuncConcat (); |
692 | break; |
693 | |
694 | case TOK_LEFT: |
695 | FuncLeft (); |
696 | break; |
697 | |
698 | case TOK_MAKEIDENT: |
699 | FuncIdent (); |
700 | break; |
701 | |
702 | case TOK_MID: |
703 | FuncMid (); |
704 | break; |
705 | |
706 | case TOK_RIGHT: |
707 | FuncRight (); |
708 | break; |
709 | |
710 | case TOK_SPRINTF: |
711 | FuncSPrintF (); |
712 | break; |
713 | |
714 | case TOK_STRING: |
715 | FuncString (); |
716 | break; |
717 | |
718 | default: |
719 | /* Quiet down gcc */ |
720 | break; |
721 | |
722 | } |
723 | } |
724 | } |
725 | |
726 | |
727 | |
728 | void Consume (token_t Expected, const char* ErrMsg) |
729 | /* Consume Expected, print an error if we don't find it */ |
730 | { |
731 | if (CurTok.Tok == Expected) { |
732 | NextTok (); |
733 | } else { |
734 | Error ("%s" , ErrMsg); |
735 | } |
736 | } |
737 | |
738 | |
739 | |
740 | void ConsumeSep (void) |
741 | /* Consume a separator token */ |
742 | { |
743 | /* We expect a separator token */ |
744 | ExpectSep (); |
745 | |
746 | /* If we are at end of line, skip it */ |
747 | if (CurTok.Tok == TOK_SEP) { |
748 | NextTok (); |
749 | } |
750 | } |
751 | |
752 | |
753 | |
754 | void ConsumeLParen (void) |
755 | /* Consume a left paren */ |
756 | { |
757 | Consume (TOK_LPAREN, "'(' expected" ); |
758 | } |
759 | |
760 | |
761 | |
762 | void ConsumeRParen (void) |
763 | /* Consume a right paren */ |
764 | { |
765 | Consume (TOK_RPAREN, "')' expected" ); |
766 | } |
767 | |
768 | |
769 | |
770 | void ConsumeComma (void) |
771 | /* Consume a comma */ |
772 | { |
773 | Consume (TOK_COMMA, "',' expected" ); |
774 | } |
775 | |
776 | |
777 | |
778 | void SkipUntilSep (void) |
779 | /* Skip tokens until we reach a line separator or end of file */ |
780 | { |
781 | while (!TokIsSep (CurTok.Tok)) { |
782 | NextTok (); |
783 | } |
784 | } |
785 | |
786 | |
787 | |
788 | void ExpectSep (void) |
789 | /* Check if we've reached a line separator, and output an error if not. Do |
790 | ** not skip the line separator. |
791 | */ |
792 | { |
793 | if (!TokIsSep (CurTok.Tok)) { |
794 | ErrorSkip ("Unexpected trailing garbage characters" ); |
795 | } |
796 | } |
797 | |
798 | |
799 | |
800 | void EnterRawTokenMode (void) |
801 | /* Enter raw token mode. In raw mode, token handling functions are not |
802 | ** executed, but the function tokens are passed untouched to the upper |
803 | ** layer. Raw token mode is used when storing macro tokens for later |
804 | ** use. |
805 | ** Calls to EnterRawTokenMode and LeaveRawTokenMode may be nested. |
806 | */ |
807 | { |
808 | ++RawMode; |
809 | } |
810 | |
811 | |
812 | |
813 | void LeaveRawTokenMode (void) |
814 | /* Leave raw token mode. */ |
815 | { |
816 | PRECONDITION (RawMode > 0); |
817 | --RawMode; |
818 | } |
819 | |