static void CheckSymTable (SymTable* Tab) /* Check a symbol table for open references, unused symbols ... */ { SymEntry* Entry = Tab->SymHead; while (Entry) { /* Get the storage flags for tne entry */ unsigned Flags = Entry->Flags; /* Ignore typedef entries */ if (!SymIsTypeDef (Entry)) { /* Check if the symbol is one with storage, and it if it was ** defined but not used. */ if (((Flags & SC_AUTO) || (Flags & SC_STATIC)) && (Flags & SC_EXTERN) == 0) { if (SymIsDef (Entry) && !SymIsRef (Entry) && !SymHasAttr (Entry, atUnused)) { if (Flags & SC_PARAM) { if (IS_Get (&WarnUnusedParam)) { Warning ("Parameter `%s' is never used", Entry->Name); } } else { if (IS_Get (&WarnUnusedVar)) { Warning ("`%s' is defined but never used", Entry->Name); } } } } /* If the entry is a label, check if it was defined in the function */ if (Flags & SC_LABEL) { if (!SymIsDef (Entry)) { /* Undefined label */ Error ("Undefined label: `%s'", Entry->Name); } else if (!SymIsRef (Entry)) { /* Defined but not used */ if (IS_Get (&WarnUnusedLabel)) { Warning ("`%s' is defined but never used", Entry->Name); } } } } /* Next entry */ Entry = Entry->NextSym; } }
int F_AllocRegVar (Function* F, const Type* Type) /* Allocate a register variable for the given variable type. If the allocation * was successful, return the offset of the register variable in the register * bank (zero page storage). If there is no register space left, return -1. */ { /* Allow register variables only on top level and if enabled */ if (IS_Get (&EnableRegVars) && GetLexicalLevel () == LEX_LEVEL_FUNCTION) { /* Get the size of the variable */ unsigned Size = CheckedSizeOf (Type); /* Do we have space left? */ if (F->RegOffs >= Size) { /* Space left. We allocate the variables from high to low addresses, * so the adressing is compatible with the saved values on stack. * This allows shorter code when saving/restoring the variables. */ F->RegOffs -= Size; return F->RegOffs; } } /* No space left or no allocation */ return -1; }
int SignExtendChar (int C) /* Do correct sign extension of a character */ { if (IS_Get (&SignedChars) && (C & 0x80) != 0) { return C | ~0xFF; } else { return C & 0xFF; } }
Literal* UseLiteral (Literal* L) /* Increase the reference counter for the literal and return it */ { /* Increase the reference count */ ++L->RefCount; /* If --local-strings was given, immediately output the literal */ if (IS_Get (&LocalStrings)) { /* Switch to the proper data segment */ if (IS_Get (&WritableStrings)) { g_usedata (); } else { g_userodata (); } /* Output the literal */ OutputLiteral (L); } /* Return the literal */ return L; }
static token_t FindKey (const char* Key) /* Find a keyword and return the token. Return IDENT if the token is not a ** keyword. */ { struct Keyword* K; K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey); if (K && (K->Std & (0x01 << IS_Get (&Standard))) != 0) { return K->Tok; } else { return TOK_IDENT; } }
CodeSeg* NewCodeSeg (const char* SegName, SymEntry* Func) /* Create a new code segment, initialize and return it */ { unsigned I; const Type* RetType; /* Allocate memory */ CodeSeg* S = xmalloc (sizeof (CodeSeg)); /* Initialize the fields */ S->SegName = xstrdup (SegName); S->Func = Func; InitCollection (&S->Entries); InitCollection (&S->Labels); for (I = 0; I < sizeof(S->LabelHash) / sizeof(S->LabelHash[0]); ++I) { S->LabelHash[I] = 0; } /* If we have a function given, get the return type of the function. * Assume ANY return type besides void will use the A and X registers. */ if (S->Func && !IsTypeVoid ((RetType = GetFuncReturn (Func->Type)))) { if (SizeOf (RetType) == SizeOf (type_long)) { S->ExitRegs = REG_EAX; } else { S->ExitRegs = REG_AX; } } else { S->ExitRegs = REG_NONE; } /* Copy the global optimization settings */ S->Optimize = (unsigned char) IS_Get (&Optimize); S->CodeSizeFactor = (unsigned) IS_Get (&CodeSizeFactor); /* Return the new struct */ return S; }
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; } }
static void CharMapPragma (StrBuf* B) /* Change the character map */ { long Index, C; /* Read the character index */ if (!GetNumber (B, &Index)) { return; } if (Index < 0 || Index > 255) { Error ("Character index out of range"); return; } /* Comma follows */ if (!GetComma (B)) { return; } /* Read the character code */ if (!GetNumber (B, &C)) { return; } if (C < 0 || C > 255) { Error ("Character code out of range"); return; } /* Warn about remapping character code 0x00 ** (except when remapping it back to itself). */ if (Index + C != 0 && IS_Get (&WarnRemapZero)) { if (Index == 0) { Warning ("Remapping from 0 is dangerous with string functions"); } else if (C == 0) { Warning ("Remapping to 0 can make string functions stop unexpectedly"); } } /* Remap the character */ TgtTranslateSet ((unsigned) Index, (unsigned char) C); }
static void LoadConstant (unsigned Flags, ExprDesc* Expr) /* Load the primary register with some constant value. */ { switch (ED_GetLoc (Expr)) { case E_LOC_ABS: /* Number constant */ g_getimmed (Flags | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: /* Global symbol, load address */ g_getimmed ((Flags | CF_EXTERNAL) & ~CF_CONST, Expr->Name, Expr->IVal); break; case E_LOC_STATIC: case E_LOC_LITERAL: /* Static symbol or literal, load address */ g_getimmed ((Flags | CF_STATIC) & ~CF_CONST, Expr->Name, Expr->IVal); break; case E_LOC_REGISTER: /* Register variable. Taking the address is usually not * allowed. */ if (IS_Get (&AllowRegVarAddr) == 0) { Error ("Cannot take the address of a register variable"); } g_getimmed ((Flags | CF_REGVAR) & ~CF_CONST, Expr->Name, Expr->IVal); break; case E_LOC_STACK: g_leasp (Expr->IVal); break; default: Internal ("Unknown constant type: %04X", Expr->Flags); } }
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)); }
void NewFunc (SymEntry* Func) /* Parse argument declarations and function body. */ { int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; /* Get the function descriptor from the function entry */ FuncDesc* D = Func->V.F.Func; /* Allocate the function activation record for the function */ CurrentFunc = NewFunction (Func); /* Reenter the lexical level */ ReenterFunctionLevel (D); /* Check if the function header contains unnamed parameters. These are * only allowed in cc65 mode. */ if ((D->Flags & FD_UNNAMED_PARAMS) != 0 && (IS_Get (&Standard) != STD_CC65)) { Error ("Parameter name omitted"); } /* Declare two special functions symbols: __fixargs__ and __argsize__. * The latter is different depending on the type of the function (variadic * or not). */ AddConstSym ("__fixargs__", type_uint, SC_DEF | SC_CONST, D->ParamSize); if (D->Flags & FD_VARIADIC) { /* Variadic function. The variable must be const. */ static const Type T[] = { TYPE(T_UCHAR | T_QUAL_CONST), TYPE(T_END) }; AddLocalSym ("__argsize__", T, SC_DEF | SC_REF | SC_AUTO, 0); } else { /* Non variadic */ AddConstSym ("__argsize__", type_uchar, SC_DEF | SC_CONST, D->ParamSize); } /* Function body now defined */ Func->Flags |= SC_DEF; /* Special handling for main() */ if (strcmp (Func->Name, "main") == 0) { /* Mark this as the main function */ CurrentFunc->Flags |= FF_IS_MAIN; /* Main cannot be a fastcall function */ if (IsQualFastcall (Func->Type)) { Error ("`main' cannot be declared as __fastcall__"); } /* If cc65 extensions aren't enabled, don't allow a main function that * doesn't return an int. */ if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) { Error ("`main' must always return an int"); } /* Add a forced import of a symbol that is contained in the startup * code. This will force the startup code to be linked in. */ g_importstartup (); /* If main() takes parameters, generate a forced import to a function * that will setup these parameters. This way, programs that do not * need the additional code will not get it. */ if (D->ParamCount > 0 || (D->Flags & FD_VARIADIC) != 0) { g_importmainargs (); } /* Determine if this is a main function in a C99 environment that * returns an int. */ if (IsTypeInt (F_GetReturnType (CurrentFunc)) && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } } /* Allocate code and data segments for this function */ Func->V.F.Seg = PushSegments (Func); /* Allocate a new literal pool */ PushLiteralPool (Func); /* If this is a fastcall function, push the last parameter onto the stack */ if (IsQualFastcall (Func->Type) && D->ParamCount > 0) { unsigned Flags; /* Fastcall functions may never have an ellipsis or the compiler is buggy */ CHECK ((D->Flags & FD_VARIADIC) == 0); /* Generate the push */ if (IsTypeFunc (D->LastParam->Type)) { /* Pointer to function */ Flags = CF_PTR; } else { Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; } g_push (Flags, 0); } /* Generate function entry code if needed */ g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc)); /* If stack checking code is requested, emit a call to the helper routine */ if (IS_Get (&CheckStack)) { g_stackcheck (); } /* Setup the stack */ StackPtr = 0; /* Walk through the parameter list and allocate register variable space * for parameters declared as register. Generate code to swap the contents * of the register bank with the save area on the stack. */ Param = D->SymTab->SymHead; while (Param && (Param->Flags & SC_PARAM) != 0) { /* Check for a register variable */ if (SymIsRegVar (Param)) { /* Allocate space */ int Reg = F_AllocRegVar (CurrentFunc, Param->Type); /* Could we allocate a register? */ if (Reg < 0) { /* No register available: Convert parameter to auto */ CvtRegVarToAuto (Param); } else { /* Remember the register offset */ Param->V.R.RegOffs = Reg; /* Generate swap code */ g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (Param->Type)); } } /* Next parameter */ Param = Param->NextSym; } /* Need a starting curly brace */ ConsumeLCurly (); /* Parse local variable declarations if any */ DeclareLocals (); /* Remember the current stack pointer. All variables allocated elsewhere * must be dropped when doing a return from an inner block. */ CurrentFunc->TopLevelSP = StackPtr; /* Now process statements in this block */ while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { Statement (0); } /* If this is not a void function, and not the main function in a C99 * environment returning int, output a warning if we didn't see a return * statement. */ if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc) { Warning ("Control reaches end of non-void function"); } /* If this is the main function in a C99 environment returning an int, let * it always return zero. Note: Actual return statements jump to the return * label defined below. * The code is removed by the optimizer if unused. */ if (C99MainFunc) { g_getimmed (CF_INT | CF_CONST, 0, 0); } /* Output the function exit code label */ g_defcodelabel (F_GetRetLab (CurrentFunc)); /* Restore the register variables */ F_RestoreRegVars (CurrentFunc); /* Generate the exit code */ g_leave (); /* Emit references to imports/exports */ EmitExternals (); /* Emit function debug info */ F_EmitDebugInfo (); EmitDebugInfo (); /* Leave the lexical level */ LeaveFunctionLevel (); /* Eat the closing brace */ ConsumeRCurly (); /* Restore the old literal pool, remembering the one for the function */ Func->V.F.LitPool = PopLiteralPool (); /* Switch back to the old segments */ PopSegments (); /* Reset the current function pointer */ FreeFunction (CurrentFunc); CurrentFunc = 0; }
static void ParsePragma (void) /* Parse the contents of the _Pragma statement */ { pragma_t Pragma; StrBuf Ident = AUTO_STRBUF_INITIALIZER; /* Create a string buffer from the string literal */ StrBuf B = AUTO_STRBUF_INITIALIZER; SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); /* Skip the string token */ NextToken (); /* Get the pragma name from the string */ SB_SkipWhite (&B); if (!SB_GetSym (&B, &Ident, "-")) { Error ("Invalid pragma"); goto ExitPoint; } /* Search for the name */ Pragma = FindPragma (&Ident); /* Do we know this pragma? */ if (Pragma == PRAGMA_ILLEGAL) { /* According to the ANSI standard, we're not allowed to generate errors * for unknown pragmas, but warn about them if enabled (the default). */ if (IS_Get (&WarnUnknownPragma)) { Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident)); } goto ExitPoint; } /* Check for an open paren */ SB_SkipWhite (&B); if (SB_Get (&B) != '(') { Error ("'(' expected"); goto ExitPoint; } /* Skip white space before the argument */ SB_SkipWhite (&B); /* Switch for the different pragmas */ switch (Pragma) { case PRAGMA_ALIGN: IntPragma (&B, &DataAlignment, 1, 4096); break; case PRAGMA_BSSSEG: Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); /* FALLTHROUGH */ case PRAGMA_BSS_NAME: SegNamePragma (&B, SEG_BSS); break; case PRAGMA_CHARMAP: CharMapPragma (&B); break; case PRAGMA_CHECKSTACK: Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); /* FALLTHROUGH */ case PRAGMA_CHECK_STACK: FlagPragma (&B, &CheckStack); break; case PRAGMA_CODESEG: Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); /* FALLTHROUGH */ case PRAGMA_CODE_NAME: SegNamePragma (&B, SEG_CODE); break; case PRAGMA_CODESIZE: IntPragma (&B, &CodeSizeFactor, 10, 1000); break; case PRAGMA_DATASEG: Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); /* FALLTHROUGH */ case PRAGMA_DATA_NAME: SegNamePragma (&B, SEG_DATA); break; case PRAGMA_LOCAL_STRINGS: FlagPragma (&B, &LocalStrings); break; case PRAGMA_OPTIMIZE: FlagPragma (&B, &Optimize); break; case PRAGMA_REGVARADDR: FlagPragma (&B, &AllowRegVarAddr); break; case PRAGMA_REGVARS: Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); /* FALLTHROUGH */ case PRAGMA_REGISTER_VARS: FlagPragma (&B, &EnableRegVars); break; case PRAGMA_RODATASEG: Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); /* FALLTHROUGH */ case PRAGMA_RODATA_NAME: SegNamePragma (&B, SEG_RODATA); break; case PRAGMA_SIGNEDCHARS: Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); /* FALLTHROUGH */ case PRAGMA_SIGNED_CHARS: FlagPragma (&B, &SignedChars); break; case PRAGMA_STATICLOCALS: Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); /* FALLTHROUGH */ case PRAGMA_STATIC_LOCALS: FlagPragma (&B, &StaticLocals); break; case PRAGMA_WARN: WarnPragma (&B); break; case PRAGMA_WRITABLE_STRINGS: FlagPragma (&B, &WritableStrings); break; case PRAGMA_ZPSYM: StringPragma (&B, MakeZPSym); break; default: Internal ("Invalid pragma"); } /* Closing paren expected */ SB_SkipWhite (&B); if (SB_Get (&B) != ')') { Error ("')' expected"); goto ExitPoint; } SB_SkipWhite (&B); /* Allow an optional semicolon to be compatible with the old syntax */ if (SB_Peek (&B) == ';') { SB_Skip (&B); SB_SkipWhite (&B); } /* Make sure nothing follows */ if (SB_Peek (&B) != '\0') { Error ("Unexpected input following pragma directive"); } ExitPoint: /* Release the string buffers */ SB_Done (&B); SB_Done (&Ident); }
int Statement (int* PendingToken) /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or ** semicolon), but store true if there is a pending token, and false if there ** is none. The token is always checked, so there is no need for the caller to ** check this token, it must be skipped, however. If the argument pointer is ** NULL, the function will skip the token. */ { ExprDesc Expr; int GotBreak; CodeMark Start, End; /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; } /* Check for a label. A label is always part of a statement, it does not ** replace one. */ while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { /* Handle the label */ DoLabel (); if (CheckLabelWithoutStatement ()) { return 0; } } switch (CurTok.Tok) { case TOK_LCURLY: NextToken (); GotBreak = CompoundStatement (); CheckTok (TOK_RCURLY, "`{' expected", PendingToken); return GotBreak; case TOK_IF: return IfStatement (); case TOK_WHILE: WhileStatement (); break; case TOK_DO: DoStatement (); break; case TOK_SWITCH: SwitchStatement (); break; case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); return 1; case TOK_BREAK: BreakStatement (); CheckSemi (PendingToken); return 1; case TOK_CONTINUE: ContinueStatement (); CheckSemi (PendingToken); return 1; case TOK_FOR: ForStatement (); break; case TOK_GOTO: GotoStatement (); CheckSemi (PendingToken); return 1; case TOK_SEMI: /* Ignore it */ CheckSemi (PendingToken); break; case TOK_PRAGMA: DoPragma (); break; case TOK_CASE: CaseLabel (); CheckLabelWithoutStatement (); break; case TOK_DEFAULT: DefaultLabel (); CheckLabelWithoutStatement (); break; default: /* Remember the current code position */ GetCodePos (&Start); /* Actual statement */ ExprWithCheck (hie0, &Expr); /* Load the result only if it is an lvalue and the type is ** marked as volatile. Otherwise the load is useless. */ if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) { LoadExpr (CF_NONE, &Expr); } /* If the statement didn't generate code, and is not of type ** void, emit a warning. */ GetCodePos (&End); if (CodeRangeIsEmpty (&Start, &End) && !IsTypeVoid (Expr.Type) && IS_Get (&WarnNoEffect)) { Warning ("Statement has no effect"); } CheckSemi (PendingToken); } return 0; }
TypeCode GetDefaultChar (void) /* Return the default char type (signed/unsigned) depending on the settings */ { return IS_Get (&SignedChars)? T_SCHAR : T_UCHAR; }
static unsigned Pass1 (StrBuf* Source, StrBuf* Target) /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments * and the "defined" operator. */ { unsigned IdentCount; ident Ident; int HaveParen; /* Switch to the new input source */ StrBuf* OldSource = InitLine (Source); /* Loop removing ws and comments */ IdentCount = 0; while (CurC != '\0') { if (SkipWhitespace (0)) { /* Squeeze runs of blanks */ if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } } else if (IsSym (Ident)) { if (Preprocessing && strcmp (Ident, "defined") == 0) { /* Handle the "defined" operator */ SkipWhitespace (0); HaveParen = 0; if (CurC == '(') { HaveParen = 1; NextChar (); SkipWhitespace (0); } if (IsSym (Ident)) { SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); if (HaveParen) { SkipWhitespace (0); if (CurC != ')') { PPError ("`)' expected"); } else { NextChar (); } } } else { PPError ("Identifier expected"); SB_AppendChar (Target, '0'); } } else { ++IdentCount; SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { CopyQuotedString (Target); } else if (CurC == '/' && NextC == '*') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } NewStyleComment (); } else { SB_AppendChar (Target, CurC); NextChar (); } } /* Switch back to the old source */ InitLine (OldSource); /* Return the number of identifiers found in the line */ return IdentCount; }
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"); } }
static void ReadMacroArgs (MacroExp* E) /* Identify the arguments to a macro call */ { unsigned Parens; /* Number of open parenthesis */ StrBuf Arg = STATIC_STRBUF_INITIALIZER; /* Read the actual macro arguments */ Parens = 0; while (1) { if (CurC == '(') { /* Nested parenthesis */ SB_AppendChar (&Arg, CurC); NextChar (); ++Parens; } else if (IsQuote (CurC)) { /* Quoted string - just copy */ CopyQuotedString (&Arg); } else if (CurC == ',' || CurC == ')') { if (Parens) { /* Comma or right paren inside nested parenthesis */ if (CurC == ')') { --Parens; } SB_AppendChar (&Arg, CurC); NextChar (); } else if (CurC == ',' && ME_ArgIsVariadic (E)) { /* It's a comma, but we're inside a variadic macro argument, so * just copy it and proceed. */ SB_AppendChar (&Arg, CurC); NextChar (); } else { /* End of actual argument. Remove whitespace from the end. */ while (IsSpace (SB_LookAtLast (&Arg))) { SB_Drop (&Arg, 1); } /* If this is not the single empty argument for a macro with * an empty argument list, remember it. */ if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { ME_AppendActual (E, &Arg); } /* Check for end of macro param list */ if (CurC == ')') { NextChar (); break; } /* Start the next param */ NextChar (); SB_Clear (&Arg); } } else if (SkipWhitespace (1)) { /* Squeeze runs of blanks within an arg */ if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } } else if (CurC == '/' && NextC == '*') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } NewStyleComment (); } else if (CurC == '\0') { /* End of input inside macro argument list */ PPError ("Unterminated argument list invoking macro `%s'", E->M->Name); ClearLine (); break; } else { /* Just copy the character */ SB_AppendChar (&Arg, CurC); NextChar (); } } /* Deallocate string buf resources */ SB_Done (&Arg); }
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)); } }
static void NumericConst (void) /* Parse a numeric constant */ { unsigned Base; /* Temporary number base */ unsigned Prefix; /* Base according to prefix */ StrBuf S = STATIC_STRBUF_INITIALIZER; int IsFloat; char C; unsigned DigitVal; unsigned long IVal; /* Value */ /* Check for a leading hex or octal prefix and determine the possible ** integer types. */ if (CurC == '0') { /* Gobble 0 and examine next char */ NextChar (); if (toupper (CurC) == 'X') { Base = Prefix = 16; NextChar (); /* gobble "x" */ } else { Base = 10; /* Assume 10 for now - see below */ Prefix = 8; /* Actual prefix says octal */ } } else { Base = Prefix = 10; } /* Because floating point numbers don't have octal prefixes (a number ** with a leading zero is decimal), we first have to read the number ** before converting it, so we can determine if it's a float or an ** integer. */ while (IsXDigit (CurC) && HexVal (CurC) < Base) { SB_AppendChar (&S, CurC); NextChar (); } SB_Terminate (&S); /* The following character tells us if we have an integer or floating ** point constant. Note: Hexadecimal floating point constants aren't ** supported in C89. */ IsFloat = (CurC == '.' || (Base == 10 && toupper (CurC) == 'E') || (Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99)); /* If we don't have a floating point type, an octal prefix results in an ** octal base. */ if (!IsFloat && Prefix == 8) { Base = 8; } /* Since we do now know the correct base, convert the remembered input ** into a number. */ SB_Reset (&S); IVal = 0; while ((C = SB_Get (&S)) != '\0') { DigitVal = HexVal (C); if (DigitVal >= Base) { Error ("Numeric constant contains digits beyond the radix"); } IVal = (IVal * Base) + DigitVal; } /* We don't need the string buffer any longer */ SB_Done (&S); /* Distinguish between integer and floating point constants */ if (!IsFloat) { unsigned Types; int HaveSuffix; /* Check for a suffix and determine the possible types */ HaveSuffix = 1; if (toupper (CurC) == 'U') { /* Unsigned type */ NextChar (); if (toupper (CurC) != 'L') { Types = IT_UINT | IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else if (toupper (CurC) == 'L') { /* Long type */ NextChar (); if (toupper (CurC) != 'U') { Types = IT_LONG | IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else { HaveSuffix = 0; if (Prefix == 10) { /* Decimal constants are of any type but uint */ Types = IT_INT | IT_LONG | IT_ULONG; } else { /* Octal or hex constants are of any type */ Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; } } /* Check the range to determine the type */ if (IVal > 0x7FFF) { /* Out of range for int */ Types &= ~IT_INT; /* If the value is in the range 0x8000..0xFFFF, unsigned int is not ** allowed, and we don't have a type specifying suffix, emit a ** warning, because the constant is of type long. */ if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) { Warning ("Constant is long"); } } if (IVal > 0xFFFF) { /* Out of range for unsigned int */ Types &= ~IT_UINT; } if (IVal > 0x7FFFFFFF) { /* Out of range for long int */ Types &= ~IT_LONG; } /* Now set the type string to the smallest type in types */ if (Types & IT_INT) { NextTok.Type = type_int; } else if (Types & IT_UINT) { NextTok.Type = type_uint; } else if (Types & IT_LONG) { NextTok.Type = type_long; } else { NextTok.Type = type_ulong; } /* Set the value and the token */ NextTok.IVal = IVal; NextTok.Tok = TOK_ICONST; } else { /* Float constant */ Double FVal = FP_D_FromInt (IVal); /* Convert to double */ /* Check for a fractional part and read it */ if (CurC == '.') { Double Scale; /* Skip the dot */ NextChar (); /* Read fractional digits */ Scale = FP_D_Make (1.0); while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) { /* Get the value of this digit */ Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale); /* Add it to the float value */ FVal = FP_D_Add (FVal, FracVal); /* Scale base */ Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); /* Skip the digit */ NextChar (); } } /* Check for an exponent and read it */ if ((Base == 16 && toupper (CurC) == 'F') || (Base == 10 && toupper (CurC) == 'E')) { unsigned Digits; unsigned Exp; /* Skip the exponent notifier */ NextChar (); /* Read an optional sign */ if (CurC == '-') { NextChar (); } else if (CurC == '+') { NextChar (); } /* Read exponent digits. Since we support only 32 bit floats ** with a maximum exponent of +-/127, we read the exponent ** part as integer with up to 3 digits and drop the remainder. ** This avoids an overflow of Exp. The exponent is always ** decimal, even for hex float consts. */ Digits = 0; Exp = 0; while (IsDigit (CurC)) { if (++Digits <= 3) { Exp = Exp * 10 + HexVal (CurC); } NextChar (); } /* Check for errors: We must have exponent digits, and not more ** than three. */ if (Digits == 0) { Error ("Floating constant exponent has no digits"); } else if (Digits > 3) { Warning ("Floating constant exponent is too large"); } /* Scale the exponent and adjust the value accordingly */ if (Exp) { FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp))); } } /* Check for a suffix and determine the type of the constant */ if (toupper (CurC) == 'F') { NextChar (); NextTok.Type = type_float; } else { NextTok.Type = type_double; } /* Set the value and the token */ NextTok.FVal = FVal; NextTok.Tok = TOK_FCONST; } }