1 /*****************************************************************************/
5 /* Get next token and handle token level functions */
9 /* (C) 2000-2004 Ullrich von Bassewitz */
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 /*****************************************************************************/
54 /*****************************************************************************/
56 /*****************************************************************************/
60 static unsigned RawMode = 0; /* Raw token mode flag/counter */
64 /*****************************************************************************/
66 /*****************************************************************************/
70 static int LookAtStrCon (void)
71 /* Make sure the next token is a string constant. If not, print an error
72 * messages skip the remainder of the line and return false. Otherwise return
76 if (Tok != TOK_STRCON) {
77 Error ("String constant expected");
87 /*****************************************************************************/
89 /*****************************************************************************/
93 static TokList* CollectTokens (unsigned Start, unsigned Count)
94 /* Read a list of tokens that is optionally enclosed in curly braces and
95 * terminated by a right paren. For all tokens starting at the one with index
96 * Start, and ending at (Start+Count-1), place them into a token list, and
97 * return this token list.
101 /* Create the token list */
102 TokList* List = NewTokList ();
104 /* Determine if the list is enclosed in curly braces. */
105 enum Token Term = GetTokListTerm (TOK_RPAREN);
107 /* Read the token list */
108 unsigned Current = 0;
109 while (Tok != Term) {
111 /* Check for end of line or end of input */
112 if (TokIsSep (Tok)) {
113 Error ("Unexpected end of line");
117 /* Collect tokens in the given range */
118 if (Current >= Start && Current < Start+Count) {
119 /* Add the current token to the list */
123 /* Get the next token */
128 /* Eat the terminator token */
131 /* If the list was enclosed in curly braces, we do expect now a right paren */
132 if (Term == TOK_RCURLY) {
136 /* Return the list of collected tokens */
142 static void FuncConcat (void)
143 /* Handle the .CONCAT function */
145 char Buf[MAX_STR_LEN+1];
153 /* Left paren expected */
156 /* Concatenate any number of strings */
162 /* Next token must be a string */
163 if (!LookAtStrCon ()) {
167 /* Get the length of the string const and check total length */
169 if (Length + L > MAX_STR_LEN) {
170 Error ("String is too long");
176 /* Add the new string */
181 /* Skip the string token */
184 /* Comma means another argument */
185 if (Tok == TOK_COMMA) {
193 /* Terminate the string */
196 /* We expect a closing parenthesis, but will not skip it but replace it
197 * by the string token just created.
199 if (Tok != TOK_RPAREN) {
200 Error ("`)' expected");
209 static void FuncLeft (void)
210 /* Handle the .LEFT function */
218 /* Left paren expected */
222 Count = ConstExpression ();
223 if (Count < 0 || Count > 100) {
224 Error ("Range error");
229 /* Read the token list */
230 List = CollectTokens (0, (unsigned) Count);
232 /* Since we want to insert the list before the now current token, we have
233 * to save the current token in some way and then skip it. To do this, we
234 * will add the current token at the end of the token list (so the list
235 * will never be empty), push the token list, and then skip the current
236 * token. This will replace the current token by the first token from the
237 * list (which will be the old current token in case the list was empty).
241 /* Insert it into the scanner feed */
242 PushTokList (List, ".LEFT");
244 /* Skip the current token */
250 static void NoIdent (void)
251 /* Print an error message and skip the remainder of the line */
253 Error ("Argument of .IDENT is not a valid identifier");
259 static void FuncIdent (void)
260 /* Handle the .IDENT function */
262 char Buf[sizeof(SVal)];
270 /* Left paren expected */
273 /* The function expects a string argument */
274 if (!LookAtStrCon ()) {
278 /* Check that the string contains a valid identifier. While doing so,
279 * determine if it is a cheap local, or global one.
287 if (SVal[0] == LocalStart) {
293 Id = TOK_LOCAL_IDENT;
297 if (!IsIdStart (SVal[I])) {
302 if (!IsIdChar (SVal[I])) {
312 /* If anything is ok, save and skip the string. Check that the next token
313 * is a right paren, in which case SVal is untouched. Replace the token by
314 * a identifier token.
316 memcpy (Buf, SVal, Len+1);
318 if (Tok != TOK_RPAREN) {
319 Error ("`)' expected");
322 memcpy (SVal, Buf, Len+1);
328 static void FuncMid (void)
329 /* Handle the .MID function */
338 /* Left paren expected */
342 Start = ConstExpression ();
343 if (Start < 0 || Start > 100) {
344 Error ("Range error");
350 Count = ConstExpression ();
351 if (Count < 0 || Count > 100) {
352 Error ("Range error");
357 /* Read the token list */
358 List = CollectTokens ((unsigned) Start, (unsigned) Count);
360 /* Since we want to insert the list before the now current token, we have
361 * to save the current token in some way and then skip it. To do this, we
362 * will add the current token at the end of the token list (so the list
363 * will never be empty), push the token list, and then skip the current
364 * token. This will replace the current token by the first token from the
365 * list (which will be the old current token in case the list was empty).
369 /* Insert it into the scanner feed */
370 PushTokList (List, ".MID");
372 /* Skip the current token */
378 static void FuncRight (void)
379 /* Handle the .RIGHT function */
387 /* Left paren expected */
391 Count = ConstExpression ();
392 if (Count < 0 || Count > 100) {
393 Error ("Range error");
398 /* Read the complete token list */
399 List = CollectTokens (0, 9999);
401 /* Delete tokens from the list until Count tokens are remaining */
402 while (List->Count > (unsigned) Count) {
403 /* Get the first node */
404 TokNode* T = List->Root;
406 /* Remove it from the list */
407 List->Root = List->Root->Next;
412 /* Corrent the token counter */
416 /* Since we want to insert the list before the now current token, we have
417 * to save the current token in some way and then skip it. To do this, we
418 * will add the current token at the end of the token list (so the list
419 * will never be empty), push the token list, and then skip the current
420 * token. This will replace the current token by the first token from the
421 * list (which will be the old current token in case the list was empty).
425 /* Insert it into the scanner feed */
426 PushTokList (List, ".RIGHT");
428 /* Skip the current token */
434 static void InvalidFormatString (void)
435 /* Print an error message and skip the remainder of the line */
437 Error ("Invalid format string");
443 static void FuncSPrintF (void)
444 /* Handle the .SPRINTF function */
446 char Format[sizeof (SVal)]; /* User given format */
447 const char* F = Format; /* User format pointer */
448 StrBuf R = AUTO_STRBUF_INITIALIZER; /* Result string */
449 StrBuf F1 = AUTO_STRBUF_INITIALIZER; /* One format spec from F */
450 StrBuf R1 = AUTO_STRBUF_INITIALIZER; /* One result */
452 long IVal; /* Integer value */
456 /* Skip the .SPRINTF token */
459 /* Left paren expected */
462 /* First argument is a format string. Remember and skip it */
463 if (!LookAtStrCon ()) {
466 strcpy (Format, SVal);
469 /* Walk over the format string, generating the function result in R */
472 /* Get the next char from the format string and check for EOS */
477 /* Check for a format specifier */
479 /* No format specifier, just copy */
480 SB_AppendChar (&R, *F++);
485 SB_AppendChar (&R, '%');
490 InvalidFormatString ();
494 /* Since a format specifier follows, we do expect anotehr argument for
495 * the .sprintf function.
499 /* We will copy the format spec into F1 checking for the things we
500 * support, and later use xsprintf to do the actual formatting. This
501 * is easier than adding another printf implementation...
504 SB_AppendChar (&F1, '%');
506 /* Check for flags */
508 while (*F != '\0' && !Done) {
510 case '-': /* FALLTHROUGH */
511 case '+': /* FALLTHROUGH */
512 case ' ': /* FALLTHROUGH */
513 case '#': /* FALLTHROUGH */
514 case '0': SB_AppendChar (&F1, *F++); break;
515 default: Done = 1; break;
519 /* We do only support a numerical width field */
520 while (IsDigit (*F)) {
521 SB_AppendChar (&F1, *F++);
524 /* Precision - only positive numerical fields supported */
526 SB_AppendChar (&F1, *F++);
527 while (IsDigit (*F)) {
528 SB_AppendChar (&F1, *F++);
532 /* Length modifiers aren't supported, so read the conversion specs */
541 /* Our ints are actually longs, so we use the 'l' modifier when
542 * calling xsprintf later. Terminate the format string.
544 SB_AppendChar (&F1, 'l');
545 SB_AppendChar (&F1, *F++);
548 /* The argument must be a constant expression */
549 IVal = ConstExpression ();
551 /* Format this argument according to the spec */
552 SB_Printf (&R1, SB_GetConstBuf (&F1), IVal);
554 /* Append the formatted argument to the result */
560 /* Add the format spec and terminate the format */
561 SB_AppendChar (&F1, *F++);
564 /* The argument must be a string constant */
565 if (!LookAtStrCon ()) {
567 strcpy (SVal, "**undefined**");
570 /* Format this argument according to the spec */
571 SB_Printf (&R1, SB_GetConstBuf (&F1), SVal);
573 /* Skip the string constant */
576 /* Append the formatted argument to the result */
582 /* Add the format spec and terminate the format */
583 SB_AppendChar (&F1, *F++);
586 /* The argument must be a constant expression */
587 IVal = ConstExpression ();
589 /* Check for a valid character range */
590 if (IVal <= 0 || IVal > 255) {
591 Error ("Char argument out of range");
595 /* Format this argument according to the spec. Be sure to pass
596 * an int as the char value.
598 SB_Printf (&R1, SB_GetConstBuf (&F1), (int) IVal);
600 /* Append the formatted argument to the result */
606 Error ("Invalid format string");
608 /* Don't skip beyond end of string */
616 /* The length of the final result may not exceed the size of a string */
617 if (SB_GetLen (&R) >= sizeof (SVal)) {
618 Error ("Resulting string is too long");
619 SB_Cut (&R, sizeof (SVal) - 1);
622 /* Terminate the result string */
625 /* We expect a closing parenthesis, but will not skip it but replace it
626 * by the string token just created.
628 if (Tok != TOK_RPAREN) {
629 Error ("`)' expected");
632 memcpy (SVal, SB_GetConstBuf (&R), SB_GetLen (&R) + 1);
636 /* Delete the string buffers */
644 static void FuncString (void)
645 /* Handle the .STRING function */
647 char Buf[MAX_STR_LEN+1];
652 /* Left paren expected */
655 /* Accept identifiers or numeric expressions */
656 if (Tok == TOK_IDENT || Tok == TOK_LOCAL_IDENT) {
657 /* Save the identifier, then skip it */
661 /* Numeric expression */
662 long Val = ConstExpression ();
663 sprintf (Buf, "%ld", Val);
666 /* We expect a closing parenthesis, but will not skip it but replace it
667 * by the string token just created.
669 if (Tok != TOK_RPAREN) {
670 Error ("`)' expected");
680 /* Get next token and handle token level functions */
682 /* Get the next raw token */
685 /* In raw mode, pass the token unchanged */
688 /* Execute token handling functions */
729 void Consume (enum Token Expected, const char* ErrMsg)
730 /* Consume Expected, print an error if we don't find it */
732 if (Tok == Expected) {
741 void ConsumeSep (void)
742 /* Consume a separator token */
744 /* We expect a separator token */
747 /* If we are at end of line, skip it */
748 if (Tok == TOK_SEP) {
755 void ConsumeLParen (void)
756 /* Consume a left paren */
758 Consume (TOK_LPAREN, "`(' expected");
763 void ConsumeRParen (void)
764 /* Consume a right paren */
766 Consume (TOK_RPAREN, "`)' expected");
771 void ConsumeComma (void)
772 /* Consume a comma */
774 Consume (TOK_COMMA, "`,' expected");
779 void SkipUntilSep (void)
780 /* Skip tokens until we reach a line separator or end of file */
782 while (!TokIsSep (Tok)) {
789 void ExpectSep (void)
790 /* Check if we've reached a line separator, and output an error if not. Do
791 * not skip the line separator.
794 if (!TokIsSep (Tok)) {
795 ErrorSkip ("Unexpected trailing garbage characters");
801 void EnterRawTokenMode (void)
802 /* Enter raw token mode. In raw mode, token handling functions are not
803 * executed, but the function tokens are passed untouched to the upper
804 * layer. Raw token mode is used when storing macro tokens for later
806 * Calls to EnterRawTokenMode and LeaveRawTokenMode may be nested.
814 void LeaveRawTokenMode (void)
815 /* Leave raw token mode. */
817 PRECONDITION (RawMode > 0);