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();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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)
|
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 << "'");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue