summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-01-22 21:16:43 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-01-22 21:16:43 -0400
commit812d38c176b76b0fda5228338f5c5d70b3c6edf1 (patch)
tree75b50d517d948fe6808dcc1fc3b9e6594fb573fb
parentd9e97cfede4afcda55eaa8defaaa63c0c18065b1 (diff)
downloadfork-ledger-812d38c176b76b0fda5228338f5c5d70b3c6edf1.tar.gz
fork-ledger-812d38c176b76b0fda5228338f5c5d70b3c6edf1.tar.bz2
fork-ledger-812d38c176b76b0fda5228338f5c5d70b3c6edf1.zip
Correctly report the line context when there is a valexpr parsing error.
-rw-r--r--src/error.h30
-rw-r--r--src/parser.cc18
-rw-r--r--src/token.cc66
-rw-r--r--src/token.h4
4 files changed, 78 insertions, 40 deletions
diff --git a/src/error.h b/src/error.h
index 1ef6ff3c..6730ce17 100644
--- a/src/error.h
+++ b/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();
}
diff --git a/src/parser.cc b/src/parser.cc
index 82bc771d..4881f7bc 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -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;
}
diff --git a/src/token.cc b/src/token.cc
index 51eb4623..6c28e7d6 100644
--- a/src/token.cc
+++ b/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
-
- 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);
-
- parse_ident(in);
- } else {
- kind = VALUE;
- value = temp;
+ 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");
+
+ c = in.peek();
+ if (std::isdigit(c) || c == '.')
+ expected('\0', c);
+
+ 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 << "'");
}
}
diff --git a/src/token.h b/src/token.h
index 5502ad94..904a2e52 100644
--- a/src/token.h
+++ b/src/token.h
@@ -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