129 lines
3.2 KiB
C++
129 lines
3.2 KiB
C++
|
#include "parsecontext.h"
|
||
|
#include "assert.h"
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <iomanip>
|
||
|
#include <cmath>
|
||
|
|
||
|
|
||
|
ParseContext::ParseContext()
|
||
|
{
|
||
|
applicationDir = std::string();
|
||
|
}
|
||
|
|
||
|
unsigned ParseContext::addFile(const std::string& fileName,
|
||
|
const std::string& source)
|
||
|
{
|
||
|
fileMap.push_back(SourceFile(fileName, source));
|
||
|
return fileMap.size();
|
||
|
}
|
||
|
|
||
|
SourceFile& ParseContext::getFile(unsigned fileId)
|
||
|
{
|
||
|
return fileMap[fileId-1];
|
||
|
}
|
||
|
|
||
|
|
||
|
struct pad {
|
||
|
char ch; int count;
|
||
|
|
||
|
pad(char ch, int count) : ch(ch), count(count) {}
|
||
|
};
|
||
|
|
||
|
std::ostream& operator<<(std::ostream& stream, const pad& p) {
|
||
|
for (int i = 0; i < p.count; i++) {
|
||
|
stream << p.ch;
|
||
|
}
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
#define ATLAS_ASSERT(x) \
|
||
|
do {} while(0)
|
||
|
|
||
|
void ParseContext::formatError(const SourceError& sourceError,
|
||
|
std::ostream& stream,
|
||
|
const std::string& ErrorOrWarning)
|
||
|
{
|
||
|
ATLAS_ASSERT(sourceError.span.lo.col != 0
|
||
|
&& sourceError.span.hi.col != 0);
|
||
|
|
||
|
SourceFile sourceFile = getFile(sourceError.span.lo.fileId);
|
||
|
|
||
|
stream << ErrorOrWarning << sourceError.message << std::endl;
|
||
|
|
||
|
Span span = sourceError.span;
|
||
|
|
||
|
// figure out how much padding is required for line numbers in front of
|
||
|
// printed source code
|
||
|
int numberPad = 0;
|
||
|
while (true) {
|
||
|
numberPad = std::max(numberPad,
|
||
|
static_cast<int>(ceil(log10(span.hi.line))) + 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
span = sourceError.span;
|
||
|
|
||
|
// print all spans
|
||
|
while (true) {
|
||
|
SourceFile sourceFile = getFile(span.lo.fileId);
|
||
|
|
||
|
// check if the span spans across multiple lines or even files
|
||
|
bool complex = span.lo.fileId != span.hi.fileId
|
||
|
|| span.lo.line != span.hi.line;
|
||
|
|
||
|
stream << pad(' ', numberPad) << " -> " << sourceFile.getFileName()
|
||
|
<< ":" << span.lo.line << ":" << span.lo.col << std::endl;
|
||
|
stream << pad(' ', numberPad) << " |" << std::endl
|
||
|
<< std::right << std::setw(numberPad) << span.lo.line << " | "
|
||
|
<< sourceFile.getLine(span.lo.line) << std::endl
|
||
|
<< pad(' ', numberPad) << " | ";
|
||
|
for (unsigned i = 0; i < span.lo.col - 1; i++) {
|
||
|
stream << ' ';
|
||
|
}
|
||
|
if (complex) {
|
||
|
// just point to the beginning of the span
|
||
|
stream << '^';
|
||
|
} else {
|
||
|
// underline the entire span
|
||
|
for (unsigned i = 0; i < span.hi.col - span.lo.col; i++) {
|
||
|
stream << '^';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stream << std::endl;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
stream << std::endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
SourceFile::SourceFile(std::string fileName, std::string source)
|
||
|
: fileName(std::move(fileName)), source(std::move(source))
|
||
|
{
|
||
|
lineOffsets.push_back(0);
|
||
|
}
|
||
|
|
||
|
const std::string& SourceFile::getFileName() const
|
||
|
{
|
||
|
return fileName;
|
||
|
}
|
||
|
|
||
|
const std::string SourceFile::getLine(unsigned line) const
|
||
|
{
|
||
|
auto lineOffset = lineOffsets[line-1];
|
||
|
auto nextLF = source.find('\n', lineOffset);
|
||
|
auto nextCR = source.find('\r', lineOffset);
|
||
|
auto nl = std::min(nextCR, nextLF);
|
||
|
|
||
|
return source.substr(lineOffset, nl - lineOffset);
|
||
|
}
|
||
|
|
||
|
void SourceFile::addLineOffset(unsigned offset)
|
||
|
{
|
||
|
lineOffsets.push_back(offset);
|
||
|
}
|