From 0063f73f8a4a80df6e271aaadd9fc2204e0c900a Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 24 Jul 2022 23:19:05 +0800 Subject: [PATCH 1/3] Fixed __FILE__ and __LINE__ macros for preprocessor. --- src/cc65/compile.c | 4 ++++ src/cc65/macrotab.c | 5 +++++ src/cc65/preproc.c | 18 ++++++++++++++++++ src/cc65/preproc.h | 13 +++++++++++++ src/cc65/scanner.c | 11 +---------- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 75250da0a..d6069e914 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -396,6 +396,10 @@ void Compile (const char* FileName) DefineNumericMacro ("__EAGERLY_INLINE_FUNCS__", 1); } + /* Placeholders for __FILE__ and __LINE__ macros */ + DefineTextMacro ("__FILE__", ""); + DefineTextMacro ("__LINE__", ""); + /* __TIME__ and __DATE__ macros */ Time = time (0); TM = localtime (&Time); diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c index 37b52351f..0e80cd638 100644 --- a/src/cc65/macrotab.c +++ b/src/cc65/macrotab.c @@ -42,6 +42,7 @@ /* cc65 */ #include "error.h" +#include "preproc.h" #include "macrotab.h" @@ -246,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; } diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index f1328c4d0..6dcbeea70 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1533,6 +1533,24 @@ static int ParseDirectives (unsigned ModeFlags) +void HandleSpecialMacro (Macro* M, const char* Name) +/* Handle special mandatory macros */ +{ + if (strcmp (Name, "__LINE__") == 0) { + /* Replace __LINE__ with the current line number */ + SB_Printf (&M->Replacement, "%u", GetCurrentLine ()); + } else if (strcmp (Name, "__FILE__") == 0) { + /* Replace __FILE__ with the current filename */ + StrBuf B = AUTO_STRBUF_INITIALIZER; + SB_InitFromString (&B, GetCurrentFile ()); + SB_Clear (&M->Replacement); + Stringize (&B, &M->Replacement); + SB_Done (&B); + } +} + + + /*****************************************************************************/ /* Preprocessing */ /*****************************************************************************/ diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index f543b05b5..2143dde98 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -38,6 +38,16 @@ +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +typedef struct Macro Macro; + + + /*****************************************************************************/ /* Data */ /*****************************************************************************/ @@ -80,6 +90,9 @@ void InitPreprocess (void); void DonePreprocess (void); /* Done with preprocessor */ +void HandleSpecialMacro (Macro* M, const char* Name); +/* Handle special mandatory macros */ + /* End of preproc.h */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index ebdcdb33e..09dd8fef8 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -835,16 +835,7 @@ void NextToken (void) /* 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)); From bb9c98f4c9ce005b5becc7d47f5b38ccc9500e91 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 24 Jul 2022 23:19:05 +0800 Subject: [PATCH 2/3] Added support for the #line directive. --- src/cc65/input.c | 40 ++++++++++++++++++-- src/cc65/input.h | 6 +++ src/cc65/preproc.c | 93 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/src/cc65/input.c b/src/cc65/input.c index 18441f5c8..c6037091f 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -92,6 +92,7 @@ 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 */ }; @@ -159,6 +160,7 @@ 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; @@ -209,6 +211,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); } @@ -599,8 +604,8 @@ 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 */ return "(outside file scope)"; @@ -614,7 +619,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 */ @@ -624,6 +629,35 @@ 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); + } +} + + + static void WriteEscaped (FILE* F, const char* Name) /* Write a file name to a dependency file escaping spaces */ { diff --git a/src/cc65/input.h b/src/cc65/input.h index 779217b9b..cf529e169 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -122,6 +122,12 @@ 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 */ + void CreateDependencies (void); /* Create dependency files requested by the user */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 6dcbeea70..3b73ee493 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -57,6 +57,7 @@ #include "ppexpr.h" #include "preproc.h" #include "scanner.h" +#include "scanstrbuf.h" #include "standard.h" @@ -1294,6 +1295,95 @@ Done: +static unsigned GetLineDirectiveNum (void) +/* Get a decimal digit-sequence from the input. Return 0 on errors. */ +{ + unsigned long Num = 0; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + /* The only non-decimal-numeric character allowed in the digit-sequence is + ** the digit separator '\'' as of C23, but we haven't supported it yet. + */ + SkipWhitespace (0); + while (IsDigit (CurC)) + { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + + /* Ensure the buffer is terminated with a '\0' */ + SB_Terminate (&Buf); + if (SkipWhitespace (0) != 0 || CurC == '\0') { + const char* Str = SB_GetConstBuf (&Buf); + if (Str[0] == '\0') { + PPWarning ("#line directive interprets number as decimal, not octal"); + } else { + Num = strtoul (Str, 0, 10); + if (Num > 2147483647) { + PPError ("#line directive requires an integer argument not greater than 2147483647"); + ClearLine (); + Num = 0; + } else if (Num == 0) { + PPError ("#line directive requires a positive integer argument"); + ClearLine (); + } + } + } else { + PPError ("#line directive requires a simple decimal digit sequence"); + ClearLine (); + } + + /* Done with the buffer */ + SB_Done (&Buf); + + return (unsigned)Num; +} + + + +static void DoLine (void) +/* Process #line directive */ +{ + unsigned LineNum; + + /* Macro-replace a single line with support for the "defined" operator */ + SB_Clear (MLine); + PreprocessDirective (Line, MLine, MSM_NONE); + + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); + + /* Parse and check the specified line number */ + LineNum = GetLineDirectiveNum (); + if (LineNum != 0) { + /* Parse and check the optional filename argument */ + if (SB_GetIndex (Line) < SB_GetLen (Line)) { + StrBuf Filename = AUTO_STRBUF_INITIALIZER; + if (SB_GetString (Line, &Filename)) { + SB_Terminate (&Filename); + SetCurrentFilename (SB_GetConstBuf (&Filename)); + } else { + PPError ("Invalid filename for #line directive"); + LineNum = 0; + } + SB_Done (&Filename); + } + + /* #line actually sets the line number of the next line */ + if (LineNum > 0) { + SetCurrentLine (LineNum - 1); + /* Check for extra tokens at the end */ + CheckExtraTokens ("line"); + } + } + + /* Restore input source */ + MLine = InitLine (MLine); +} + + + static void DoPragma (void) /* Handle a #pragma line by converting the #pragma preprocessor directive into ** the _Pragma() compiler operator. @@ -1476,9 +1566,8 @@ static int ParseDirectives (unsigned ModeFlags) break; case PPD_LINE: - /* Should do something in C99 at least, but we ignore it */ if (!PPSkip) { - ClearLine (); + DoLine (); } break; From 1dbc5cb325c487d50242051029f508b266771602 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 24 Aug 2022 16:39:44 +0800 Subject: [PATCH 3/3] Simple testcase for __LINE__, __FILE__ as well as #line. --- test/val/bug1573.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ test/val/bug1573.h | 14 ++++++++ 2 files changed, 95 insertions(+) create mode 100644 test/val/bug1573.c create mode 100644 test/val/bug1573.h diff --git a/test/val/bug1573.c b/test/val/bug1573.c new file mode 100644 index 000000000..47172c398 --- /dev/null +++ b/test/val/bug1573.c @@ -0,0 +1,81 @@ +/* Tests for predefined macros __LINE__ and __FILE__ as well as #line control */ + +#include +#include + +static int failures = 0; + +#if !defined __LINE__ +#error __LINE__ is not predefined! +#endif + +#if !defined __FILE__ +#error __FILE__ is not predefined! +#endif + +#define CONCAT(a,b) CONCAT_impl_(a,b) +#define CONCAT_impl_(a,b) a##b +#define MKSTR(a) MKSTR_impl_(a) +#define MKSTR_impl_(a) #a +char CONCAT(ident,__LINE__)[0+__LINE__]; +char CONCAT(ident,__LINE__)[0+__LINE__]; + +#define GET_FILE() __FILE__ +#define THIS_FILENAME_1 "bug1573.c" +#define THIS_FILENAME_2 "<>" +#define INC_FILENAME_1 "bug1573.h" +#define INC_FILENAME_2 "<>" + +#line __LINE__ THIS_FILENAME_1 /* Note: #line sets the line number of the NEXT line */ +void foo(void) +{ + if (strcmp (GET_FILE(), THIS_FILENAME_1) != 0) { + printf("Expected: %s, got: %s\n", THIS_FILENAME_1, GET_FILE()); + ++failures; + } +} + +#line __LINE__ THIS_FILENAME_2 /* Note: #line sets the line number of the NEXT line */ +#include INC_FILENAME_1 +long line2 = __LINE__; + +int main(void) +{ + if (strcmp (filename1, INC_FILENAME_1) != 0) { + printf("Expected filename1: %s, got: %s\n", INC_FILENAME_1, filename1); + ++failures; + } + + if (strcmp (filename2, INC_FILENAME_2) != 0) { + printf("Expected filename2: %s, got: %s\n", INC_FILENAME_2, filename2); + ++failures; + } + + foo(); + +#line 65535 + if (strcmp (GET_FILE(), THIS_FILENAME_2) != 0) { + printf("Expected: %s, got: %s\n", THIS_FILENAME_2, GET_FILE()); + ++failures; + } + + if (line1 != 5L) { + printf("Expected line1: %ld, got: %ld\n", 5L, line1); + ++failures; + } + + if (line2 != 38L) { + printf("Expected line2: %ld, got: %ld\n", 38L, line2); + ++failures; + } + + if (strcmp (ans1, ans2) != 0 || strcmp (ans1, "42") != 0) { + ++failures; + printf("Expected: 42, ans1: %s, ans2: %s\n", ans1, ans2); + } + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + return failures; +} diff --git a/test/val/bug1573.h b/test/val/bug1573.h new file mode 100644 index 000000000..4e64b5ca2 --- /dev/null +++ b/test/val/bug1573.h @@ -0,0 +1,14 @@ +/* Tests for predefined macros __LINE__ and __FILE__ as well as #line control */ + +#line __LINE__ INC_FILENAME_1 +#define GET_LINE() __LINE__ +char filename1[] = GET_FILE(); +long line1 = GET_LINE(); + +#line 42 +const char ans1[] = MKSTR(GET_LINE()); + +#line 40 INC_FILENAME_2 +char filename2[] = GET_FILE(); +const char ans2[] = \ +MKSTR(GET_LINE());