Report an error in the case of '(1' (missing rparen)

Fixes #557
This commit is contained in:
John Wiegley 2011-11-10 00:48:19 -06:00
parent d493f79651
commit 37e9ec8030
6 changed files with 115 additions and 18 deletions

View file

@ -49,15 +49,15 @@ namespace ledger {
class expr_t : public expr_base_t<value_t> class expr_t : public expr_base_t<value_t>
{ {
struct token_t; class parser_t;
class parser_t;
typedef expr_base_t<value_t> base_type; typedef expr_base_t<value_t> base_type;
public: public:
struct token_t;
class op_t; class op_t;
typedef intrusive_ptr<op_t> ptr_op_t; typedef intrusive_ptr<op_t> ptr_op_t;
typedef intrusive_ptr<const op_t> const_ptr_op_t; typedef intrusive_ptr<const op_t> const_ptr_op_t;
protected: protected:
ptr_op_t ptr; ptr_op_t ptr;

View file

@ -74,7 +74,7 @@ expr_t::parser_t::parse_value_term(std::istream& in,
case token_t::LPAREN: case token_t::LPAREN:
node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL) node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL)
.minus_flags(PARSE_SINGLE)); .minus_flags(PARSE_SINGLE));
tok = next_token(in, tflags, ')'); tok = next_token(in, tflags, token_t::RPAREN);
break; break;
default: default:
@ -367,7 +367,7 @@ expr_t::parser_t::parse_querycolon_expr(std::istream& in,
throw_(parse_error, throw_(parse_error,
_("%1 operator not followed by argument") << tok.symbol); _("%1 operator not followed by argument") << tok.symbol);
next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT), ':'); next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT), token_t::COLON);
prev = node->right(); prev = node->right();
ptr_op_t subnode = new op_t(op_t::O_COLON); ptr_op_t subnode = new op_t(op_t::O_COLON);
subnode->set_left(prev); subnode->set_left(prev);

View file

@ -53,11 +53,15 @@ class expr_t::parser_t : public noncopyable
mutable bool use_lookahead; mutable bool use_lookahead;
token_t& next_token(std::istream& in, const parse_flags_t& tflags, token_t& next_token(std::istream& in, const parse_flags_t& tflags,
const char expecting = '\0') const { const optional<token_t::kind_t>& expecting = none) const {
if (use_lookahead) if (use_lookahead)
use_lookahead = false; use_lookahead = false;
else else
lookahead.next(in, tflags, expecting); lookahead.next(in, tflags);
if (expecting && lookahead.kind != *expecting)
lookahead.expected(*expecting);
return lookahead; return lookahead;
} }

View file

@ -770,7 +770,7 @@ public:
parent->HANDLER(total_) parent->HANDLER(total_)
.set_expr(string("--percent"), .set_expr(string("--percent"),
"((is_account&parent&parent.total)?" "((is_account&parent&parent.total)?"
" percent(scrub(total), scrub(parent.total)):0"); " percent(scrub(total), scrub(parent.total)):0)");
}); });
OPTION__ OPTION__

View file

@ -137,8 +137,7 @@ void expr_t::token_t::parse_ident(std::istream& in)
value.set_string(buf); value.set_string(buf);
} }
void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
const char expecting)
{ {
if (in.eof()) { if (in.eof()) {
kind = TOK_EOF; kind = TOK_EOF;
@ -436,7 +435,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags,
kind = ERROR; kind = ERROR;
symbol[0] = c; symbol[0] = c;
symbol[1] = '\0'; symbol[1] = '\0';
unexpected(expecting); throw_(parse_error, _("Failed to parse identifier"));
} }
} else { } else {
if (! in.good()) { if (! in.good()) {
@ -505,10 +504,8 @@ void expr_t::token_t::unexpected(const char wanted)
} }
} }
void expr_t::token_t::expected(char wanted, char c) void expr_t::token_t::expected(const char wanted, char c)
{ {
kind = ERROR;
if (c == '\0' || c == -1) { if (c == '\0' || c == -1) {
if (wanted == '\0' || wanted == -1) if (wanted == '\0' || wanted == -1)
throw_(parse_error, _("Unexpected end")); throw_(parse_error, _("Unexpected end"));
@ -518,8 +515,101 @@ void expr_t::token_t::expected(char wanted, char c)
if (wanted == '\0' || wanted == -1) if (wanted == '\0' || wanted == -1)
throw_(parse_error, _("Invalid char '%1'") << c); throw_(parse_error, _("Invalid char '%1'") << c);
else else
throw_(parse_error, _("Invalid char '%1' (wanted '%2')") << c << wanted); throw_(parse_error,
_("Invalid char '%1' (wanted '%2')") << c << wanted);
} }
} }
void expr_t::token_t::expected(const kind_t wanted)
{
try {
if (wanted == '\0' || wanted == -1)
throw_(parse_error, _("Invalid token '%1'") << *this);
else
throw_(parse_error,
_("Invalid token '%1' (wanted '%2')") << *this << wanted);
}
catch (...) {
kind = ERROR;
throw;
}
}
std::ostream& operator<<(std::ostream& out, const expr_t::token_t::kind_t& kind)
{
switch (kind) {
case expr_t::token_t::ERROR: out << "<error token>"; break;
case expr_t::token_t::VALUE: out << "<value>"; break;
case expr_t::token_t::IDENT: out << "<identifier>"; break;
case expr_t::token_t::MASK: out << "<regex mask>"; break;
case expr_t::token_t::LPAREN: out << "("; break;
case expr_t::token_t::RPAREN: out << ")"; break;
case expr_t::token_t::LBRACE: out << "{"; break;
case expr_t::token_t::RBRACE: out << "}"; break;
case expr_t::token_t::EQUAL: out << "=="; break;
case expr_t::token_t::NEQUAL: out << "!="; break;
case expr_t::token_t::LESS: out << "<"; break;
case expr_t::token_t::LESSEQ: out << "<="; break;
case expr_t::token_t::GREATER: out << ">"; break;
case expr_t::token_t::GREATEREQ: out << ">="; break;
case expr_t::token_t::ASSIGN: out << "="; break;
case expr_t::token_t::MATCH: out << "=~"; break;
case expr_t::token_t::NMATCH: out << "!~"; break;
case expr_t::token_t::MINUS: out << "-"; break;
case expr_t::token_t::PLUS: out << "+"; break;
case expr_t::token_t::STAR: out << "*"; break;
case expr_t::token_t::SLASH: out << "/"; break;
case expr_t::token_t::ARROW: out << "->"; break;
case expr_t::token_t::KW_DIV: out << "div"; break;
case expr_t::token_t::EXCLAM: out << "!"; break;
case expr_t::token_t::KW_AND: out << "and"; break;
case expr_t::token_t::KW_OR: out << "or"; break;
case expr_t::token_t::KW_MOD: out << "mod"; break;
case expr_t::token_t::KW_IF: out << "if"; break;
case expr_t::token_t::KW_ELSE: out << "else"; break;
case expr_t::token_t::QUERY: out << "?"; break;
case expr_t::token_t::COLON: out << ":"; break;
case expr_t::token_t::DOT: out << "."; break;
case expr_t::token_t::COMMA: out << ","; break;
case expr_t::token_t::SEMI: out << ";"; break;
case expr_t::token_t::TOK_EOF: out << "<end of input>"; break;
case expr_t::token_t::UNKNOWN: out << "<unknown>"; break;
default:
assert(false);
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out, const expr_t::token_t& token)
{
switch (token.kind) {
case expr_t::token_t::VALUE:
out << "<value '" << token.value << "'>";
break;
case expr_t::token_t::IDENT:
out << "<ident '" << token.value << "'>";
break;
case expr_t::token_t::MASK:
out << "<mask '" << token.value << "'>";
break;
default:
out << token.kind;
break;
}
return out;
}
} // namespace ledger } // namespace ledger

View file

@ -123,13 +123,16 @@ struct expr_t::token_t : public noncopyable
int parse_reserved_word(std::istream& in); int parse_reserved_word(std::istream& in);
void parse_ident(std::istream& in); void parse_ident(std::istream& in);
void next(std::istream& in, const parse_flags_t& flags, void next(std::istream& in, const parse_flags_t& flags);
const char expecting = '\0');
void rewind(std::istream& in); void rewind(std::istream& in);
void unexpected(const char wanted = '\0'); void unexpected(const char wanted = '\0');
void expected(const char wanted, char c = '\0'); void expected(const char wanted, const char c = '\0');
void expected(const kind_t wanted);
}; };
std::ostream& operator<<(std::ostream& out, const expr_t::token_t::kind_t& kind);
std::ostream& operator<<(std::ostream& out, const expr_t::token_t& token);
} // namespace ledger } // namespace ledger
#endif // _TOKEN_H #endif // _TOKEN_H