Correctly report the line context when there is a valexpr parsing error.
This commit is contained in:
parent
d9e97cfede
commit
812d38c176
4 changed files with 75 additions and 37 deletions
30
src/error.h
30
src/error.h
|
|
@ -63,15 +63,29 @@ inline string file_context(const path& file, std::size_t line) {
|
|||
return buf.str();
|
||||
}
|
||||
|
||||
inline string line_context(const string& line, istream_pos_type pos) {
|
||||
inline string line_context(const string& line,
|
||||
istream_pos_type pos = istream_pos_type(0),
|
||||
istream_pos_type end_pos = istream_pos_type(0))
|
||||
{
|
||||
std::ostringstream buf;
|
||||
buf << " " << line << " ";
|
||||
istream_pos_type idx = (pos == istream_pos_type(0) ?
|
||||
istream_pos_type(line.length()) : pos);
|
||||
idx -= 1;
|
||||
for (istream_pos_type i = 0; i < idx; i += 1)
|
||||
buf << " ";
|
||||
buf << "^" << std::endl;
|
||||
buf << " " << line << "\n";
|
||||
|
||||
if (pos != istream_pos_type(0)) {
|
||||
buf << " ";
|
||||
if (end_pos == istream_pos_type(0)) {
|
||||
for (istream_pos_type i = 0; i < pos; i += 1)
|
||||
buf << " ";
|
||||
buf << "^\n";
|
||||
} else {
|
||||
for (istream_pos_type i = 0; i < end_pos; i += 1) {
|
||||
if (i >= pos)
|
||||
buf << "^";
|
||||
else
|
||||
buf << " ";
|
||||
}
|
||||
buf << '\n';
|
||||
}
|
||||
}
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -399,11 +399,21 @@ expr_t::parser_t::parse(std::istream& in, const flags_t flags,
|
|||
return top_node;
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
add_error_context("While parsing value expression:");
|
||||
if (original_string) {
|
||||
istream_pos_type pos = in.tellg();
|
||||
pos -= 1;
|
||||
add_error_context(line_context(*original_string, pos));
|
||||
add_error_context("While parsing value expression:");
|
||||
|
||||
istream_pos_type end_pos = in.tellg();
|
||||
istream_pos_type pos = end_pos;
|
||||
|
||||
pos -= lookahead.length;
|
||||
|
||||
DEBUG("parser.error", "original_string = '" << *original_string << "'");
|
||||
DEBUG("parser.error", " pos = " << pos);
|
||||
DEBUG("parser.error", " end_pos = " << end_pos);
|
||||
DEBUG("parser.error", " token kind = " << int(lookahead.kind));
|
||||
DEBUG("parser.error", " token length = " << lookahead.length);
|
||||
|
||||
add_error_context(line_context(*original_string, pos, end_pos));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
|
|
|||
60
src/token.cc
60
src/token.cc
|
|
@ -347,24 +347,32 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
|
|||
if (pflags & EXPR_PARSE_NO_REDUCE)
|
||||
parse_flags |= AMOUNT_PARSE_NO_REDUCE;
|
||||
|
||||
amount_t temp;
|
||||
if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) {
|
||||
// If the amount had no commodity, it must be an unambiguous
|
||||
// variable reference
|
||||
try {
|
||||
amount_t temp;
|
||||
if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) {
|
||||
// If the amount had no commodity, it must be an unambiguous
|
||||
// variable reference
|
||||
|
||||
in.clear();
|
||||
in.seekg(pos, std::ios::beg);
|
||||
if (in.fail())
|
||||
throw_(parse_error, "Failed to reset input stream");
|
||||
in.clear();
|
||||
in.seekg(pos, std::ios::beg);
|
||||
if (in.fail())
|
||||
throw_(parse_error, "Failed to reset input stream");
|
||||
|
||||
c = in.peek();
|
||||
if (std::isdigit(c) || c == '.')
|
||||
expected('\0', c);
|
||||
c = in.peek();
|
||||
if (std::isdigit(c) || c == '.')
|
||||
expected('\0', c);
|
||||
|
||||
parse_ident(in);
|
||||
} else {
|
||||
kind = VALUE;
|
||||
value = temp;
|
||||
parse_ident(in);
|
||||
} else {
|
||||
kind = VALUE;
|
||||
value = temp;
|
||||
length = in.tellg() - pos;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
kind = ERROR;
|
||||
length = in.tellg() - pos;
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -381,7 +389,11 @@ void expr_t::token_t::rewind(std::istream& in)
|
|||
|
||||
void expr_t::token_t::unexpected()
|
||||
{
|
||||
switch (kind) {
|
||||
kind_t prev_kind = kind;
|
||||
|
||||
kind = ERROR;
|
||||
|
||||
switch (prev_kind) {
|
||||
case TOK_EOF:
|
||||
throw_(parse_error, "Unexpected end of expression");
|
||||
case IDENT:
|
||||
|
|
@ -395,17 +407,19 @@ void expr_t::token_t::unexpected()
|
|||
|
||||
void expr_t::token_t::expected(char wanted, char c)
|
||||
{
|
||||
if (c == '\0') {
|
||||
if (wanted)
|
||||
throw_(parse_error, "Missing '" << wanted << "'");
|
||||
else
|
||||
kind = ERROR;
|
||||
|
||||
if (c == '\0' || c == -1) {
|
||||
if (wanted == '\0' || wanted == -1)
|
||||
throw_(parse_error, "Unexpected end");
|
||||
else
|
||||
throw_(parse_error, "Missing '" << wanted << "'");
|
||||
} else {
|
||||
if (wanted)
|
||||
if (wanted == '\0' || wanted == -1)
|
||||
throw_(parse_error, "Invalid char '" << c << "'");
|
||||
else
|
||||
throw_(parse_error, "Invalid char '" << c
|
||||
<< "' (wanted '" << wanted << "')");
|
||||
else
|
||||
throw_(parse_error, "Invalid char '" << c << "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ namespace ledger {
|
|||
struct expr_t::token_t : public noncopyable
|
||||
{
|
||||
enum kind_t {
|
||||
ERROR, // an error occurred while tokenizing
|
||||
VALUE, // any kind of literal value
|
||||
IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
|
||||
MASK, // /regexp/
|
||||
|
|
@ -109,8 +110,7 @@ struct expr_t::token_t : public noncopyable
|
|||
void next(std::istream& in, const uint_least8_t flags);
|
||||
void rewind(std::istream& in);
|
||||
void unexpected();
|
||||
|
||||
static void expected(char wanted, char c = '\0');
|
||||
void expected(char wanted, char c = '\0');
|
||||
};
|
||||
|
||||
} // namespace ledger
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue