C ++ Interpreter: how to emit error messages?
I want to generate dynamic error messages like all interpreters like:
Name error: Undefined variable
will be persistent, however I want to achieve:
Name error: Undefined variable 'X', in line 1
Okay. The line number is really not a problem: every error message must have a line number, so I added that to the error emitter function:
Error( ErrType type, string msg, int line );
So where is my problem?
How do I get 'X'
in Undefined variable *
?
- I can't use sprintf as it doesn't support strings, but I use them everywhere
- I can't just use cout and hook everything up as I want the error messages to be jerky.
- I want everything to be in one function like above,
Error()
How do I collect dynamic error messages?
For instance: Error( Name, sprintf("Undefined variable %s", myVariableName ), lineNum );
(But it myVariableName
is a string, and sprintf
will be corrupted)
a source to share
You can get a null terminated C string from myVariableName
by calling c_str
:
myVariableName.c_str()
Please note that the usage sprintf
is incorrect; the first parameter sprintf
takes a buffer to hold the formatted string. If you are using std::string
in your program, why use at all sprintf
? If it Error
does std::string
, you can simply use string concatenation:
Error(Name, "Undefined variable " + myVariableName, lineNum);
a source to share
Clang decides that if their diagnostics are overloaded operator<<
, streaming the required arguments that I accepted in my compiler too
DiagnosticBuilder Error( ErrType type, string msg, int line );
Then you can call it like
Error(Serious, "Variable % is not known", lineNumber) << var;
When the builder diagnostic destructor is called, an error is thrown.
struct DiagnosticBuilder {
DiagnosticBuilder(std::string const& format)
:m_emit(true), m_format(format)
{ }
DiagnosticBuilder(DiagnosticBuilder const& other)
:m_emit(true), m_format(other.m_format), m_args(other.m_args) {
other.m_emit = false;
}
~DiagnosticBuilder() {
if(m_emit) {
/* iterate over m_format, and print the next arg
everytime you hit '%' */
}
}
DiagnosticBuilder &operator<<(string const& s) {
m_args.push_back(s);
return *this;
}
DiagnosticBuilder &operator<<(int n) {
std::ostringstream oss; oss << n;
m_args.push_back(oss.str());
return *this;
}
// ...
private:
mutable bool m_emit;
std::string m_format;
std::vector<std::string> m_args;
};
I find this way of doing it quite conveniently. You can extend it to support multiple languages by numbering the argument placeholders, eg "Variable %2 isn't found in scope %1"
.
a source to share