preproc: Parse cpp linemarkers

The line numbers in preproc's errors involving strings or INCBINs were
incorrect before this change because all the lines from '#include'd
files incremented the line number that would be reported.
This commit is contained in:
Martin Griffin 2026-05-13 05:55:12 +01:00
parent a3c551fe21
commit e38dfaa92c
4 changed files with 95 additions and 19 deletions

View File

@ -36,26 +36,26 @@
CFile::CFile(const char * filenameCStr, bool isStdin, const char * graphicsRootCStr)
{
if (isStdin)
m_filename = std::string{"<stdin>/"}.append(filenameCStr);
m_location.filename = std::string{"<stdin>/"}.append(filenameCStr);
else
m_filename = std::string(filenameCStr);
m_location.filename = std::string(filenameCStr);
m_location.lineNum = 1;
m_location.acceptLineMarker = isStdin;
m_buffer = ReadFileToBuffer(filenameCStr, isStdin, &m_size);
m_pos = 0;
m_lineNum = 1;
m_isStdin = isStdin;
m_graphicsRoot = graphicsRootCStr;
if (m_graphicsRoot.empty()) m_graphicsRoot = "./";
if (m_graphicsRoot[m_graphicsRoot.length() - 1] != '/') m_graphicsRoot.push_back('/');
}
CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename))
CFile::CFile(CFile&& other) : m_location(std::move(other.m_location))
{
m_buffer = other.m_buffer;
m_pos = other.m_pos;
m_size = other.m_size;
m_lineNum = other.m_lineNum;
m_isStdin = other.m_isStdin;
other.m_buffer = NULL;
@ -72,6 +72,66 @@ void CFile::Preproc()
while (m_pos < m_size)
{
if (m_location.acceptLineMarker)
{
if (m_buffer[m_pos] == '#')
{
long hashPos = m_pos;
long startPos;
long lineNum;
std::string filename;
m_pos++;
if (m_buffer[m_pos] != ' ')
goto linemarker_error;
m_pos++;
startPos = m_pos;
if (!IsAsciiDigit(m_buffer[m_pos]))
goto linemarker_error;
do
m_pos++;
while (IsAsciiDigit(m_buffer[m_pos]));
lineNum = atol(&m_buffer[startPos]);
if (m_buffer[m_pos] != ' ')
goto linemarker_error;
m_pos++;
if (m_buffer[m_pos] != '"')
goto linemarker_error;
m_pos++;
startPos = m_pos;
while (m_pos < m_size && m_buffer[m_pos] != '"')
m_pos++;
filename = std::string(&m_buffer[startPos], m_pos - startPos);
if (m_buffer[m_pos] != '"')
goto linemarker_error;
m_pos++;
while (m_pos < m_size && m_buffer[m_pos] != '\n')
m_pos++;
if (m_buffer[m_pos] != '\n')
goto linemarker_error;
m_pos++;
m_location.lineNum = lineNum - 1;
m_location.filename = std::move(filename);
linemarker_error:
m_location.acceptLineMarker = false;
// Re-parse this line so that it's available to cc1.
m_pos = hashPos;
}
else if (!IsAsciiWhitespace(m_buffer[m_pos]))
{
m_location.acceptLineMarker = false;
}
}
if (stringChar)
{
if (m_buffer[m_pos] == stringChar)
@ -89,7 +149,7 @@ void CFile::Preproc()
else
{
if (m_buffer[m_pos] == '\n')
m_lineNum++;
Newline();
std::putchar(m_buffer[m_pos]);
m_pos++;
}
@ -108,7 +168,7 @@ void CFile::Preproc()
std::putchar(c);
if (c == '\n')
m_lineNum++;
Newline();
else if (c == '"')
stringChar = '"';
else if (c == '\'')
@ -133,7 +193,7 @@ bool CFile::ConsumeNewline()
if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
{
m_pos += 2;
m_lineNum++;
Newline();
std::putchar('\n');
return true;
}
@ -141,7 +201,7 @@ bool CFile::ConsumeNewline()
if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineNum++;
Newline();
std::putchar('\n');
return true;
}
@ -149,6 +209,12 @@ bool CFile::ConsumeNewline()
return false;
}
void CFile::Newline()
{
m_location.lineNum++;
m_location.acceptLineMarker = m_isStdin;
}
void CFile::SkipWhitespace()
{
while (ConsumeHorizontalWhitespace() || ConsumeNewline())
@ -158,7 +224,7 @@ void CFile::SkipWhitespace()
void CFile::TryConvertString()
{
long oldPos = m_pos;
long oldLineNum = m_lineNum;
auto oldLocation = m_location;
bool noTerminator = false;
if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1])))
@ -177,7 +243,7 @@ void CFile::TryConvertString()
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
m_location = oldLocation;
return;
}
@ -335,7 +401,7 @@ void CFile::TryConvertIncbin()
int size = 1 << incbinType;
long oldPos = m_pos;
long oldLineNum = m_lineNum;
auto oldLocation = m_location;
m_pos += idents[incbinType].length();
@ -344,7 +410,7 @@ void CFile::TryConvertIncbin()
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
m_location = oldLocation;
return;
}
@ -412,7 +478,7 @@ void CFile::TryConvertIncgfx()
int size = 1 << incgfxType;
long oldPos = m_pos;
long oldLineNum = m_lineNum;
auto oldLocation = m_location;
m_pos += idents[incgfxType].length();
@ -420,7 +486,7 @@ void CFile::TryConvertIncgfx()
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
m_location = oldLocation;
return;
}
m_pos++;
@ -497,7 +563,7 @@ void CFile::ReportDiagnostic(const char* type, const char* format, std::va_list
const int bufferSize = 1024;
char buffer[bufferSize];
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_location.filename.c_str(), m_location.lineNum, type, buffer);
}
#define DO_REPORT(type) \

View File

@ -40,13 +40,17 @@ private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
std::string m_filename;
struct LogicalLocation {
std::string filename;
long lineNum;
bool acceptLineMarker;
} m_location;
bool m_isStdin;
std::string m_graphicsRoot;
bool ConsumeHorizontalWhitespace();
bool ConsumeNewline();
void Newline();
void SkipWhitespace();
void TryConvertString();
std::unique_ptr<unsigned char[]> ReadWholeFile(const std::string& path, int& size);

View File

@ -56,6 +56,11 @@ inline bool IsAsciiPrintable(unsigned char c)
return (c >= ' ' && c <= '~');
}
inline bool IsAsciiWhitespace(unsigned char c)
{
return c == ' ' || c == '\t' || c == '\f' || c == '\n' || c == '\r' || c == '\v';
}
// Returns whether the character can start a C identifier or the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierStartingChar(unsigned char c)
{

View File

@ -204,8 +204,9 @@ void StringParser::RaiseError(const char* format, ...)
std::va_list args;
va_start(args, format);
std::vsnprintf(buffer, bufferSize, format, args);
int n = std::vsnprintf(buffer, bufferSize, format, args);
va_end(args);
std::snprintf(buffer + n, bufferSize - n, " before '%.10s'", &m_buffer[m_pos]);
throw std::runtime_error(buffer);
}