Correctly report the line context when there is a valexpr parsing error.

This commit is contained in:
John Wiegley 2009-01-22 21:16:43 -04:00
parent d9e97cfede
commit 812d38c176
4 changed files with 75 additions and 37 deletions

View file

@ -63,15 +63,29 @@ inline string file_context(const path& file, std::size_t line) {
return buf.str(); 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; std::ostringstream buf;
buf << " " << line << " "; buf << " " << line << "\n";
istream_pos_type idx = (pos == istream_pos_type(0) ?
istream_pos_type(line.length()) : pos); if (pos != istream_pos_type(0)) {
idx -= 1; buf << " ";
for (istream_pos_type i = 0; i < idx; i += 1) if (end_pos == istream_pos_type(0)) {
buf << " "; for (istream_pos_type i = 0; i < pos; i += 1)
buf << "^" << std::endl; 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(); return buf.str();
} }

View file

@ -399,11 +399,21 @@ expr_t::parser_t::parse(std::istream& in, const flags_t flags,
return top_node; return top_node;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
add_error_context("While parsing value expression:");
if (original_string) { if (original_string) {
istream_pos_type pos = in.tellg(); add_error_context("While parsing value expression:");
pos -= 1;
add_error_context(line_context(*original_string, pos)); 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; throw;
} }

View file

@ -347,24 +347,32 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
if (pflags & EXPR_PARSE_NO_REDUCE) if (pflags & EXPR_PARSE_NO_REDUCE)
parse_flags |= AMOUNT_PARSE_NO_REDUCE; parse_flags |= AMOUNT_PARSE_NO_REDUCE;
amount_t temp; try {
if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) { amount_t temp;
// If the amount had no commodity, it must be an unambiguous if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) {
// variable reference // If the amount had no commodity, it must be an unambiguous
// variable reference
in.clear(); in.clear();
in.seekg(pos, std::ios::beg); in.seekg(pos, std::ios::beg);
if (in.fail()) if (in.fail())
throw_(parse_error, "Failed to reset input stream"); throw_(parse_error, "Failed to reset input stream");
c = in.peek(); c = in.peek();
if (std::isdigit(c) || c == '.') if (std::isdigit(c) || c == '.')
expected('\0', c); expected('\0', c);
parse_ident(in); parse_ident(in);
} else { } else {
kind = VALUE; kind = VALUE;
value = temp; value = temp;
length = in.tellg() - pos;
}
}
catch (const std::exception& err) {
kind = ERROR;
length = in.tellg() - pos;
throw;
} }
break; break;
} }
@ -381,7 +389,11 @@ void expr_t::token_t::rewind(std::istream& in)
void expr_t::token_t::unexpected() void expr_t::token_t::unexpected()
{ {
switch (kind) { kind_t prev_kind = kind;
kind = ERROR;
switch (prev_kind) {
case TOK_EOF: case TOK_EOF:
throw_(parse_error, "Unexpected end of expression"); throw_(parse_error, "Unexpected end of expression");
case IDENT: case IDENT:
@ -395,17 +407,19 @@ void expr_t::token_t::unexpected()
void expr_t::token_t::expected(char wanted, char c) void expr_t::token_t::expected(char wanted, char c)
{ {
if (c == '\0') { kind = ERROR;
if (wanted)
throw_(parse_error, "Missing '" << wanted << "'"); if (c == '\0' || c == -1) {
else if (wanted == '\0' || wanted == -1)
throw_(parse_error, "Unexpected end"); throw_(parse_error, "Unexpected end");
else
throw_(parse_error, "Missing '" << wanted << "'");
} else { } else {
if (wanted) if (wanted == '\0' || wanted == -1)
throw_(parse_error, "Invalid char '" << c << "'");
else
throw_(parse_error, "Invalid char '" << c throw_(parse_error, "Invalid char '" << c
<< "' (wanted '" << wanted << "')"); << "' (wanted '" << wanted << "')");
else
throw_(parse_error, "Invalid char '" << c << "'");
} }
} }

View file

@ -39,6 +39,7 @@ namespace ledger {
struct expr_t::token_t : public noncopyable struct expr_t::token_t : public noncopyable
{ {
enum kind_t { enum kind_t {
ERROR, // an error occurred while tokenizing
VALUE, // any kind of literal value VALUE, // any kind of literal value
IDENT, // [A-Za-z_][-A-Za-z0-9_:]* IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
MASK, // /regexp/ MASK, // /regexp/
@ -109,8 +110,7 @@ struct expr_t::token_t : public noncopyable
void next(std::istream& in, const uint_least8_t flags); void next(std::istream& in, const uint_least8_t flags);
void rewind(std::istream& in); void rewind(std::istream& in);
void unexpected(); void unexpected();
void expected(char wanted, char c = '\0');
static void expected(char wanted, char c = '\0');
}; };
} // namespace ledger } // namespace ledger