static void GetInputChar (void) /* Read the next character from the input stream and make CurC and NextC * valid. If end of line is reached, both are set to NUL, no more lines * are read by this function. */ { /* Drop all pushed fragments that don't have data left */ while (SB_GetIndex (Line) >= SB_GetLen (Line)) { /* Cannot read more from this line, check next line on stack if any */ if (CollCount (&InputStack) == 0) { /* This is THE line */ break; } FreeStrBuf (Line); Line = CollPop (&InputStack); } /* Now get the next characters from the line */ if (SB_GetIndex (Line) >= SB_GetLen (Line)) { CurC = NextC = '\0'; } else { CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { /* NextC comes from this fragment */ NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); } else { /* NextC comes from next fragment */ if (CollCount (&InputStack) > 0) { NextC = ' '; } else { NextC = '\0'; } } } }
void GetLiteralStrBuf (StrBuf* Target, unsigned Offs) /* Copy the string starting at Offs and lasting to the end of the buffer * into Target. */ { CHECK (Offs <= SB_GetLen (&LiteralPool)); SB_Slice (Target, &LiteralPool, Offs, SB_GetLen (&LiteralPool) - Offs); }
static int Sweet16Reg (const StrBuf* Id) /* Check if the given identifier is a sweet16 register. Return -1 if this is ** not the case, return the register number otherwise. */ { unsigned RegNum; char Check; if (SB_GetLen (Id) < 2) { return -1; } if (toupper (SB_AtUnchecked (Id, 0)) != 'R') { return -1; } if (!IsDigit (SB_AtUnchecked (Id, 1))) { return -1; } if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) { /* Invalid register */ return -1; } /* The register number is valid */ return (int) RegNum; }
void TgtTranslateStrBuf (StrBuf* Buf) /* Translate a string buffer from the source character set into the target * system character set. */ { TgtTranslateBuf (SB_GetBuf (Buf), SB_GetLen (Buf)); }
void Fatal (const char* Format, ...) /* Print a message about a fatal error and die */ { va_list ap; const char* FileName; unsigned LineNum; if (CurTok.LI) { FileName = GetInputName (CurTok.LI); LineNum = GetInputLine (CurTok.LI); } else { FileName = GetCurrentFile (); LineNum = GetCurrentLine (); } fprintf (stderr, "%s(%u): Fatal: ", FileName, LineNum); va_start (ap, Format); vfprintf (stderr, Format, ap); va_end (ap); fprintf (stderr, "\n"); if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } exit (EXIT_FAILURE); }
void InitListingLine (void) /* Initialize the current listing line */ { if (SB_GetLen (&ListingName) > 0) { /* Make the last loaded line the current line */ /* ###### This code is a hack! We really need to do it right -- ** as soon as we know how. :-( */ if (LineCur && LineCur->Next && LineCur->Next != LineLast) { ListLine* L = LineCur; do { L = L->Next; /* Set the values for this line */ CHECK (L != 0); L->PC = GetPC (); L->Reloc = GetRelocMode (); L->Output = (ListingEnabled > 0); L->ListBytes = (unsigned char) ListBytes; } while (L->Next != LineLast); } LineCur = LineLast; /* Set the values for this line */ CHECK (LineCur != 0); LineCur->PC = GetPC (); LineCur->Reloc = GetRelocMode (); LineCur->Output = (ListingEnabled > 0); LineCur->ListBytes = (unsigned char) ListBytes; } }
void TranslateLiteralPool (unsigned Offs) /* Translate the literals starting from the given offset into the target * charset. */ { TgtTranslateBuf (SB_GetBuf (&LiteralPool) + Offs, SB_GetLen (&LiteralPool) - Offs); }
void Internal (const char* Format, ...) /* Print a message about an internal compiler error and die. */ { va_list ap; const char* FileName; unsigned LineNum; if (CurTok.LI) { FileName = GetInputName (CurTok.LI); LineNum = GetInputLine (CurTok.LI); } else { FileName = GetCurrentFile (); LineNum = GetCurrentLine (); } fprintf (stderr, "%s(%u): Internal compiler error:\n", FileName, LineNum); va_start (ap, Format); vfprintf (stderr, Format, ap); va_end (ap); fprintf (stderr, "\n"); if (Line) { fprintf (stderr, "\nInput: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } /* Use abort to create a core dump */ abort (); }
static void WriteDep (FILE* F, FileType Types) /* Helper function. Writes all file names that match Types to the output */ { unsigned I; /* Loop over all files */ for (I = 0; I < CollCount (&FileTab); ++I) { const StrBuf* Filename; /* Get the next input file */ const FileEntry* E = (const FileEntry*) CollAt (&FileTab, I); /* Ignore it if it is not of the correct type */ if ((E->Type & Types) == 0) { continue; } /* If this is not the first file, add a space */ if (I > 0) { fputc (' ', F); } /* Print the dependency */ Filename = GetStrBuf (E->Name); fprintf (F, "%*s", SB_GetLen (Filename), SB_GetConstBuf (Filename)); } }
void ResetLiteralPoolOffs (unsigned Offs) /* Reset the offset into the literal pool to some earlier value, effectively * removing values from the pool. */ { CHECK (Offs <= SB_GetLen (&LiteralPool)); SB_Cut (&LiteralPool, Offs); }
int IsAnonName (const StrBuf* Name) /* Check if the given symbol name is that of an anonymous symbol */ { if (SB_GetLen (Name) < sizeof (AnonTag) - 1) { /* Too short */ return 0; } return (strncmp (SB_GetConstBuf (Name), AnonTag, sizeof (AnonTag) - 1) == 0); }
void NewListingLine (const StrBuf* Line, unsigned char File, unsigned char Depth) /* Create a new ListLine struct and insert it */ { /* Store only if listing is enabled */ if (SB_GetLen (&ListingName) > 0) { ListLine* L; /* Get the length of the line */ unsigned Len = SB_GetLen (Line); /* Ignore trailing newlines */ while (Len > 0 && SB_AtUnchecked (Line, Len-1) == '\n') { --Len; } /* Allocate memory */ L = xmalloc (sizeof (ListLine) + Len); /* Initialize the fields. */ L->Next = 0; L->FragList = 0; L->FragLast = 0; L->PC = GetPC (); L->Reloc = GetRelocMode (); L->File = File; L->Depth = Depth; L->Output = (ListingEnabled > 0); L->ListBytes = (unsigned char) ListBytes; memcpy (L->Line, SB_GetConstBuf (Line), Len); L->Line[Len] = '\0'; /* Insert the line into the list of lines */ if (LineList == 0) { LineList = L; } else { LineLast->Next = L; } LineLast = L; } }
void EnableListing (void) /* Enable output of lines to the listing */ { if (SB_GetLen (&ListingName) > 0) { /* If we're about to enable the listing, do this for the current line ** also, so we will see the source line that did this. */ if (ListingEnabled++ == 0) { LineCur->Output = 1; } } }
void DisableListing (void) /* Disable output of lines to the listing */ { if (SB_GetLen (&ListingName) > 0) { if (ListingEnabled == 0) { /* Cannot switch the listing off once more */ Error ("Counter underflow"); } else { --ListingEnabled; } } }
static int HasStr (StrBuf* B, const char* E) /* Checks if E follows in B. If so, skips it and returns true */ { unsigned Len = strlen (E); if (SB_GetLen (B) - SB_GetIndex (B) >= Len) { if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) { /* Found */ SB_SkipMultiple (B, Len); return 1; } } return 0; }
unsigned AddLiteral (const char* S) /* Add a literal string to the literal pool. Return the starting offset into * the pool */ { /* Remember the starting offset */ unsigned Start = SB_GetLen (&LiteralPool); /* Copy the string including the terminator growing the buffer if needed */ SB_AppendBuf (&LiteralPool, S, strlen (S) + 1); /* Return the starting offset */ return Start; }
static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print warning message - internal function. */ { if (!IS_Get (&WarnDisable)) { fprintf (stderr, "%s(%u): Warning: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } ++WarningCount; } }
void DumpLiteralPool (void) /* Dump the literal pool */ { /* If nothing there, exit... */ if (SB_GetLen (&LiteralPool) == 0) { return; } /* Switch to the data segment */ if (IS_Get (&WritableStrings)) { g_usedata (); } else { g_userodata (); } /* Define the label */ g_defdatalabel (LiteralPoolLabel); /* Translate the buffer contents into the target charset */ TranslateLiteralPool (0); /* Output the buffer data */ g_defbytes (SB_GetConstBuf (&LiteralPool), SB_GetLen (&LiteralPool)); }
static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print an error message - internal function*/ { fprintf (stderr, "%s(%u): Error: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } ++ErrorCount; if (ErrorCount > 10) { Fatal ("Too many errors"); } }
static void OutputLiteral (Literal* L) /* Output one literal to the currently active data segment */ { /* Translate the literal into the target charset */ TranslateLiteral (L); /* Define the label for the literal */ g_defdatalabel (L->Label); /* Output the literal data */ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)); /* Mark the literal as output */ L->Output = 1; }
static void PrintPageHeader (FILE* F, const ListLine* L) /* Print the header for a new page. It is assumed that the given line is the ** last line of the previous page. */ { /* Gte a pointer to the current input file */ const StrBuf* CurFile = GetFileName (L->File); /* Print the header on the new page */ fprintf (F, "ca65 V%s\n" "Main file : %s\n" "Current file: %.*s\n" "\n", GetVersionAsString (), InFile, (int) SB_GetLen (CurFile), SB_GetConstBuf (CurFile)); /* Count pages, reset lines */ ++PageNumber; PageLines = 4; }
static int ValidateType (StrBuf* Type) /* Check if the given type is valid and if so, return a string id for it. If ** the type isn't valid, return -1. Type is overwritten when checking. */ { unsigned I; const char* A; char* B; /* The length must not be zero and divideable by two */ unsigned Length = SB_GetLen (Type); if (Length < 2 || (Length & 0x01) != 0) { ErrorSkip ("Type value has invalid length"); return -1; } /* The string must consist completely of hex digit chars */ A = SB_GetConstBuf (Type); for (I = 0; I < Length; ++I) { if (!IsXDigit (A[I])) { ErrorSkip ("Type value contains invalid characters"); return -1; } } /* Convert the type to binary */ B = SB_GetBuf (Type); while (A < SB_GetConstBuf (Type) + Length) { /* Since we know, there are only hex digits, there can't be any errors */ *B++ = (HexValue (A[0]) << 4) | HexValue (A[1]); A += 2; } Type->Len = (Length /= 2); /* Allocate the type and return it */ return GetStrBufId (Type); }
void EmitStrBuf (const StrBuf* Data) /* Emit a string into the current segment */ { /* Use EmitData to output the data */ EmitData (SB_GetConstBuf (Data), SB_GetLen (Data)); }
static void IFNextChar (CharSource* S) /* Read the next character from the input file */ { /* Check for end of line, read the next line if needed */ while (SB_GetIndex (&S->V.File.Line) >= SB_GetLen (&S->V.File.Line)) { unsigned Len; /* End of current line reached, read next line */ SB_Clear (&S->V.File.Line); while (1) { int N = fgetc (S->V.File.F); if (N == EOF) { /* End of file. Accept files without a newline at the end */ if (SB_NotEmpty (&S->V.File.Line)) { break; } /* No more data - add an empty line to the listing. This ** is a small hack needed to keep the PC output in sync. */ NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount); C = EOF; return; /* Check for end of line */ } else if (N == '\n') { /* End of line */ break; /* Collect other stuff */ } else { /* Append data to line */ SB_AppendChar (&S->V.File.Line, N); } } /* If we come here, we have a new input line. To avoid problems ** with strange line terminators, remove all whitespace from the ** end of the line, then add a single newline. */ Len = SB_GetLen (&S->V.File.Line); while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) { --Len; } SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len); SB_AppendChar (&S->V.File.Line, '\n'); /* Terminate the string buffer */ SB_Terminate (&S->V.File.Line); /* One more line */ S->V.File.Pos.Line++; /* Remember the new line for the listing */ NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount); } /* Set the column pointer */ S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line); /* Return the next character from the buffer */ C = SB_Get (&S->V.File.Line); }
void NextRawTok (void) /* Read the next raw token from the input stream */ { Macro* M; /* If we've a forced end of assembly, don't read further */ if (ForcedEnd) { CurTok.Tok = TOK_EOF; return; } Restart: /* Check if we have tokens from another input source */ if (InputFromStack ()) { if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } return; } Again: /* Skip whitespace, remember if we had some */ if ((CurTok.WS = IsBlank (C)) != 0) { do { NextChar (); } while (IsBlank (C)); } /* Mark the file position of the next token */ Source->Func->MarkStart (Source); /* Clear the string attribute */ SB_Clear (&CurTok.SVal); /* Generate line info for the current token */ NewAsmLine (); /* Hex number or PC symbol? */ if (C == '$') { NextChar (); /* Hex digit must follow or DollarIsPC must be enabled */ if (!IsXDigit (C)) { if (DollarIsPC) { CurTok.Tok = TOK_PC; return; } else { Error ("Hexadecimal digit expected"); } } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { if (CurTok.IVal & 0xF0000000) { Error ("Overflow in hexadecimal number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Binary number? */ if (C == '%') { NextChar (); /* 0 or 1 must follow */ if (!IsBDigit (C)) { Error ("Binary digit expected"); } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsBDigit (C)) { Error ("Number may not end with underline"); } } if (IsBDigit (C)) { if (CurTok.IVal & 0x80000000) { Error ("Overflow in binary number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Number? */ if (IsDigit (C)) { char Buf[16]; unsigned Digits; unsigned Base; unsigned I; long Max; unsigned DVal; /* Ignore leading zeros */ while (C == '0') { NextChar (); } /* Read the number into Buf counting the digits */ Digits = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { /* Buf is big enough to allow any decimal and hex number to ** overflow, so ignore excess digits here, they will be detected ** when we convert the value. */ if (Digits < sizeof (Buf)) { Buf[Digits++] = C; } NextChar (); } else { break; } } /* Allow zilog/intel style hex numbers with a 'h' suffix */ if (C == 'h' || C == 'H') { NextChar (); Base = 16; Max = 0xFFFFFFFFUL / 16; } else { Base = 10; Max = 0xFFFFFFFFUL / 10; } /* Convert the number using the given base */ CurTok.IVal = 0; for (I = 0; I < Digits; ++I) { if (CurTok.IVal > Max) { Error ("Number out of range"); CurTok.IVal = 0; break; } DVal = DigitVal (Buf[I]); if (DVal >= Base) { Error ("Invalid digits in number"); CurTok.IVal = 0; break; } CurTok.IVal = (CurTok.IVal * Base) + DVal; } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Control command? */ if (C == '.') { /* Remember and skip the dot */ NextChar (); /* Check if it's just a dot */ if (!IsIdStart (C)) { /* Just a dot */ CurTok.Tok = TOK_DOT; } else { /* Read the remainder of the identifier */ SB_AppendChar (&CurTok.SVal, '.'); ReadIdent (); /* Dot keyword, search for it */ CurTok.Tok = FindDotKeyword (); if (CurTok.Tok == TOK_NONE) { /* Not found */ if (!LeadingDotInIdents) { /* Invalid pseudo instruction */ Error ("'%m%p' is not a recognized control command", &CurTok.SVal); goto Again; } /* An identifier with a dot. Check if it's a define style ** macro. */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } /* Just an identifier with a dot */ CurTok.Tok = TOK_IDENT; } } return; } /* Indirect op for sweet16 cpu. Must check this before checking for local ** symbols, because these may also use the '@' symbol. */ if (CPU == CPU_SWEET16 && C == '@') { NextChar (); CurTok.Tok = TOK_AT; return; } /* Local symbol? */ if (C == LocalStart) { /* Read the identifier. */ ReadIdent (); /* Start character alone is not enough */ if (SB_GetLen (&CurTok.SVal) == 1) { Error ("Invalid cheap local symbol"); goto Again; } /* A local identifier */ CurTok.Tok = TOK_LOCAL_IDENT; return; } /* Identifier or keyword? */ if (IsIdStart (C)) { /* Read the identifier */ ReadIdent (); /* Check for special names. Bail out if we have identified the type of ** the token. Go on if the token is an identifier. */ switch (SB_GetLen (&CurTok.SVal)) { case 1: switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) { case 'A': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ABS; } else { CurTok.Tok = TOK_A; } return; case 'F': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_FAR; return; } break; case 'S': if ((CPU == CPU_4510) || (CPU == CPU_65816)) { CurTok.Tok = TOK_S; return; } break; case 'X': CurTok.Tok = TOK_X; return; case 'Y': CurTok.Tok = TOK_Y; return; case 'Z': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ZP; return; } else { if (CPU == CPU_4510) { CurTok.Tok = TOK_Z; return; } } break; default: break; } break; case 2: if ((CPU == CPU_4510) && (toupper (SB_AtUnchecked (&CurTok.SVal, 0)) == 'S') && (toupper (SB_AtUnchecked (&CurTok.SVal, 1)) == 'P')) { CurTok.Tok = TOK_S; return; } /* FALL THROUGH */ default: if (CPU == CPU_SWEET16 && (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) { /* A sweet16 register number in sweet16 mode */ CurTok.Tok = TOK_REG; return; } } /* Check for define style macro */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* Macro - expand it */ MacExpandStart (M); goto Restart; } else { /* An identifier */ CurTok.Tok = TOK_IDENT; } return; } /* Ok, let's do the switch */ CharAgain: switch (C) { case '+': NextChar (); CurTok.Tok = TOK_PLUS; return; case '-': NextChar (); CurTok.Tok = TOK_MINUS; return; case '/': NextChar (); if (C != '*') { CurTok.Tok = TOK_DIV; } else if (CComments) { /* Remember the position, then skip the '*' */ Collection LineInfos = STATIC_COLLECTION_INITIALIZER; GetFullLineInfo (&LineInfos); NextChar (); do { while (C != '*') { if (C == EOF) { LIError (&LineInfos, "Unterminated comment"); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto CharAgain; } NextChar (); } NextChar (); } while (C != '/'); NextChar (); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto Again; } return; case '*': NextChar (); CurTok.Tok = TOK_MUL; return; case '^': NextChar (); CurTok.Tok = TOK_XOR; return; case '&': NextChar (); if (C == '&') { NextChar (); CurTok.Tok = TOK_BOOLAND; } else { CurTok.Tok = TOK_AND; } return; case '|': NextChar (); if (C == '|') { NextChar (); CurTok.Tok = TOK_BOOLOR; } else { CurTok.Tok = TOK_OR; } return; case ':': NextChar (); switch (C) { case ':': NextChar (); CurTok.Tok = TOK_NAMESPACE; break; case '-': CurTok.IVal = 0; do { --CurTok.IVal; NextChar (); } while (C == '-'); CurTok.Tok = TOK_ULABEL; break; case '+': CurTok.IVal = 0; do { ++CurTok.IVal; NextChar (); } while (C == '+'); CurTok.Tok = TOK_ULABEL; break; case '=': NextChar (); CurTok.Tok = TOK_ASSIGN; break; default: CurTok.Tok = TOK_COLON; break; } return; case ',': NextChar (); CurTok.Tok = TOK_COMMA; return; case ';': NextChar (); while (C != '\n' && C != EOF) { NextChar (); } goto CharAgain; case '#': NextChar (); CurTok.Tok = TOK_HASH; return; case '(': NextChar (); CurTok.Tok = TOK_LPAREN; return; case ')': NextChar (); CurTok.Tok = TOK_RPAREN; return; case '[': NextChar (); CurTok.Tok = TOK_LBRACK; return; case ']': NextChar (); CurTok.Tok = TOK_RBRACK; return; case '{': NextChar (); CurTok.Tok = TOK_LCURLY; return; case '}': NextChar (); CurTok.Tok = TOK_RCURLY; return; case '<': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_LE; } else if (C == '<') { NextChar (); CurTok.Tok = TOK_SHL; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_NE; } else { CurTok.Tok = TOK_LT; } return; case '=': NextChar (); CurTok.Tok = TOK_EQ; return; case '!': NextChar (); CurTok.Tok = TOK_BOOLNOT; return; case '>': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_GE; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_SHR; } else { CurTok.Tok = TOK_GT; } return; case '~': NextChar (); CurTok.Tok = TOK_NOT; return; case '\'': /* Hack: If we allow ' as terminating character for strings, read ** the following stuff as a string, and check for a one character ** string later. */ if (LooseStringTerm) { ReadStringConst ('\''); if (SB_GetLen (&CurTok.SVal) == 1) { CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); CurTok.Tok = TOK_CHARCON; } else { CurTok.Tok = TOK_STRCON; } } else { /* Always a character constant */ NextChar (); if (C == EOF || IsControl (C)) { Error ("Illegal character constant"); goto CharAgain; } CurTok.IVal = C; CurTok.Tok = TOK_CHARCON; NextChar (); if (C != '\'') { if (!MissingCharTerm) { Error ("Illegal character constant"); } } else { NextChar (); } } return; case '\"': ReadStringConst ('\"'); CurTok.Tok = TOK_STRCON; return; case '\\': /* Line continuation? */ if (LineCont) { NextChar (); /* Next char should be a LF, if not, will result in an error later */ if (C == '\n') { /* Ignore the '\n' */ NextChar (); goto Again; } else { /* Make it clear what the problem is: */ Error ("EOL expected."); } } break; case '\n': NextChar (); CurTok.Tok = TOK_SEP; return; case EOF: CheckInputStack (); /* In case of the main file, do not close it, but return EOF. */ if (Source && Source->Next) { DoneCharSource (); goto Again; } else { CurTok.Tok = TOK_EOF; } return; } /* If we go here, we could not identify the current character. Skip it ** and try again. */ Error ("Invalid input character: 0x%02X", C & 0xFF); NextChar (); goto Again; }
unsigned GetLiteralSize (const Literal* L) /* Get the size of a literal string */ { return SB_GetLen (&L->Data); }
void TranslateLiteral (Literal* L) /* Translate a literal into the target charset. */ { TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data)); }
static void FuncIdent (void) /* Handle the .IDENT function */ { StrBuf Buf = STATIC_STRBUF_INITIALIZER; token_t Id; unsigned I; /* Skip it */ NextTok (); /* Left paren expected */ ConsumeLParen (); /* The function expects a string argument */ if (!LookAtStrCon ()) { return; } /* Check that the string contains a valid identifier. While doing so, * determine if it is a cheap local, or global one. */ SB_Reset (&CurTok.SVal); /* Check for a cheap local symbol */ if (SB_Peek (&CurTok.SVal) == LocalStart) { SB_Skip (&CurTok.SVal); Id = TOK_LOCAL_IDENT; } else { Id = TOK_IDENT; } /* Next character must be a valid identifier start */ if (!IsIdStart (SB_Get (&CurTok.SVal))) { NoIdent (); return; } for (I = SB_GetIndex (&CurTok.SVal); I < SB_GetLen (&CurTok.SVal); ++I) { if (!IsIdChar (SB_AtUnchecked (&CurTok.SVal, I))) { NoIdent (); return; } } if (IgnoreCase) { UpcaseSVal (); } /* If anything is ok, save and skip the string. Check that the next token * is a right paren, then replace the token by an identifier token. */ SB_Copy (&Buf, &CurTok.SVal); NextTok (); if (CurTok.Tok != TOK_RPAREN) { Error ("`)' expected"); } else { CurTok.Tok = Id; SB_Copy (&CurTok.SVal, &Buf); SB_Terminate (&CurTok.SVal); } /* Free buffer memory */ SB_Done (&Buf); }
static void DefineMacro (void) /* Handle a macro definition. */ { ident Ident; Macro* M; Macro* Existing; int C89; /* Read the macro name */ SkipWhitespace (0); if (!MacName (Ident)) { return; } /* Remember if we're in C89 mode */ C89 = (IS_Get (&Standard) == STD_C89); /* Get an existing macro definition with this name */ Existing = FindMacro (Ident); /* Create a new macro definition */ M = NewMacro (Ident); /* Check if this is a function like macro */ if (CurC == '(') { /* Skip the left paren */ NextChar (); /* Set the marker that this is a function like macro */ M->ArgCount = 0; /* Read the formal parameter list */ while (1) { /* Skip white space and check for end of parameter list */ SkipWhitespace (0); if (CurC == ')') { break; } /* The next token must be either an identifier, or - if not in * C89 mode - the ellipsis. */ if (!C89 && CurC == '.') { /* Ellipsis */ NextChar (); if (CurC != '.' || NextC != '.') { PPError ("`...' expected"); ClearLine (); return; } NextChar (); NextChar (); /* Remember that the macro is variadic and use __VA_ARGS__ as * the argument name. */ AddMacroArg (M, "__VA_ARGS__"); M->Variadic = 1; } else { /* Must be macro argument name */ if (MacName (Ident) == 0) { return; } /* __VA_ARGS__ is only allowed in C89 mode */ if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) { PPWarning ("`__VA_ARGS__' can only appear in the expansion " "of a C99 variadic macro"); } /* Add the macro argument */ AddMacroArg (M, Ident); } /* If we had an ellipsis, or the next char is not a comma, we've * reached the end of the macro argument list. */ SkipWhitespace (0); if (M->Variadic || CurC != ',') { break; } NextChar (); } /* Check for a right paren and eat it if we find one */ if (CurC != ')') { PPError ("`)' expected"); ClearLine (); return; } NextChar (); } /* Skip whitespace before the macro replacement */ SkipWhitespace (0); /* Insert the macro into the macro table and allocate the ActualArgs array */ InsertMacro (M); /* Remove whitespace and comments from the line, store the preprocessed * line into the macro replacement buffer. */ Pass1 (Line, &M->Replacement); /* Remove whitespace from the end of the line */ while (IsSpace (SB_LookAtLast (&M->Replacement))) { SB_Drop (&M->Replacement, 1); } #if 0 printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); #endif /* If we have an existing macro, check if the redefinition is identical. * Print a diagnostic if not. */ if (Existing && MacroCmp (M, Existing) != 0) { PPError ("Macro redefinition is not identical"); } }
void Preprocess (void) /* Preprocess a line */ { int Skip; ident Directive; /* Create the output buffer if we don't already have one */ if (MLine == 0) { MLine = NewStrBuf (); } /* Skip white space at the beginning of the line */ SkipWhitespace (0); /* Check for stuff to skip */ Skip = 0; while (CurC == '\0' || CurC == '#' || Skip) { /* Check for preprocessor lines lines */ if (CurC == '#') { NextChar (); SkipWhitespace (0); if (CurC == '\0') { /* Ignore the empty preprocessor directive */ continue; } if (!IsSym (Directive)) { PPError ("Preprocessor directive expected"); ClearLine (); } else { switch (FindPPToken (Directive)) { case PP_DEFINE: if (!Skip) { DefineMacro (); } break; case PP_ELIF: if (IfIndex >= 0) { if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { /* Handle as #else/#if combination */ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { Skip = !Skip; } IfStack[IfIndex] |= IFCOND_ELSE; Skip = DoIf (Skip); /* #elif doesn't need a terminator */ IfStack[IfIndex] &= ~IFCOND_NEEDTERM; } else { PPError ("Duplicate #else/#elif"); } } else { PPError ("Unexpected #elif"); } break; case PP_ELSE: if (IfIndex >= 0) { if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { Skip = !Skip; } IfStack[IfIndex] |= IFCOND_ELSE; } else { PPError ("Duplicate #else"); } } else { PPError ("Unexpected `#else'"); } break; case PP_ENDIF: if (IfIndex >= 0) { /* Remove any clauses on top of stack that do not * need a terminating #endif. */ while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { --IfIndex; } /* Stack may not be empty here or something is wrong */ CHECK (IfIndex >= 0); /* Remove the clause that needs a terminator */ Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0; } else { PPError ("Unexpected `#endif'"); } break; case PP_ERROR: if (!Skip) { DoError (); } break; case PP_IF: Skip = DoIf (Skip); break; case PP_IFDEF: Skip = DoIfDef (Skip, 1); break; case PP_IFNDEF: Skip = DoIfDef (Skip, 0); break; case PP_INCLUDE: if (!Skip) { DoInclude (); } break; case PP_LINE: /* Should do something in C99 at least, but we ignore it */ if (!Skip) { ClearLine (); } break; case PP_PRAGMA: if (!Skip) { DoPragma (); goto Done; } break; case PP_UNDEF: if (!Skip) { DoUndef (); } break; case PP_WARNING: /* #warning is a non standard extension */ if (IS_Get (&Standard) > STD_C99) { if (!Skip) { DoWarning (); } } else { if (!Skip) { PPError ("Preprocessor directive expected"); } ClearLine (); } break; default: if (!Skip) { PPError ("Preprocessor directive expected"); } ClearLine (); } } } if (NextLine () == 0) { if (IfIndex >= 0) { PPError ("`#endif' expected"); } return; } SkipWhitespace (0); } PreprocessLine (); Done: if (Verbosity > 1 && SB_NotEmpty (Line)) { printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } }