Merge branch 'cc65:master' into kim1

This commit is contained in:
Rutger van Bergen
2022-09-19 20:03:23 +02:00
committed by GitHub
218 changed files with 92413 additions and 995 deletions

View File

@@ -1298,7 +1298,7 @@ static void EmitCode (EffAddr* A)
break;
case 2:
if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y))) {
if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y | AM65_ABS_X_IND))) {
/* This is a 16 bit mode that uses an address. If in 65816,
** mode, force this address into 16 bit range to allow
** addressing inside a 64K segment.

View File

@@ -489,23 +489,6 @@ void MacDef (unsigned Style)
** the .LOCAL command is detected and removed, at this time.
*/
while (1) {
/* Check for include */
if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) {
/* Include another file */
NextTok ();
/* Name must follow */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
/* Error opening the file, skip remainder of line */
SkipUntilSep ();
}
}
NextTok ();
}
/* Check for end of macro */
if (Style == MAC_STYLE_CLASSIC) {
/* In classic macros, only .endmacro is allowed */

View File

@@ -103,6 +103,7 @@
<ClInclude Include="cc65\macrotab.h" />
<ClInclude Include="cc65\opcodes.h" />
<ClInclude Include="cc65\output.h" />
<ClInclude Include="cc65\ppexpr.h" />
<ClInclude Include="cc65\pragma.h" />
<ClInclude Include="cc65\preproc.h" />
<ClInclude Include="cc65\reginfo.h" />
@@ -182,6 +183,7 @@
<ClCompile Include="cc65\main.c" />
<ClCompile Include="cc65\opcodes.c" />
<ClCompile Include="cc65\output.c" />
<ClCompile Include="cc65\ppexpr.c" />
<ClCompile Include="cc65\pragma.c" />
<ClCompile Include="cc65\preproc.c" />
<ClCompile Include="cc65\reginfo.c" />

View File

@@ -49,21 +49,6 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
token_t Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@@ -605,6 +590,12 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
/* Normal straight 'op=' */
OpAssignArithmetic (Gen, Expr, Op);
}
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@@ -725,4 +716,10 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
/* Expression is an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}

View File

@@ -1797,7 +1797,6 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
}
} else if (strcmp (E->Arg, "tosaslax") == 0) {
if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) {
printf ("Hey!\n");
Out->RegA = 0;
}
} else if (strcmp (E->Arg, "tosorax") == 0) {

View File

@@ -461,7 +461,8 @@ void g_importstartup (void)
void g_importmainargs (void)
/* Forced import of a special symbol that handles arguments to main */
/* Forced import of a special symbol that handles arguments to main. This will
happen only when the compiler sees a main function that takes arguments. */
{
AddTextLine ("\t.forceimport\tinitmainargs");
}
@@ -4920,5 +4921,25 @@ void g_switch (Collection* Nodes, unsigned DefaultLabel, unsigned Depth)
void g_asmcode (struct StrBuf* B)
/* Output one line of assembler code. */
{
AddCodeLine ("%.*s", (int) SB_GetLen (B), SB_GetConstBuf (B));
int len = (int) SB_GetLen(B);
const char *buf = SB_GetConstBuf(B);
/* remove whitespace at end of line */
/* NOTE: This masks problems in ParseInsn(), which in some cases seems to
rely on no whitespace being present at the end of a line in generated
code (see issue #1252). However, it generally seems to be a good
idea to remove trailing whitespace from (inline) assembly, so we
do it anyway. */
while (len) {
switch (buf[len - 1]) {
case '\n':
case ' ':
case '\t':
--len;
continue;
}
break;
}
AddCodeLine ("%.*s", len, buf);
}

View File

@@ -82,8 +82,11 @@ static void Parse (void)
SymEntry* Entry;
FuncDesc* FuncDef = 0;
/* Go... */
NextToken ();
/* Initialization for deferred operations */
InitDeferredOps ();
/* Fill up the next token with a bogus semicolon and start the tokenizer */
NextTok.Tok = TOK_SEMI;
NextToken ();
/* Parse until end of input */
@@ -207,7 +210,7 @@ static void Parse (void)
/* Allow initialization */
if (CurTok.Tok == TOK_ASSIGN) {
/* This is a definition */
/* This is a definition with storage */
if (SymIsDef (Entry)) {
Error ("Global variable '%s' has already been defined",
Entry->Name);
@@ -218,14 +221,16 @@ static void Parse (void)
** void types in ISO modes.
*/
if (Size == 0) {
if (!IsTypeVoid (Decl.Type)) {
if (!IsEmptiableObjectType (Decl.Type)) {
if (!IsTypeArray (Decl.Type)) {
/* Size is unknown and not an array */
Error ("Variable '%s' has unknown size", Decl.Ident);
Error ("Cannot initialize variable '%s' of unknown size", Decl.Ident);
}
} else if (IS_Get (&Standard) != STD_CC65) {
/* We cannot declare variables of type void */
Error ("Illegal type for variable '%s'", Decl.Ident);
Error ("Illegal type '%s' for variable '%s'",
GetFullTypeName (Decl.Type),
Decl.Ident);
}
}
@@ -249,16 +254,26 @@ static void Parse (void)
ParseInit (Entry->Type);
} else {
/* This is a declaration */
if (IsTypeVoid (Decl.Type)) {
/* We cannot declare variables of type void */
Error ("Illegal type for variable '%s'", Decl.Ident);
Entry->Flags &= ~(SC_STORAGE | SC_DEF);
} else if (Size == 0 && SymIsDef (Entry)) {
} else if (Size == 0 && SymIsDef (Entry) && !IsEmptiableObjectType (Decl.Type)) {
/* Size is unknown. Is it an array? */
if (!IsTypeArray (Decl.Type)) {
Error ("Variable '%s' has unknown size", Decl.Ident);
}
} else {
/* Check for enum forward declaration.
** Warn about it when extensions are not allowed.
*/
if (Size == 0 && IsTypeEnum (Decl.Type)) {
if (IS_Get (&Standard) != STD_CC65) {
Warning ("ISO C forbids forward references to 'enum' types");
}
}
/* A global (including static) uninitialized variable is
** only a tentative definition. For example, this is valid:
** int i;
@@ -285,17 +300,9 @@ static void Parse (void)
}
/* Make the symbol zeropage according to the segment address size */
if ((Entry->Flags & SC_EXTERN) != 0) {
if ((Entry->Flags & SC_STATIC) != 0) {
if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) {
Entry->Flags |= SC_ZEROPAGE;
/* Check for enum forward declaration.
** Warn about it when extensions are not allowed.
*/
if (Size == 0 && IsTypeEnum (Decl.Type)) {
if (IS_Get (&Standard) != STD_CC65) {
Warning ("ISO C forbids forward references to 'enum' types");
}
}
}
}
@@ -334,6 +341,9 @@ static void Parse (void)
}
}
/* Done with deferred operations */
DoneDeferredOps ();
}
@@ -386,6 +396,11 @@ void Compile (const char* FileName)
DefineNumericMacro ("__EAGERLY_INLINE_FUNCS__", 1);
}
/* Placeholders for __FILE__, __LINE__ and __COUNTER__ macros */
DefineTextMacro ("__FILE__", "");
DefineTextMacro ("__LINE__", "");
DefineTextMacro ("__COUNTER__", "");
/* __TIME__ and __DATE__ macros */
Time = time (0);
TM = localtime (&Time);
@@ -399,7 +414,13 @@ void Compile (const char* FileName)
/* DefineNumericMacro ("__STDC__", 1); <- not now */
DefineNumericMacro ("__STDC_HOSTED__", 1);
InitDeferredOps ();
/* Stuff unsupported */
if (IS_Get (&Standard) > STD_C99) {
DefineNumericMacro ("__STDC_NO_ATOMICS__", 1);
DefineNumericMacro ("__STDC_NO_COMPLEX__", 1);
DefineNumericMacro ("__STDC_NO_THREADS__", 1);
DefineNumericMacro ("__STDC_NO_VLA__", 1);
}
/* Create the base lexical level */
EnterGlobalLevel ();
@@ -419,6 +440,9 @@ void Compile (const char* FileName)
/* Generate the code generator preamble */
g_preamble ();
/* Init preprocessor */
InitPreprocess ();
/* Open the input file */
OpenMainFile (FileName);
@@ -429,10 +453,8 @@ void Compile (const char* FileName)
OpenOutputFile ();
/* Preprocess each line and write it to the output file */
while (NextLine ()) {
Preprocess ();
WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
while (PreprocessNextLine ())
{ /* Nothing */ }
/* Close the output file */
CloseOutputFile ();
@@ -490,9 +512,11 @@ void Compile (const char* FileName)
}
}
}
}
DoneDeferredOps ();
/* Done with preprocessor */
DonePreprocess ();
if (Debug) {
PrintMacroStats (stdout);

View File

@@ -805,7 +805,11 @@ unsigned CheckedSizeOf (const Type* T)
{
unsigned Size = SizeOf (T);
if (Size == 0) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T));
if (HasUnknownSize (T + 1)) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T));
} else {
Error ("Size of type '%s' is 0", GetFullTypeName (T));
}
Size = SIZEOF_CHAR; /* Don't return zero */
}
return Size;
@@ -821,7 +825,11 @@ unsigned CheckedPSizeOf (const Type* T)
{
unsigned Size = PSizeOf (T);
if (Size == 0) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T + 1));
if (HasUnknownSize (T + 1)) {
Error ("Pointer to type '%s' of unknown size", GetFullTypeName (T + 1));
} else {
Error ("Pointer to type '%s' of 0 size", GetFullTypeName (T + 1));
}
Size = SIZEOF_CHAR; /* Don't return zero */
}
return Size;
@@ -999,6 +1007,25 @@ const Type* PtrConversion (const Type* T)
const Type* StdConversion (const Type* T)
/* If the type is a function, convert it to pointer to function. If the
** expression is an array, convert it to pointer to first element. If the
** type is an integer, do integeral promotion. Otherwise return T.
*/
{
if (IsTypeFunc (T)) {
return AddressOf (T);
} else if (IsTypeArray (T)) {
return AddressOf (GetElementType (T));
} else if (IsClassInt (T)) {
return IntPromotion (T);
} else {
return T;
}
}
const Type* IntPromotion (const Type* T)
/* Apply the integer promotions to T and return the result. The returned type
** string may be T if there is no need to change it.

View File

@@ -368,6 +368,12 @@ const Type* PtrConversion (const Type* T);
** return T.
*/
const Type* StdConversion (const Type* T);
/* If the type is a function, convert it to pointer to function. If the
** expression is an array, convert it to pointer to first element. If the
** type is an integer, do integeral promotion. Otherwise return T.
*/
const Type* IntPromotion (const Type* T);
/* Apply the integer promotions to T and return the result. The returned type
** string may be T if there is no need to change it.

View File

@@ -48,13 +48,6 @@
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
token_t Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/* Descriptors for the operations */
static GenDesc GenPASGN = { TOK_PLUS_ASSIGN, GEN_NOPUSH, g_add };
static GenDesc GenSASGN = { TOK_MINUS_ASSIGN, GEN_NOPUSH, g_sub };
@@ -243,7 +236,7 @@ static const GenDesc* FindGen (token_t Tok, const GenDesc* Table)
/* Find a token in a generator table */
{
while (Table->Tok != TOK_INVALID) {
if (Table->Tok == Tok) {
if ((token_t)Table->Tok == Tok) {
return Table;
}
++Table;
@@ -311,13 +304,9 @@ void PushAddr (const ExprDesc* Expr)
static void WarnConstCompareResult (const ExprDesc* Expr)
/* If the result of a comparison is constant, this is suspicious when not in
** preprocessor mode.
*/
/* If the result of a comparison is constant, this is suspicious */
{
if (!Preprocessing &&
!ED_NeedsConst (Expr) &&
IS_Get (&WarnConstComparison) != 0) {
if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) {
Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false");
}
}
@@ -649,6 +638,9 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr)
/* Sufficient to pop the processor flags */
AddCodeLine ("plp");
}
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@@ -772,9 +764,10 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED)
} else {
/* No prototype available. Convert array to "pointer to first
** element", and function to "pointer to function".
** element", function to "pointer to function" and do integral
** promotion if necessary.
*/
Expr.Type = PtrConversion (Expr.Type);
TypeConversion (&Expr, StdConversion (Expr.Type));
}
@@ -822,6 +815,9 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED)
}
}
/* Propagate viral flags */
ED_PropagateFrom (ED, &Expr);
/* Check for end of argument list */
if (CurTok.Tok != TOK_COMMA) {
break;
@@ -1064,6 +1060,9 @@ static void FunctionCall (ExprDesc* Expr)
}
Expr->Type = ReturnType;
/* We assume all function calls had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@@ -1072,58 +1071,19 @@ static void Primary (ExprDesc* E)
/* This is the lowest level of the expression parser. */
{
SymEntry* Sym;
/* Character and integer constants. */
if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) {
E->IVal = CurTok.IVal;
E->Flags |= E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Floating point constant */
if (CurTok.Tok == TOK_FCONST) {
E->V.FVal = CurTok.FVal;
E->Flags |= E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
if (CurTok.Tok == TOK_LPAREN) {
NextToken ();
hie0 (E);
ConsumeRParen ();
return;
}
/* If we run into an identifier in preprocessing mode, we assume that this
** is an undefined macro and replace it by a constant value of zero.
*/
if (Preprocessing && CurTok.Tok == TOK_IDENT) {
NextToken ();
ED_MakeConstAbsInt (E, 0);
return;
}
/* All others may only be used if the expression evaluation is not called
** recursively by the preprocessor.
*/
if (Preprocessing) {
/* Illegal expression in PP mode */
Error ("Preprocessor expression expected");
ED_MakeConstAbsInt (E, 1);
return;
}
unsigned Flags = E->Flags & E_MASK_KEEP_MAKE;
switch (CurTok.Tok) {
case TOK_LPAREN:
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
hie0 (E);
ConsumeRParen ();
break;
case TOK_BOOL_AND:
/* A computed goto label address */
if (IS_Get (&Standard) >= STD_CC65) {
@@ -1133,7 +1093,7 @@ static void Primary (ExprDesc* E)
/* output its label */
E->Flags = E_RTYPE_RVAL | E_LOC_CODE | E_ADDRESS_OF;
E->Name = Entry->V.L.Label;
E->Type = NewPointerTo (type_void);
E->Type = type_void_p;
NextToken ();
} else {
Error ("Computed gotos are a C extension, not supported with this --standard");
@@ -1157,9 +1117,9 @@ static void Primary (ExprDesc* E)
/* Cannot use type symbols */
Error ("Variable identifier expected");
/* Assume an int type to make E valid */
E->Flags |= E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
return;
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
break;
}
/* Mark the symbol as referenced */
@@ -1172,11 +1132,7 @@ static void Primary (ExprDesc* E)
if ((Sym->Flags & SC_CONST) == SC_CONST) {
/* Enum or some other numeric constant */
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->IVal = Sym->V.ConstVal;
} else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
E->IVal = Sym->V.ConstVal;
} else if ((Sym->Flags & SC_AUTO) == SC_AUTO) {
/* Local variable. If this is a parameter for a variadic
** function, we have to add some address calculations, and the
@@ -1191,6 +1147,10 @@ static void Primary (ExprDesc* E)
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->IVal = Sym->V.Offs;
}
} else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
} else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) {
/* Register variable, zero page based */
E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL;
@@ -1199,10 +1159,10 @@ static void Primary (ExprDesc* E)
/* Static variable */
if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) {
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
E->Name = (uintptr_t) Sym->Name;
} else {
E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
E->Name = Sym->V.L.Label;
E->Name = Sym->V.L.Label;
}
} else {
/* Local static variable */
@@ -1248,7 +1208,7 @@ static void Primary (ExprDesc* E)
/* Undeclared Variable */
Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0);
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
E->Type = type_int;
Error ("Undefined symbol: '%s'", Ident);
}
@@ -1260,6 +1220,8 @@ static void Primary (ExprDesc* E)
/* String literal */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
E->V.LVal = UseLiteral (CurTok.SVal);
/* Translate into target charset */
TranslateLiteral (E->V.LVal);
} else {
E->V.LVal = CurTok.SVal;
}
@@ -1270,10 +1232,28 @@ static void Primary (ExprDesc* E)
NextToken ();
break;
case TOK_ICONST:
case TOK_CCONST:
case TOK_WCCONST:
/* Character and integer constants */
E->IVal = CurTok.IVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_FCONST:
/* Floating point constant */
E->V.FVal = CurTok.FVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_ASM:
/* ASM statement */
AsmStatement ();
E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED;
E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED | E_SIDE_EFFECTS;
E->Type = type_void;
break;
@@ -1658,6 +1638,9 @@ static void PreInc (ExprDesc* Expr)
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@@ -1685,6 +1668,9 @@ static void PreDec (ExprDesc* Expr)
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@@ -1721,6 +1707,9 @@ static void PostInc (ExprDesc* Expr)
LoadExpr (CF_NONE, Expr);
AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
@@ -1728,6 +1717,9 @@ static void PostInc (ExprDesc* Expr)
/* Do the increment */
DoInc (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the increment until after the value of this expression is used */
@@ -1771,6 +1763,9 @@ static void PostDec (ExprDesc* Expr)
LoadExpr (CF_NONE, Expr);
AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
@@ -1778,6 +1773,9 @@ static void PostDec (ExprDesc* Expr)
/* Do the decrement */
DoDec (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the decrement until after the value of this expression is used */
@@ -1952,7 +1950,7 @@ void hie10 (ExprDesc* Expr)
/* The & operator yields an rvalue address */
ED_AddrExpr (Expr);
}
Expr->Type = NewPointerTo (Expr->Type);
Expr->Type = AddressOf (Expr->Type);
break;
case TOK_SIZEOF:
@@ -2238,6 +2236,9 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* We have an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
}
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
@@ -2271,9 +2272,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
Tok = CurTok.Tok;
NextToken ();
/* If lhs is a function, convert it to pointer to function */
/* If lhs is a function, convert it to the address of the function */
if (IsTypeFunc (Expr->Type)) {
Expr->Type = NewPointerTo (Expr->Type);
Expr->Type = AddressOf (Expr->Type);
}
/* Get the lhs on stack */
@@ -2293,9 +2294,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
/* Get the right hand side */
MarkedExprWithCheck (hienext, &Expr2);
/* If rhs is a function, convert it to pointer to function */
/* If rhs is a function, convert it to the address of the function */
if (IsTypeFunc (Expr2.Type)) {
Expr2.Type = NewPointerTo (Expr2.Type);
Expr2.Type = AddressOf (Expr2.Type);
}
/* Check for a numeric constant expression */
@@ -2340,7 +2341,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
} else if (IsClassPtr (Expr->Type)) {
if (IsClassPtr (Expr2.Type)) {
/* Pointers are allowed in comparison */
if (TypeCmp (Expr->Type, Expr2.Type).C < TC_STRICT_COMPATIBLE) {
if (TypeCmp (Expr->Type, Expr2.Type).C < TC_VOID_PTR) {
/* Warn about distinct pointer types */
TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0,
"Distinct pointer types comparing '%s' with '%s'");
@@ -2659,6 +2660,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
/* Result type is always boolean */
Done: Expr->Type = type_bool;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
@@ -3069,12 +3073,15 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef)
Error ("Invalid operands for binary operator '+'");
} else {
/* Array and function types must be converted to pointer types */
Expr->Type = PtrConversion (Expr->Type);
Expr->Type = StdConversion (Expr->Type);
}
}
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@@ -3348,10 +3355,13 @@ static void parsesub (ExprDesc* Expr)
}
/* Result type is either a pointer or an integer */
Expr->Type = PtrConversion (Expr->Type);
Expr->Type = StdConversion (Expr->Type);
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@@ -3441,48 +3451,6 @@ static void hie2 (ExprDesc* Expr)
static void hieAndPP (ExprDesc* Expr)
/* Process "exp && exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
*Expr = NoCodeConstAbsIntExpr (hie2);
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get rhs */
ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2);
/* Combine the two */
Expr->IVal = (Expr->IVal && Expr2.IVal);
}
}
static void hieOrPP (ExprDesc *Expr)
/* Process "exp || exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
*Expr = NoCodeConstAbsIntExpr (hieAndPP);
while (CurTok.Tok == TOK_BOOL_OR) {
/* Skip the && */
NextToken ();
/* Get rhs */
ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP);
/* Combine the two */
Expr->IVal = (Expr->IVal || Expr2.IVal);
}
}
static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
/* Process "exp && exp". This should only be called within hieOr.
** Return true if logic AND does occur.
@@ -3603,6 +3571,12 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
}
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Last expression */
@@ -3768,6 +3742,11 @@ static void hieOr (ExprDesc *Expr)
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Convert to bool */
@@ -3821,11 +3800,7 @@ static void hieQuest (ExprDesc* Expr)
Type* ResultType; /* Type of result */
/* Call the lower level eval routine */
if (Preprocessing) {
ExprWithCheck (hieOrPP, Expr);
} else {
ExprWithCheck (hieOr, Expr);
}
ExprWithCheck (hieOr, Expr);
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
@@ -4059,6 +4034,14 @@ static void hieQuest (ExprDesc* Expr)
/* Setup the target expression */
Expr->Type = ResultType;
/* Propagate viral flags */
if ((Expr2.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
if ((Expr3.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr3);
}
}
}
@@ -4136,26 +4119,39 @@ void hie0 (ExprDesc *Expr)
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_NONE, Expr);
/* If the expression didn't generate code or isn't cast to type void,
** emit a warning.
/* If the expression has no observable effect and isn't cast to type
** void, emit a warning and remove useless code if any.
*/
GetCodePos (&End);
if (!ED_MayHaveNoEffect (Expr) &&
CodeRangeIsEmpty (&Start, &End) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Expression result unused");
if (CodeRangeIsEmpty (&Start, &End) ||
(Expr->Flags & E_SIDE_EFFECTS) == 0) {
if (!ED_MayHaveNoEffect (Expr) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Left-hand operand of comma expression has no effect");
}
/* Remove code with no effect */
RemoveCode (&Start);
}
PrevErrorCount = ErrorCount;
/* Remember the current code position */
GetCodePos (&Start);
/* Keep viral flags propagated from subexpressions */
Flags |= Expr->Flags & E_MASK_VIRAL;
/* Reset the expression */
ED_Init (Expr);
Expr->Flags = Flags;
Expr->Flags = Flags & ~E_MASK_VIRAL;
NextToken ();
hie1 (Expr);
/* Propagate viral flags */
Expr->Flags |= Flags & E_MASK_VIRAL;
}
}
@@ -4217,7 +4213,8 @@ ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*))
if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) {
Error ("Constant expression expected");
/* To avoid any compiler errors, make the expression a valid const */
ED_MakeConstAbsInt (&Expr, 1);
Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_RESULT;
Expr.Flags |= E_LOC_NONE;
}
/* Return by value */

View File

@@ -26,13 +26,20 @@
#define SQP_KEEP_NONE 0x00
#define SQP_KEEP_TEST 0x01U
#define SQP_KEEP_EAX 0x02U
#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */
#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */
/* Generator attributes */
#define GEN_NOPUSH 0x01 /* Don't push lhs */
#define GEN_COMM 0x02 /* Operator is commutative */
#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
long Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/*****************************************************************************/

View File

@@ -125,6 +125,7 @@ enum {
E_LOADED = 0x1000, /* Expression is loaded in primary */
E_CC_SET = 0x2000, /* Condition codes are set */
E_HAVE_MARKS = 0x4000, /* Code marks are valid */
E_SIDE_EFFECTS = 0x8000, /* Expression has had side effects */
/* Optimization hints */
E_MASK_NEED = 0x030000,
@@ -181,6 +182,9 @@ enum {
/* Expression result must be known to the compiler and generate no code to load */
E_EVAL_C_CONST = E_EVAL_COMPILER_KNOWN | E_EVAL_NO_CODE,
/* Flags to combine from subexpressions */
E_MASK_VIRAL = E_SIDE_EFFECTS,
/* Flags to keep in subexpressions of most operations other than ternary */
E_MASK_KEEP_SUBEXPR = E_MASK_EVAL,
@@ -467,6 +471,16 @@ INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr)
# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED)
#endif
#if defined(HAVE_INLINE)
INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr)
/* Propagate viral flags from subexpression */
{
Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL;
}
#else
# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL)
#endif
#if defined(HAVE_INLINE)
INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */

View File

@@ -54,6 +54,7 @@
#include "input.h"
#include "lineinfo.h"
#include "output.h"
#include "preproc.h"
@@ -91,6 +92,9 @@ struct AFile {
FILE* F; /* Input file stream */
IFile* Input; /* Points to corresponding IFile */
int SearchPath; /* True if we've added a path for this file */
char* PName; /* Presumed name of the file */
PPIfStack IfStack; /* PP #if stack */
int MissingNL; /* Last input line was missing a newline */
};
/* List of all input files */
@@ -102,6 +106,9 @@ static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
/* Input stack used when preprocessing. */
static Collection InputStack = STATIC_COLLECTION_INITIALIZER;
/* Counter for the __COUNTER__ macro */
static unsigned MainFileCounter;
/*****************************************************************************/
@@ -156,6 +163,9 @@ static AFile* NewAFile (IFile* IF, FILE* F)
AF->Line = 0;
AF->F = F;
AF->Input = IF;
AF->PName = 0;
AF->IfStack.Index = -1;
AF->MissingNL = 0;
/* Increment the usage counter of the corresponding IFile. If this
** is the first use, set the file data and output debug info if
@@ -204,6 +214,9 @@ static AFile* NewAFile (IFile* IF, FILE* F)
static void FreeAFile (AFile* AF)
/* Free an AFile structure */
{
if (AF->PName != 0) {
xfree (AF->PName);
}
xfree (AF);
}
@@ -257,6 +270,12 @@ void OpenMainFile (const char* Name)
/* Allocate a new AFile structure for the file */
MainFile = NewAFile (IF, F);
/* Use this file with PP */
SetPPIfStack (&MainFile->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
/* Allocate the input line buffer */
Line = NewStrBuf ();
@@ -264,6 +283,9 @@ void OpenMainFile (const char* Name)
** the main file before the first line is read.
*/
UpdateLineInfo (MainFile->Input, MainFile->Line, Line);
/* Initialize the __COUNTER__ counter */
MainFileCounter = 0;
}
@@ -274,6 +296,7 @@ void OpenIncludeFile (const char* Name, InputType IT)
char* N;
FILE* F;
IFile* IF;
AFile* AF;
/* Check for the maximum include nesting */
if (CollCount (&AFiles) > MAX_INC_NESTING) {
@@ -311,12 +334,18 @@ void OpenIncludeFile (const char* Name, InputType IT)
Print (stdout, 1, "Opened include file '%s'\n", IF->Name);
/* Allocate a new AFile structure */
(void) NewAFile (IF, F);
AF = NewAFile (IF, F);
/* Use this file with PP */
SetPPIfStack (&AF->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
}
static void CloseIncludeFile (void)
void CloseIncludeFile (void)
/* Close an include file and switch to the higher level file. Set Input to
** NULL if this was the main file.
*/
@@ -329,14 +358,18 @@ static void CloseIncludeFile (void)
/* Must have an input file when called */
PRECONDITION (AFileCount > 0);
/* End preprocessor in this file */
PreprocessEnd ();
/* Get the current active input file */
Input = (AFile*) CollLast (&AFiles);
Input = CollLast (&AFiles);
/* Close the current input file (we're just reading so no error check) */
fclose (Input->F);
/* Delete the last active file from the active file collection */
CollDelete (&AFiles, AFileCount-1);
--AFileCount;
CollDelete (&AFiles, AFileCount);
/* If we had added an extra search path for this AFile, remove it */
if (Input->SearchPath) {
@@ -345,6 +378,12 @@ static void CloseIncludeFile (void)
/* Delete the active file structure */
FreeAFile (Input);
/* Use previous file with PP if it is not the main file */
if (AFileCount > 0) {
Input = CollLast (&AFiles);
SetPPIfStack (&Input->IfStack);
}
}
@@ -436,47 +475,49 @@ StrBuf* InitLine (StrBuf* Buf)
int NextLine (void)
/* Get a line from the current input. Returns 0 on end of file. */
/* Get a line from the current input. Returns 0 on end of file with no new
** input bytes.
*/
{
int C;
AFile* Input;
/* Clear the current line */
ClearLine ();
SB_Clear (Line);
/* If there is no file open, bail out, otherwise get the current input file */
if (CollCount (&AFiles) == 0) {
/* Must have an input file when called */
if (CollCount(&AFiles) == 0) {
return 0;
}
/* Get the current input file */
Input = CollLast (&AFiles);
/* Read characters until we have one complete line */
while (1) {
/* Read the next character */
int C = fgetc (Input->F);
C = fgetc (Input->F);
/* Check for EOF */
if (C == EOF) {
/* Accept files without a newline at the end */
if (SB_NotEmpty (Line)) {
if (!Input->MissingNL || SB_NotEmpty (Line)) {
/* Accept files without a newline at the end */
++Input->Line;
break;
}
/* Leave the current file */
CloseIncludeFile ();
/* Assume no new line */
Input->MissingNL = 1;
/* If there is no file open, bail out, otherwise get the
** previous input file and start over.
*/
if (CollCount (&AFiles) == 0) {
return 0;
}
Input = CollLast (&AFiles);
continue;
break;
}
/* Assume no new line */
Input->MissingNL = 1;
/* Check for end of line */
if (C == '\n') {
@@ -497,6 +538,7 @@ int NextLine (void)
if (SB_LookAtLast (Line) == '\\') {
Line->Buf[Line->Len-1] = '\n';
} else {
Input->MissingNL = 0;
break;
}
@@ -517,6 +559,38 @@ int NextLine (void)
/* Create line information for this line */
UpdateLineInfo (Input->Input, Input->Line, Line);
/* Done */
return C != EOF || SB_NotEmpty (Line);
}
int PreprocessNextLine (void)
/* Get a line from opened input files and do preprocess. Returns 0 on end of
** main file.
*/
{
while (NextLine() == 0) {
/* If there is no input file open, bail out. Otherwise get the previous
** input file and start over.
*/
if (CollCount (&AFiles) == 0) {
return 0;
}
/* Leave the current file */
CloseIncludeFile ();
}
/* Do preprocess anyways */
Preprocess ();
/* Write it to the output file if in preprocess-only mode */
if (PreprocessOnly) {
WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
/* Done */
return 1;
}
@@ -536,17 +610,11 @@ const char* GetCurrentFile (void)
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
return AF->Input->Name;
const AFile* AF = CollLast (&AFiles);
return AF->PName == 0 ? AF->Input->Name : AF->PName;
} else {
/* No open file. Use the main file if we have one. */
unsigned IFileCount = CollCount (&IFiles);
if (IFileCount > 0) {
const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
return IF->Name;
} else {
return "(outside file scope)";
}
/* No open file */
return "(outside file scope)";
}
}
@@ -557,7 +625,7 @@ unsigned GetCurrentLine (void)
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
const AFile* AF = CollLast (&AFiles);
return AF->Line;
} else {
/* No open file */
@@ -567,6 +635,43 @@ unsigned GetCurrentLine (void)
void SetCurrentLine (unsigned LineNum)
/* Set the line number in the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
AFile* AF = CollLast (&AFiles);
AF->Line = LineNum;
}
}
void SetCurrentFilename (const char* Name)
/* Set the presumed name of the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
size_t Len = strlen (Name);
AFile* AF = CollLast (&AFiles);
if (AF->PName != 0) {
xfree (AF->PName);
}
AF->PName = xmalloc (Len + 1);
memcpy (AF->PName, Name, Len + 1);
}
}
unsigned GetCurrentCounter (void)
/* Return the counter number in the current input file */
{
return MainFileCounter++;
}
static void WriteEscaped (FILE* F, const char* Name)
/* Write a file name to a dependency file escaping spaces */
{

View File

@@ -46,7 +46,7 @@
/*****************************************************************************/
/* data */
/* Data */
/*****************************************************************************/
@@ -84,6 +84,11 @@ void OpenMainFile (const char* Name);
void OpenIncludeFile (const char* Name, InputType IT);
/* Open an include file and insert it into the tables. */
void CloseIncludeFile (void);
/* Close an include file and switch to the higher level file. Set Input to
** NULL if this was the main file.
*/
void NextChar (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
@@ -99,7 +104,14 @@ StrBuf* InitLine (StrBuf* Buf);
*/
int NextLine (void);
/* Get a line from the current input. Returns 0 on end of file. */
/* Get a line from the current input. Returns 0 on end of file with no new
** input bytes.
*/
int PreprocessNextLine (void);
/* Get a line from opened input files and do preprocess. Returns 0 on end of
** main file.
*/
const char* GetInputFile (const struct IFile* IF);
/* Return a filename from an IFile struct */
@@ -110,6 +122,15 @@ const char* GetCurrentFile (void);
unsigned GetCurrentLine (void);
/* Return the line number in the current input file */
void SetCurrentLine (unsigned LineNum);
/* Set the line number in the current input file */
void SetCurrentFilename (const char* Name);
/* Set the presumed name of the current input file */
unsigned GetCurrentCounter (void);
/* Return the counter number in the current input file */
void CreateDependencies (void);
/* Create dependency files requested by the user */

View File

@@ -126,9 +126,6 @@ static void FreeLiteral (Literal* L)
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_defliterallabel (L->Label);
@@ -387,9 +384,6 @@ static void OutputReadOnlyLiterals (Collection* Literals)
continue;
}
/* Translate the literal into the target charset */
TranslateLiteral (L);
/* Check if this literal is part of another one. Since the literals
** are sorted by size (larger ones first), it can only be part of a
** literal with a smaller index.

View File

@@ -277,4 +277,8 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr)
}
}
if (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)) {
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
}

View File

@@ -174,8 +174,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg)
Sym->Flags |= SC_REF;
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate a variable of unknown size */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {
@@ -370,8 +370,8 @@ static void ParseAutoDecl (Declaration* Decl)
}
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate an incomplete variable */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {
@@ -428,8 +428,8 @@ static void ParseStaticDecl (Declaration* Decl)
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate an incomplete variable */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {

View File

@@ -42,6 +42,7 @@
/* cc65 */
#include "error.h"
#include "preproc.h"
#include "macrotab.h"
@@ -56,6 +57,9 @@
#define MACRO_TAB_SIZE 211
static Macro* MacroTab[MACRO_TAB_SIZE];
/* The undefined macros list head */
static Macro* UndefinedMacrosListHead;
/*****************************************************************************/
@@ -108,6 +112,29 @@ void FreeMacro (Macro* M)
Macro* CloneMacro (const Macro* M)
/* Clone a macro definition. The function is not insert the macro into the
** macro table, thus the cloned instance cannot be freed with UndefineMacro.
** Use FreeMacro for that.
*/
{
Macro* New = NewMacro (M->Name);
unsigned I;
for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
/* Copy the argument */
const char* Arg = CollAtUnchecked (&M->FormalArgs, I);
CollAppend (&New->FormalArgs, xstrdup (Arg));
}
New->ArgCount = M->ArgCount;
New->Variadic = M->Variadic;
SB_Copy (&New->Replacement, &M->Replacement);
return New;
}
void DefineNumericMacro (const char* Name, long Val)
/* Define a macro for a numeric constant */
{
@@ -150,10 +177,11 @@ void InsertMacro (Macro* M)
int UndefineMacro (const char* Name)
/* Search for the macro with the given name and remove it from the macro
** table if it exists. Return 1 if a macro was found and deleted, return
** 0 otherwise.
Macro* UndefineMacro (const char* Name)
/* Search for the macro with the given name, if it exists, remove it from
** the defined macro table and insert it to a list for pending deletion.
** Return the macro if it was found and removed, return 0 otherwise.
** To safely free the removed macro, use FreeUndefinedMacros().
*/
{
/* Get the hash value of the macro name */
@@ -173,11 +201,12 @@ int UndefineMacro (const char* Name)
L->Next = M->Next;
}
/* Delete the macro */
FreeMacro (M);
/* Add this macro to pending deletion list */
M->Next = UndefinedMacrosListHead;
UndefinedMacrosListHead = M;
/* Done */
return 1;
return M;
}
/* Next macro */
@@ -191,6 +220,23 @@ int UndefineMacro (const char* Name)
void FreeUndefinedMacros (void)
/* Free all undefined macros */
{
Macro* Next;
while (UndefinedMacrosListHead != 0) {
Next = UndefinedMacrosListHead->Next;
/* Delete the macro */
FreeMacro (UndefinedMacrosListHead);
UndefinedMacrosListHead = Next;
}
}
Macro* FindMacro (const char* Name)
/* Find a macro with the given name. Return the macro definition or NULL */
{
@@ -201,6 +247,10 @@ Macro* FindMacro (const char* Name)
Macro* M = MacroTab[Hash];
while (M) {
if (strcmp (M->Name, Name) == 0) {
/* Check for some special macro names */
if (Name[0] == '_') {
HandleSpecialMacro (M, Name);
}
/* Found it */
return M;
}
@@ -245,7 +295,7 @@ void AddMacroArg (Macro* M, const char* Arg)
for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) {
/* Found */
Error ("Duplicate macro parameter: '%s'", Arg);
PPError ("Duplicate macro parameter: '%s'", Arg);
break;
}
}

View File

@@ -82,6 +82,12 @@ void FreeMacro (Macro* M);
** table, use UndefineMacro for that.
*/
Macro* CloneMacro (const Macro* M);
/* Clone a macro definition. The function is not insert the macro into the
** macro table, thus the cloned instance cannot be freed with UndefineMacro.
** Use FreeMacro for that.
*/
void DefineNumericMacro (const char* Name, long Val);
/* Define a macro for a numeric constant */
@@ -91,12 +97,16 @@ void DefineTextMacro (const char* Name, const char* Val);
void InsertMacro (Macro* M);
/* Insert the given macro into the macro table. */
int UndefineMacro (const char* Name);
/* Search for the macro with the given name and remove it from the macro
** table if it exists. Return 1 if a macro was found and deleted, return
** 0 otherwise.
Macro* UndefineMacro (const char* Name);
/* Search for the macro with the given name, if it exists, remove it from
** the defined macro table and insert it to a list for pending deletion.
** Return the macro if it was found and removed, return 0 otherwise.
** To safely free the removed macro, use FreeUndefinedMacros().
*/
void FreeUndefinedMacros (void);
/* Free all undefined macros */
Macro* FindMacro (const char* Name);
/* Find a macro with the given name. Return the macro definition or NULL */

878
src/cc65/ppexpr.c Normal file
View File

@@ -0,0 +1,878 @@
/*****************************************************************************/
/* */
/* ppexpr.h */
/* */
/* Expressions for C preprocessor */
/* */
/* */
/* */
/* (C) 2022 The cc65 Authors */
/* */
/* */
/* 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. */
/* */
/*****************************************************************************/
/* cc65 */
#include "error.h"
#include "scanner.h"
#include "ppexpr.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* PP expression parser status */
static int PPEvaluationEnabled = 0;
static int PPEvaluationFailed = 0;
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static void PPhie0 (PPExpr* Expr);
static void PPhie1 (PPExpr* Expr);
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static token_t PPFindTok (token_t Tok, const token_t* Table)
/* Find a token in a generator table */
{
while (*Table != TOK_INVALID) {
if (*Table == Tok) {
return Tok;
}
++Table;
}
return TOK_INVALID;
}
static void PPExprInit (PPExpr* Expr)
/* Initialize the expression */
{
Expr->IVal = 0;
Expr->Flags = PPEXPR_NONE;
}
static void PPErrorSkipLine (void)
/* Set the expression parser error flag, skip the remain tokens till the end
** of the line, clear the current and the next tokens.
*/
{
PPEvaluationFailed = 1;
SkipTokens (0, 0);
CurTok.Tok = TOK_CEOF;
NextTok.Tok = TOK_CEOF;
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static void PPhiePrimary (PPExpr* Expr)
/* This is the lowest level of the PP expression parser */
{
switch (CurTok.Tok) {
case TOK_ICONST:
case TOK_CCONST:
case TOK_WCCONST:
/* Character and integer constants */
Expr->IVal = CurTok.IVal;
/* According to the C standard, all signed types act as intmax_t
** and all unsigned types act as uintmax_t.
*/
if (IsSignUnsigned (CurTok.Type)) {
Expr->Flags |= PPEXPR_UNSIGNED;
}
NextToken ();
break;
case TOK_FCONST:
/* Floating point constant */
PPError ("Floating constant in preprocessor expression");
Expr->IVal = 0;
NextToken ();
break;
case TOK_LPAREN:
/* Parse parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
PPhie0 (Expr);
ConsumeRParen ();
break;
case TOK_IDENT:
/* Assume that this identifier is an undefined macro and replace
** it by a constant value of zero.
*/
NextToken ();
Expr->Flags |= PPEXPR_UNDEFINED;
Expr->IVal = 0;
break;
case TOK_CEOF:
/* Error recovery */
break;
default:
/* Illegal expression in PP mode */
PPError ("Preprocessor expression expected");
PPErrorSkipLine ();
break;
}
}
static void PPhie11 (PPExpr* Expr)
/* Handle compound types (structs and arrays) etc which are invalid in PP */
{
/* Evaluate the lhs */
PPhiePrimary (Expr);
/* Check for a rhs */
while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC ||
CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN ||
CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) {
switch (CurTok.Tok) {
case TOK_LBRACK:
PPError ("Token \".\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_LPAREN:
/* Function call syntax is not recognized in preprocessor
** expressions.
*/
PPError ("Missing binary operator before token \"(\"");
PPErrorSkipLine ();
break;
case TOK_DOT:
PPError ("Token \".\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PTR_REF:
PPError ("Token \"->\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_INC:
PPError ("Token \"++\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DEC:
PPError ("Token \"--\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
default:
Internal ("Invalid token in PPhie11: %d", CurTok.Tok);
}
}
/* Check for excessive expressions */
if (!TokIsPunc (&CurTok)) {
PPError ("Missing binary operator");
PPErrorSkipLine ();
}
}
void PPhie10 (PPExpr* Expr)
/* Handle prefixing unary operators */
{
switch (CurTok.Tok) {
case TOK_INC:
PPError ("Token \"++\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DEC:
PPError ("Token \"--\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PLUS:
NextToken ();
PPhie10 (Expr);
Expr->IVal = +Expr->IVal;
break;
case TOK_MINUS:
NextToken ();
PPhie10 (Expr);
Expr->IVal = -Expr->IVal;
break;
case TOK_COMP:
NextToken ();
PPhie10 (Expr);
Expr->IVal = ~Expr->IVal;
break;
case TOK_BOOL_NOT:
NextToken ();
PPhie10 (Expr);
Expr->IVal = !Expr->IVal;
break;
case TOK_CEOF:
/* Error recovery */
break;
case TOK_STAR:
case TOK_AND:
case TOK_SIZEOF:
default:
/* Type cast, sizeof, *, &, are not recognized in preprocessor
** expressions. So everything is treated as as expression here.
*/
PPhie11 (Expr);
break;
}
}
static void PPhie_internal (const token_t* Ops, /* List of generators */
PPExpr* Expr,
void (*hienext) (PPExpr*))
/* Helper function */
{
token_t Tok;
hienext (Expr);
while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) {
PPExpr Rhs;
PPExprInit (&Rhs);
/* Remember the operator token, then skip it */
NextToken ();
/* Get the right hand side */
hienext (&Rhs);
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* If either side is unsigned, the result is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
/* Handle the op differently for signed and unsigned integers */
if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_PLUS:
Expr->IVal = (Val1 + Val2);
break;
case TOK_MINUS:
Expr->IVal = (Val1 - Val2);
break;
case TOK_MUL:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
PPError ("Division by zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
PPError ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("PPhie_internal: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_PLUS:
Expr->IVal = (Val1 + Val2);
break;
case TOK_MINUS:
Expr->IVal = (Val1 - Val2);
break;
case TOK_MUL:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
PPError ("Division by zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
PPError ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("PPhie_internal: got token 0x%X\n", Tok);
}
}
}
}
}
static void PPhie_compare (const token_t* Ops, /* List of generators */
PPExpr* Expr,
void (*hienext) (PPExpr*))
/* Helper function for the compare operators */
{
token_t Tok;
hienext (Expr);
while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) {
PPExpr Rhs;
PPExprInit (&Rhs);
/* Skip the operator token */
NextToken ();
/* Get the right hand side */
hienext (&Rhs);
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* If either side is unsigned, the comparison is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
/* Determine if this is a signed or unsigned compare */
if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("PPhie_compare: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("PPhie_compare: got token 0x%X\n", Tok);
}
}
}
}
/* The result is signed */
Expr->Flags &= ~PPEXPR_UNSIGNED;
}
static void PPhie9 (PPExpr* Expr)
/* Handle "*", "/" and "%" operators */
{
static const token_t PPhie9_ops[] = {
TOK_STAR,
TOK_DIV,
TOK_MOD,
TOK_INVALID
};
PPhie_internal (PPhie9_ops, Expr, PPhie10);
}
static void PPhie8 (PPExpr* Expr)
/* Handle "+" and "-" binary operators */
{
static const token_t PPhie8_ops[] = {
TOK_PLUS,
TOK_MINUS,
TOK_INVALID
};
PPhie_internal (PPhie8_ops, Expr, PPhie9);
}
static void PPhie7 (PPExpr* Expr)
/* Handle the "<<" and ">>" shift operators */
{
/* Evaluate the lhs */
PPhie8 (Expr);
while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) {
token_t Op; /* The operator token */
PPExpr Rhs;
PPExprInit (&Rhs);
/* Remember the operator, then skip its token */
Op = CurTok.Tok;
NextToken ();
/* Get the right hand side */
PPhie8 (&Rhs);
/* Evaluate */
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* To shift by a negative value is equivalent to shift to the
** opposite direction.
*/
if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0 && Rhs.IVal > (long)LONG_BITS) {
Rhs.IVal = (long)LONG_BITS;
}
if (Op == TOK_SHR) {
Rhs.IVal = -Rhs.IVal;
}
/* Evaluate the result */
if ((Expr->Flags & PPEXPR_UNSIGNED) != 0) {
if (Rhs.IVal >= (long)LONG_BITS) {
/* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
} else if (Rhs.IVal < -(long)LONG_BITS) {
Expr->IVal = 0;
} else if (Rhs.IVal < 0) {
Expr->IVal = (unsigned long)Expr->IVal >> -Rhs.IVal;
}
} else {
if (Rhs.IVal >= (long)(LONG_BITS - 1)) {
/* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
} else if (Rhs.IVal < -(long)LONG_BITS) {
Expr->IVal = -1;
} else if (Rhs.IVal < 0) {
Expr->IVal >>= Expr->IVal >> -Rhs.IVal;
}
}
}
}
}
static void PPhie6 (PPExpr* Expr)
/* Handle greater-than type relational operators */
{
static const token_t PPhie6_ops [] = {
TOK_LT,
TOK_LE,
TOK_GE,
TOK_GT,
TOK_INVALID
};
PPhie_compare (PPhie6_ops, Expr, PPhie7);
}
static void PPhie5 (PPExpr* Expr)
/* Handle "==" and "!=" relational operators */
{
static const token_t PPhie5_ops[] = {
TOK_EQ,
TOK_NE,
TOK_INVALID
};
PPhie_compare (PPhie5_ops, Expr, PPhie6);
}
static void PPhie4 (PPExpr* Expr)
/* Handle the bitwise AND "&" operator */
{
static const token_t PPhie4_ops[] = {
TOK_AND,
TOK_INVALID
};
PPhie_internal (PPhie4_ops, Expr, PPhie5);
}
static void PPhie3 (PPExpr* Expr)
/* Handle the bitwise exclusive OR "^" operator */
{
static const token_t PPhie3_ops[] = {
TOK_XOR,
TOK_INVALID
};
PPhie_internal (PPhie3_ops, Expr, PPhie4);
}
static void PPhie2 (PPExpr* Expr)
/* Handle the bitwise OR "|" operator */
{
static const token_t PPhie2_ops[] = {
TOK_OR,
TOK_INVALID
};
PPhie_internal (PPhie2_ops, Expr, PPhie3);
}
static void PPhieAnd (PPExpr* Expr)
/* Handle the logical AND "expr1 && expr2" operator */
{
/* Get one operand */
PPhie2 (Expr);
if (CurTok.Tok == TOK_BOOL_AND) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr One;
/* Do logical and */
Expr->IVal = (Expr->IVal != 0);
if (Expr->IVal == 0) {
PPEvaluationEnabled = 0;
}
/* While there are more expressions */
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get one operand */
PPExprInit (&One);
PPhie2 (&One);
/* Evaluate */
if (PPEvaluationEnabled) {
if (One.IVal == 0) {
/* Skip evaluating remaining */
PPEvaluationEnabled = 0;
/* The value of the result will be false */
Expr->IVal = 0;
}
}
}
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhieOr (PPExpr* Expr)
/* Handle the logical OR "||" operator */
{
/* Call the next level parser */
PPhieAnd (Expr);
if (CurTok.Tok == TOK_BOOL_OR) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr One;
/* Do logical or */
Expr->IVal = (Expr->IVal != 0);
if (Expr->IVal != 0) {
PPEvaluationEnabled = 0;
}
/* While there are more expressions */
while (CurTok.Tok == TOK_BOOL_OR) {
/* Skip the || */
NextToken ();
/* Get rhs subexpression */
PPExprInit (&One);
PPhieAnd (&One);
/* Evaluate */
if (PPEvaluationEnabled) {
if (One.IVal != 0) {
/* Skip evaluating remaining */
PPEvaluationEnabled = 0;
/* The value of the result will be true */
Expr->IVal = 1;
}
}
}
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhieQuest (PPExpr* Expr)
/* Handle the ternary "expr1 ? expr2 : expr3 " operator */
{
/* Call the lower level eval routine */
PPhieOr (Expr);
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr Expr2; /* Expression 2 */
PPExpr Expr3; /* Expression 3 */
/* Skip the question mark */
NextToken ();
/* Disable evaluation for Expr2 if the condition is false */
if (Expr->IVal == 0) {
PPEvaluationEnabled = 0;
}
/* Parse second expression */
PPExprInit (&Expr2);
PPhie0 (&Expr2);
/* Skip the colon */
ConsumeColon ();
/* Disable evaluation for Expr3 if the condition is true */
if (Expr->IVal != 0) {
PPEvaluationEnabled = 0;
}
/* Parse third expression */
PPExprInit (&Expr3);
PPhie1 (&Expr3);
/* Set the result */
Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0;
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhie1 (PPExpr* Expr)
/* Handle first level of expression hierarchy */
{
PPhieQuest (Expr);
if (!PPEvaluationEnabled) {
/* Skip evaluation */
return;
}
switch (CurTok.Tok) {
case TOK_ASSIGN:
PPError ("Token \"=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PLUS_ASSIGN:
PPError ("Token \"+=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MINUS_ASSIGN:
PPError ("Token \"-=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MUL_ASSIGN:
PPError ("Token \"*=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DIV_ASSIGN:
PPError ("Token \"/=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MOD_ASSIGN:
PPError ("Token \"%%=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_SHL_ASSIGN:
PPError ("Token \"<<=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_SHR_ASSIGN:
PPError ("Token \">>=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_AND_ASSIGN:
PPError ("Token \"&=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_OR_ASSIGN:
PPError ("Token \"|=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_XOR_ASSIGN:
PPError ("Token \"^=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
default:
break;
}
}
static void PPhie0 (PPExpr* Expr)
/* Handle the comma "," operator */
{
PPhie1 (Expr);
while (CurTok.Tok == TOK_COMMA) {
/* Skip the comma */
NextToken ();
/* Reset the expression */
PPExprInit (Expr);
/* Use the next operand as the value instead */
PPhie1 (Expr);
}
}
void ParsePPExprInLine (PPExpr* Expr)
/* Parse a line for PP expression */
{
/* Initialize the parser status */
PPEvaluationFailed = 0;
PPEvaluationEnabled = 1;
PPParserRunning = 1;
/* Parse */
PPExprInit (Expr);
PPhie0 (Expr);
/* If the evaluation fails, the result is always zero */
if (PPEvaluationFailed) {
Expr->IVal = 0;
PPEvaluationFailed = 0;
}
/* Restore parser status */
PPParserRunning = 0;
}

76
src/cc65/ppexpr.h Normal file
View File

@@ -0,0 +1,76 @@
/*****************************************************************************/
/* */
/* ppexpr.h */
/* */
/* Expressions for C preprocessor */
/* */
/* */
/* */
/* (C) 2022 The cc65 Authors */
/* */
/* */
/* 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. */
/* */
/*****************************************************************************/
#ifndef PPEXPR_H
#define PPEXPR_H
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* PPExpr data struct */
typedef struct PPExpr PPExpr;
struct PPExpr
{
long IVal;
unsigned Flags;
};
/* PPExpr initializers */
#define AUTO_PPEXPR_INITIALIZER { 0, 0 }
#define STATIC_PPEXPR_INITIALIZER { 0, 0 }
/* PPExpr flags */
#define PPEXPR_NONE 0U
#define PPEXPR_UNSIGNED 1U
#define PPEXPR_UNDEFINED 2U
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void ParsePPExprInLine (PPExpr* Expr);
/* Parse a line for PP expression */
/* End of ppexpr.h */
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -39,18 +39,35 @@
/*****************************************************************************/
/* data */
/* Forwards */
/*****************************************************************************/
/* Set when the preprocessor calls NoCodeConstExpr() recursively */
extern unsigned char Preprocessing;
typedef struct Macro Macro;
/*****************************************************************************/
/* code */
/* Data */
/*****************************************************************************/
/* Maximum #if depth per file */
#define MAX_PP_IFS 256
/* Data struct used for per-file-directive handling */
typedef struct PPIfStack PPIfStack;
struct PPIfStack {
unsigned char Stack[MAX_PP_IFS];
int Index;
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@@ -58,6 +75,24 @@ extern unsigned char Preprocessing;
void Preprocess (void);
/* Preprocess a line */
void SetPPIfStack (PPIfStack* Stack);
/* Specify which PP #if stack to use */
void PreprocessBegin (void);
/* Initialize preprocessor with current file */
void PreprocessEnd (void);
/* Preprocessor done with current file */
void InitPreprocess (void);
/* Init preprocessor */
void DonePreprocess (void);
/* Done with preprocessor */
void HandleSpecialMacro (Macro* M, const char* Name);
/* Handle special "magic" macros that may change */
/* End of preproc.h */

View File

@@ -69,6 +69,7 @@
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
@@ -188,10 +189,12 @@ static int SkipWhite (void)
{
while (1) {
while (CurC == '\0') {
if (NextLine () == 0) {
/* If reading next line fails or is disabled with directives, bail
** out.
*/
if (PPParserRunning || PreprocessNextLine () == 0) {
return 0;
}
Preprocess ();
}
if (IsSpace (CurC)) {
NextChar ();
@@ -245,6 +248,45 @@ int IsSym (char* S)
int IsPPNumber (int Cur, int Next)
/* Return 1 if the two successive characters indicate a pp-number, otherwise
** return 0.
*/
{
return Cur != '.' ? IsDigit (Cur) : IsDigit (Next);
}
void CopyPPNumber (StrBuf* Target)
/* Copy a pp-number from the input to Target */
{
int Std;
if (!IsPPNumber (CurC, NextC)) {
return;
}
/* P-exp is only valid in C99 and later */
Std = IS_Get (&Standard);
while (IsIdent (CurC) || IsDigit (CurC) || CurC == '.') {
SB_AppendChar (Target, CurC);
if (NextC == '+' || NextC == '-') {
if (CurC == 'e' || CurC == 'E' ||
(Std >= STD_C99 && (CurC == 'p' || CurC == 'P'))) {
SB_AppendChar (Target, NextC);
NextChar ();
} else {
NextChar ();
break;
}
}
NextChar ();
}
}
static void UnknownChar (char C)
/* Error message for unknown character */
{
@@ -370,6 +412,15 @@ static void CharConst (void)
{
int C;
if (CurC == 'L') {
/* Wide character constant */
NextTok.Tok = TOK_WCCONST;
NextChar ();
} else {
/* Narrow character constant */
NextTok.Tok = TOK_CCONST;
}
/* Skip the quote */
NextChar ();
@@ -384,9 +435,6 @@ static void CharConst (void)
NextChar ();
}
/* Setup values and attributes */
NextTok.Tok = TOK_CCONST;
/* Translate into target charset */
NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
@@ -457,76 +505,77 @@ static void StringConst (void)
static void NumericConst (void)
/* Parse a numeric constant */
{
unsigned Base; /* Temporary number base */
unsigned Prefix; /* Base according to prefix */
StrBuf S = STATIC_STRBUF_INITIALIZER;
unsigned Base; /* Temporary number base according to prefix */
unsigned Index;
StrBuf Src = AUTO_STRBUF_INITIALIZER;
int IsFloat;
char C;
unsigned DigitVal;
unsigned long IVal; /* Value */
/* Get the pp-number first, then parse on it */
CopyPPNumber (&Src);
SB_Terminate (&Src);
SB_Reset (&Src);
/* Check for a leading hex, octal or binary prefix and determine the
** possible integer types.
*/
if (CurC == '0') {
if (SB_Peek (&Src) == '0') {
/* Gobble 0 and examine next char */
NextChar ();
if (toupper (CurC) == 'X') {
Base = Prefix = 16;
NextChar (); /* gobble "x" */
} else if (toupper (CurC) == 'B' && IS_Get (&Standard) >= STD_CC65) {
Base = Prefix = 2;
NextChar (); /* gobble 'b' */
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) == 'X' &&
IsXDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) {
Base = 16;
SB_Skip (&Src); /* gobble "x" */
} else if (toupper (SB_Peek (&Src)) == 'B' &&
IS_Get (&Standard) >= STD_CC65 &&
IsDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) {
Base = 2;
SB_Skip (&Src); /* gobble 'b' */
} else {
Base = 10; /* Assume 10 for now - see below */
Prefix = 8; /* Actual prefix says octal */
}
} else {
Base = Prefix = 10;
Base = 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.
/* 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 ();
Index = SB_GetIndex (&Src);
while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) {
SB_Skip (&Src);
}
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));
IsFloat = (C == '.' ||
(Base == 10 && toupper (C) == 'E') ||
(Base == 16 && toupper (C) == '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) {
/* An octal prefix for an integer type results in an octal base */
if (!IsFloat && Base == 10 && SB_LookAt (&Src, 0) == '0') {
Base = 8;
}
/* Since we do now know the correct base, convert the remembered input
** into a number.
*/
SB_Reset (&S);
/* Since we now know the correct base, convert the input into a number */
SB_SetIndex (&Src, Index);
IVal = 0;
while ((C = SB_Get (&S)) != '\0') {
while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) {
DigitVal = HexVal (C);
if (DigitVal >= Base) {
Error ("Numeric constant contains digits beyond the radix");
Error ("Invalid digit \"%c\" beyond radix %u constant", C, Base);
SB_Clear (&Src);
break;
}
IVal = (IVal * Base) + DigitVal;
SB_Skip (&Src);
}
/* We don't need the string buffer any longer */
SB_Done (&S);
/* Distinguish between integer and floating point constants */
if (!IsFloat) {
@@ -537,27 +586,32 @@ static void NumericConst (void)
** possible to convert the data to unsigned long even if the IT_ULONG
** flag were not set, but we are not doing that.
*/
if (toupper (CurC) == 'U') {
if (toupper (SB_Peek (&Src)) == 'U') {
/* Unsigned type */
NextChar ();
if (toupper (CurC) != 'L') {
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) != 'L') {
Types = IT_UINT | IT_ULONG;
} else {
NextChar ();
SB_Skip (&Src);
Types = IT_ULONG;
}
} else if (toupper (CurC) == 'L') {
} else if (toupper (SB_Peek (&Src)) == 'L') {
/* Long type */
NextChar ();
if (toupper (CurC) != 'U') {
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) != 'U') {
Types = IT_LONG | IT_ULONG;
WarnTypes = IT_ULONG;
} else {
NextChar ();
SB_Skip (&Src);
Types = IT_ULONG;
}
} else {
if (Prefix == 10) {
if (SB_Peek (&Src) != '\0') {
Error ("Invalid suffix \"%s\" on integer constant",
SB_GetConstBuf (&Src) + SB_GetIndex (&Src));
}
if (Base == 10) {
/* Decimal constants are of any type but uint */
Types = IT_INT | IT_LONG | IT_ULONG;
WarnTypes = IT_LONG | IT_ULONG;
@@ -621,16 +675,16 @@ static void NumericConst (void)
Double FVal = FP_D_FromInt (IVal); /* Convert to double */
/* Check for a fractional part and read it */
if (CurC == '.') {
if (SB_Peek (&Src) == '.') {
Double Scale;
/* Skip the dot */
NextChar ();
SB_Skip (&Src);
/* Read fractional digits */
Scale = FP_D_Make (1.0);
while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) {
while (IsXDigit (SB_Peek (&Src)) && (DigitVal = HexVal (SB_Peek (&Src))) < Base) {
/* Get the value of this digit */
Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale);
/* Add it to the float value */
@@ -638,25 +692,25 @@ static void NumericConst (void)
/* Scale base */
Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal));
/* Skip the digit */
NextChar ();
SB_Skip (&Src);
}
}
/* Check for an exponent and read it */
if ((Base == 16 && toupper (CurC) == 'F') ||
(Base == 10 && toupper (CurC) == 'E')) {
if ((Base == 16 && toupper (SB_Peek (&Src)) == 'P') ||
(Base == 10 && toupper (SB_Peek (&Src)) == 'E')) {
unsigned Digits;
unsigned Exp;
/* Skip the exponent notifier */
NextChar ();
SB_Skip (&Src);
/* Read an optional sign */
if (CurC == '-') {
NextChar ();
} else if (CurC == '+') {
NextChar ();
if (SB_Peek (&Src) == '-') {
SB_Skip (&Src);
} else if (SB_Peek (&Src) == '+') {
SB_Skip (&Src);
}
/* Read exponent digits. Since we support only 32 bit floats
@@ -667,11 +721,11 @@ static void NumericConst (void)
*/
Digits = 0;
Exp = 0;
while (IsDigit (CurC)) {
while (IsDigit (SB_Peek (&Src))) {
if (++Digits <= 3) {
Exp = Exp * 10 + HexVal (CurC);
Exp = Exp * 10 + HexVal (SB_Peek (&Src));
}
NextChar ();
SB_Skip (&Src);
}
/* Check for errors: We must have exponent digits, and not more
@@ -690,10 +744,14 @@ static void NumericConst (void)
}
/* Check for a suffix and determine the type of the constant */
if (toupper (CurC) == 'F') {
NextChar ();
if (toupper (SB_Peek (&Src)) == 'F') {
SB_Skip (&Src);
NextTok.Type = type_float;
} else {
if (SB_Peek (&Src) != '\0') {
Error ("Invalid suffix \"%s\" on floating constant",
SB_GetConstBuf (&Src) + SB_GetIndex (&Src));
}
NextTok.Type = type_double;
}
@@ -702,6 +760,9 @@ static void NumericConst (void)
NextTok.Tok = TOK_FCONST;
}
/* We don't need the string buffer any longer */
SB_Done (&Src);
}
@@ -743,39 +804,38 @@ void NextToken (void)
}
/* Determine the next token from the lookahead */
if (IsDigit (CurC) || (CurC == '.' && IsDigit (NextC))) {
if (IsPPNumber (CurC, NextC)) {
/* A number */
NumericConst ();
return;
}
/* Check for wide character literals */
if (CurC == 'L' && NextC == '\"') {
StringConst ();
return;
/* Check for wide character constants and literals */
if (CurC == 'L') {
if (NextC == '\"') {
StringConst ();
return;
} else if (NextC == '\'') {
CharConst ();
return;
}
}
/* Check for keywords and identifiers */
if (IsSym (token)) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
/* Reserved word found */
return;
if (!PPParserRunning) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
/* Reserved word found */
return;
}
}
/* No reserved word, check for special symbols */
if (token[0] == '_' && token[1] == '_') {
/* Special symbols */
if (strcmp (token+2, "FILE__") == 0) {
NextTok.SVal = AddLiteral (GetCurrentFile());
NextTok.Tok = TOK_SCONST;
return;
} else if (strcmp (token+2, "LINE__") == 0) {
NextTok.Tok = TOK_ICONST;
NextTok.IVal = GetCurrentLine();
NextTok.Type = type_int;
return;
} else if (strcmp (token+2, "func__") == 0) {
if (strcmp (token+2, "func__") == 0) {
/* __func__ is only defined in functions */
if (CurrentFunc) {
NextTok.SVal = AddLiteral (F_GetFuncName (CurrentFunc));
@@ -1011,6 +1071,15 @@ void NextToken (void)
SetTok (TOK_COMP);
break;
case '#':
NextChar ();
if (CurC == '#') {
SetTok (TOK_DOUBLE_HASH);
} else {
NextTok.Tok = TOK_HASH;
}
break;
default:
UnknownChar (CurC);

View File

@@ -79,6 +79,10 @@ typedef enum token_t {
TOK_FASTCALL,
TOK_CDECL,
/* Address sizes */
TOK_FAR,
TOK_NEAR,
/* Tokens denoting types */
TOK_FIRST_TYPE,
TOK_ENUM = TOK_FIRST_TYPE,
@@ -95,94 +99,101 @@ typedef enum token_t {
TOK_VOID,
TOK_LAST_TYPE = TOK_VOID,
/* Control statements */
/* Selection statements */
TOK_IF,
TOK_ELSE,
TOK_SWITCH,
/* Iteration statements */
TOK_WHILE,
TOK_DO,
TOK_FOR,
TOK_GOTO,
TOK_IF,
TOK_RETURN,
TOK_SWITCH,
TOK_WHILE,
TOK_ASM,
/* Jump statements */
TOK_GOTO,
TOK_CONTINUE,
TOK_BREAK,
TOK_RETURN,
/* Labels */
TOK_CASE,
TOK_DEFAULT,
TOK_BREAK,
TOK_CONTINUE,
TOK_ELSE,
TOK_ELLIPSIS,
/* Misc. */
TOK_ATTRIBUTE,
TOK_PRAGMA,
TOK_STATIC_ASSERT,
TOK_ASM,
TOK_SIZEOF,
TOK_IDENT,
TOK_SEMI,
/* Primary operators */
TOK_LBRACK,
/* Punctuators */
TOK_FIRST_PUNC,
TOK_LBRACK = TOK_FIRST_PUNC,
TOK_RBRACK,
TOK_LPAREN,
TOK_RPAREN,
TOK_LCURLY,
TOK_RCURLY,
TOK_DOT,
TOK_PTR_REF,
TOK_LCURLY,
TOK_RBRACK,
TOK_COMP,
TOK_INC,
TOK_PLUS_ASSIGN,
TOK_PLUS,
TOK_COMMA,
TOK_DEC,
TOK_MINUS_ASSIGN,
TOK_RCURLY,
TOK_MINUS,
TOK_MUL_ASSIGN,
TOK_ADDR,
TOK_AND = TOK_ADDR, /* Alias */
TOK_STAR,
TOK_MUL = TOK_STAR, /* Alias */
TOK_DIV_ASSIGN,
TOK_DIV,
TOK_BOOL_AND,
TOK_AND_ASSIGN,
TOK_AND,
TOK_NE,
TOK_PLUS,
TOK_MINUS,
TOK_COMP,
TOK_BOOL_NOT,
TOK_BOOL_OR,
TOK_OR_ASSIGN,
TOK_OR,
TOK_EQ,
TOK_ASSIGN,
/* Inequalities */
TOK_LE,
TOK_LT,
TOK_GE,
TOK_GT,
TOK_SHL_ASSIGN,
TOK_SHL,
TOK_SHR_ASSIGN,
TOK_SHR,
TOK_XOR_ASSIGN,
TOK_XOR,
TOK_MOD_ASSIGN,
TOK_DIV,
TOK_MOD,
TOK_SHL,
TOK_SHR,
TOK_LT,
TOK_GT,
TOK_LE,
TOK_GE,
TOK_EQ,
TOK_NE,
TOK_XOR,
TOK_OR,
TOK_BOOL_AND,
TOK_BOOL_OR,
TOK_QUEST,
TOK_COLON,
TOK_RPAREN,
TOK_SCONST,
TOK_SEMI,
TOK_ELLIPSIS,
TOK_ASSIGN,
TOK_MUL_ASSIGN,
TOK_DIV_ASSIGN,
TOK_MOD_ASSIGN,
TOK_PLUS_ASSIGN,
TOK_MINUS_ASSIGN,
TOK_SHL_ASSIGN,
TOK_SHR_ASSIGN,
TOK_AND_ASSIGN,
TOK_XOR_ASSIGN,
TOK_OR_ASSIGN,
TOK_COMMA,
TOK_HASH,
TOK_HASH_HASH,
TOK_DOUBLE_HASH = TOK_HASH_HASH, /* Alias */
TOK_LAST_PUNC = TOK_DOUBLE_HASH,
/* Primary expressions */
TOK_ICONST,
TOK_CCONST,
TOK_WCCONST,
TOK_FCONST,
TOK_SCONST,
TOK_WCSCONST,
TOK_ATTRIBUTE,
TOK_STATIC_ASSERT,
TOK_FAR,
TOK_NEAR,
TOK_IDENT,
TOK_A,
TOK_X,
TOK_Y,
TOK_AX,
TOK_EAX,
TOK_PRAGMA
TOK_EAX
} token_t;
@@ -210,6 +221,7 @@ struct Token {
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
@@ -219,6 +231,17 @@ extern Token NextTok; /* The next token */
#if defined(HAVE_INLINE)
INLINE int TokIsPunc (const Token* T)
/* Return true if the token is a punctuator */
{
return (T->Tok >= TOK_FIRST_PUNC && T->Tok <= TOK_LAST_PUNC);
}
#else
# define TokIsPunc(T) \
((T)->Tok >= TOK_FIRST_PUNC && (T)->Tok <= TOK_LAST_PUNC)
#endif
#if defined(HAVE_INLINE)
INLINE int TokIsStorageClass (const Token* T)
/* Return true if the token is a storage class specifier */
@@ -262,6 +285,14 @@ void SymName (char* S);
int IsSym (char* S);
/* If a symbol follows, read it and return 1, otherwise return 0 */
int IsPPNumber (int Cur, int Next);
/* Return 1 if the two successive characters indicate a pp-number, otherwise
** return 0.
*/
void CopyPPNumber (StrBuf* Target);
/* Copy a pp-number from the input to Target */
void NextToken (void);
/* Get next token from input stream */

View File

@@ -237,5 +237,8 @@ MakeRVal:
/* Set the type of the result */
Expr->Type = ResultType;
/* Propagate from subexpressions */
Expr->Flags |= Expr2.Flags & E_MASK_VIRAL;
}
}

View File

@@ -185,6 +185,9 @@ static void ParseArg (ArgDesc* Arg, const Type* Type, ExprDesc* Expr)
/* Use the type of the argument for the push */
Arg->Flags |= TypeOf (Arg->Expr.Type);
/* Propagate from subexpressions */
Expr->Flags |= Arg->Expr.Flags & E_MASK_VIRAL;
}
@@ -1365,6 +1368,9 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
ExitPoint:
/* We expect the closing brace */
ConsumeRParen ();
/* Propagate from subexpressions */
Expr->Flags |= Arg.Flags & E_MASK_VIRAL;
}
@@ -1405,4 +1411,7 @@ void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
/* Call the handler function */
D->Handler (F, lval);
/* We assume all function calls had side effects */
lval->Flags |= E_SIDE_EFFECTS;
}

View File

@@ -609,17 +609,23 @@ static void Statement (int* PendingToken)
Expr.Flags |= E_NEED_NONE;
Expression0 (&Expr);
/* If the statement didn't generate code, and is not of type
** void, emit a warning.
/* If the statement has no observable effect and isn't cast to type
** void, emit a warning and remove useless code if any.
*/
GetCodePos (&End);
if (!ED_YetToLoad (&Expr) &&
!ED_MayHaveNoEffect (&Expr) &&
CodeRangeIsEmpty (&Start, &End) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Expression result unused");
if (CodeRangeIsEmpty (&Start, &End) ||
(Expr.Flags & E_SIDE_EFFECTS) == 0) {
if (!ED_MayHaveNoEffect (&Expr) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Statement has no effect");
}
/* Remove code with no effect */
RemoveCode (&Start);
}
CheckSemi (PendingToken);
}

View File

@@ -206,7 +206,7 @@ void SwitchStatement (void)
void CaseLabel (void)
/* Handle a case sabel */
/* Handle a case label */
{
ExprDesc CaseExpr; /* Case label expression */
long Val; /* Case label value */

View File

@@ -728,6 +728,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
/* New type must be compatible with the composite prototype */
if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
Error ("Conflicting function types for '%s'", Entry->Name);
TypeCompatibilityDiagnostic (T, E_Type, 0, "'%s' vs '%s'");
Entry = 0;
} else {
/* Refine the existing composite prototype with this new

View File

@@ -143,10 +143,10 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType)
/* Do the integer constant <-> absolute address conversion if necessary */
if (IsClassPtr (NewType)) {
Expr->Flags &= ~E_LOC_NONE;
Expr->Flags &= ~E_MASK_LOC;
Expr->Flags |= E_LOC_ABS | E_ADDRESS_OF;
} else if (IsClassInt (NewType)) {
Expr->Flags &= ~(E_LOC_ABS | E_ADDRESS_OF);
Expr->Flags &= ~(E_MASK_LOC | E_ADDRESS_OF);
Expr->Flags |= E_LOC_NONE;
}

View File

@@ -776,14 +776,20 @@ static void PrintUnresolved (ExpCheckFunc F, void* Data)
Import* Imp = E->ImpList;
const char* name = GetString (E->Name);
while (Imp) {
unsigned J;
for (J = 0; J < CollCount (&Imp->RefLines); ++J) {
const LineInfo* LI = CollConstAt (&Imp->RefLines, J);
fprintf (stderr,
"%s:%u: Error: Unresolved external '%s'\n",
GetSourceName (LI),
GetSourceLine (LI),
name);
unsigned J, count = CollCount (&Imp->RefLines);
/* The count is 0 when the import was not added by an input file,
but by the compiler itself. */
if (count == 0) {
fprintf (stderr, "Error: Unresolved external '%s'\n", name);
} else {
for (J = 0; J < count; ++J) {
const LineInfo* LI = CollConstAt (&Imp->RefLines, J);
fprintf (stderr,
"%s:%u: Error: Unresolved external '%s'\n",
GetSourceName (LI),
GetSourceLine (LI),
name);
}
}
Imp = Imp->Next;
}