/*****************************************************************************/ /* */ /* scanstrbuf.c */ /* */ /* Small scanner for input from a StrBuf */ /* */ /* */ /* */ /* (C) 2002-2009, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ /* common */ #include "chartype.h" #include "tgttrans.h" /* cc65 */ #include "datatype.h" #include "error.h" #include "hexval.h" #include "ident.h" #include "scanstrbuf.h" /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ static int ParseChar (StrBuf* B) /* Parse a character. Converts \n into EOL, etc. */ { unsigned I; unsigned Val; int C; /* Check for escape chars */ if ((C = SB_Get (B)) == '\\') { switch (SB_Peek (B)) { case '?': C = '?'; SB_Skip (B); break; case 'a': C = '\a'; SB_Skip (B); break; case 'b': C = '\b'; SB_Skip (B); break; case 'f': C = '\f'; SB_Skip (B); break; case 'r': C = '\r'; SB_Skip (B); break; case 'n': C = '\n'; SB_Skip (B); break; case 't': C = '\t'; SB_Skip (B); break; case 'v': C = '\v'; SB_Skip (B); break; case '\"': C = '\"'; SB_Skip (B); break; case '\'': C = '\''; SB_Skip (B); break; case '\\': C = '\\'; SB_Skip (B); break; case 'x': case 'X': /* Hex character constant */ SB_Skip (B); C = HexVal (SB_Get (B)) << 4; C |= HexVal (SB_Get (B)); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* Octal constant */ I = 0; Val = SB_Get (B) - '0'; while (SB_Peek (B) >= '0' && SB_Peek (B) <= '7' && ++I <= 3) { Val = (Val << 3) | (SB_Get (B) - '0'); } C = (int) Val; if (Val > 256) { Error ("Character constant out of range"); C = ' '; } break; default: Error ("Illegal character constant 0x%02X", SB_Get (B)); C = ' '; break; } } /* Return the character */ return C; } /*****************************************************************************/ /* Code */ /*****************************************************************************/ void SB_SkipWhite (StrBuf* B) /* Skip whitespace in the string buffer */ { while (IsBlank (SB_Peek (B))) { SB_Skip (B); } } int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars) /* Get a symbol from the string buffer. If SpecialChars is not NULL, it * points to a string that contains characters allowed within the string in * addition to letters, digits and the underline. Note: The identifier must * still begin with a letter. * Returns 1 if a symbol was found and 0 otherwise but doesn't output any * errors. */ { /* Handle a NULL argument for SpecialChars transparently */ if (SpecialChars == 0) { SpecialChars = ""; } /* Clear Ident */ SB_Clear (Ident); if (IsIdent (SB_Peek (B))) { char C = SB_Peek (B); do { SB_AppendChar (Ident, C); SB_Skip (B); C = SB_Peek (B); } while (IsIdent (C) || IsDigit (C) || (C != '\0' && strchr (SpecialChars, C) != 0)); SB_Terminate (Ident); return 1; } else { return 0; } } int SB_GetString (StrBuf* B, StrBuf* S) /* Get a string from the string buffer. Returns 1 if a string was found and 0 * otherwise. Errors are only output in case of invalid strings (missing end * of string). */ { char C; /* Clear S */ SB_Clear (S); /* A string starts with quote marks */ if (SB_Peek (B) == '\"') { /* String follows, be sure to concatenate strings */ while (SB_Peek (B) == '\"') { /* Skip the quote char */ SB_Skip (B); /* Read the actual string contents */ while ((C = SB_Peek (B)) != '\"') { if (C == '\0') { Error ("Unexpected end of string"); break; } SB_AppendChar (S, ParseChar (B)); } /* Skip the closing quote char if there was one */ SB_Skip (B); /* Skip white space, read new input */ SB_SkipWhite (B); } /* Terminate the string */ SB_Terminate (S); /* Success */ return 1; } else { /* Not a string */ SB_Terminate (S); return 0; } } int SB_GetNumber (StrBuf* B, long* Val) /* Get a number from the string buffer. Accepted formats are decimal, octal, * hex and character constants. Numeric constants may be preceeded by a * minus or plus sign. The function returns 1 if a number was found and * zero otherwise. Errors are only output for invalid numbers. */ { int Sign; char C; unsigned Base; unsigned DigitVal; /* Initialize Val */ *Val = 0; /* Handle character constants */ if (SB_Peek (B) == '\'') { /* Character constant */ SB_Skip (B); *Val = SignExtendChar (TgtTranslateChar (ParseChar (B))); if (SB_Peek (B) != '\'') { Error ("`\'' expected"); return 0; } else { /* Skip the quote */ SB_Skip (B); return 1; } } /* Check for a sign. A sign must be followed by a digit, otherwise it's * not a number */ Sign = 1; switch (SB_Peek (B)) { case '-': Sign = -1; /* FALLTHROUGH */ case '+': if (!IsDigit (SB_LookAt (B, SB_GetIndex (B) + 1))) { return 0; } SB_Skip (B); break; } /* We must have a digit now, otherwise its not a number */ C = SB_Peek (B); if (!IsDigit (C)) { return 0; } /* Determine the base */ if (C == '0') { /* Hex or octal */ SB_Skip (B); if (tolower (SB_Peek (B)) == 'x') { SB_Skip (B); Base = 16; if (!IsXDigit (SB_Peek (B))) { Error ("Invalid hexadecimal number"); return 0; } } else { Base = 8; } } else { Base = 10; } /* Read the number */ while (IsXDigit (C = SB_Peek (B)) && (DigitVal = HexVal (C)) < Base) { *Val = (*Val * Base) + DigitVal; SB_Skip (B); } /* Allow optional 'U' and 'L' modifiers */ C = SB_Peek (B); if (C == 'u' || C == 'U') { SB_Skip (B); C = SB_Peek (B); if (C == 'l' || C == 'L') { SB_Skip (B); } } else if (C == 'l' || C == 'L') { SB_Skip (B); C = SB_Peek (B); if (C == 'u' || C == 'U') { SB_Skip (B); } } /* Success, value read is in Val */ *Val *= Sign; return 1; }