improved error checking in parser, in case parts of an amount are missing

This commit is contained in:
John Wiegley 2004-09-24 23:11:50 -04:00
parent 93bd16b545
commit eda733a56e
5 changed files with 87 additions and 31 deletions

View file

@ -698,8 +698,6 @@ void amount_t::parse(std::istream& in)
std::string quant;
unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
_init();
char c = peek_next_nonws(in);
if (std::isdigit(c) || c == '.' || c == '-') {
parse_quantity(in, quant);
@ -722,6 +720,11 @@ void amount_t::parse(std::istream& in)
parse_quantity(in, quant);
}
if (quant.empty())
throw amount_error("No quantity specified for amount");
_init();
std::string::size_type last_comma = quant.rfind(',');
std::string::size_type last_period = quant.rfind('.');
@ -745,7 +748,9 @@ void amount_t::parse(std::istream& in)
quantity->prec = 0;
}
// Create the commodity if has not already been seen.
// Create the commodity if has not already been seen, and update the
// precision if something greater was used for the quantity.
commodity_ = commodity_t::find_commodity(symbol, true);
commodity_->flags |= flags;
if (quantity->prec > commodity_->precision)
@ -879,16 +884,15 @@ void amount_t::write_quantity(std::ostream& out) const
mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
unsigned short len = size * sizeof(short);
out.write((char *)&len, sizeof(len));
if (len) {
assert(len < 4096);
out.write(buf, len);
byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&byte, sizeof(byte));
out.write((char *)&quantity->prec, sizeof(quantity->prec));
}
byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&byte, sizeof(byte));
out.write((char *)&quantity->prec, sizeof(quantity->prec));
} else {
assert(quantity->ref > 1);

View file

@ -29,6 +29,7 @@ config_t::config_t()
plot_total_format = "%D %T\n";
print_format = "\n%D %X%C%P\n %-34N %12o\n%/ %-34N %12o\n";
equity_format = "\n%D %X%C%P\n%/ %-34N %12t\n";
prices_format = "%D %-10N %12t %12T\n";
show_collapsed = false;
show_subtotal = false;

View file

@ -14,13 +14,6 @@
namespace ledger {
extern std::string bal_fmt;
extern std::string reg_fmt;
extern std::string plot_value_fmt;
extern std::string plot_total_fmt;
extern std::string print_fmt;
extern std::string equity_fmt;
struct config_t
{
// These options can all be set used text fields.
@ -42,6 +35,7 @@ struct config_t
std::string plot_total_format;
std::string print_format;
std::string equity_format;
std::string prices_format;
std::string date_format;
std::string sort_string;
std::string amount_expr;

36
main.cc
View file

@ -137,6 +137,36 @@ chain_formatters(const std::string& command,
return formatter;
}
void walk_commodities(commodities_map& commodities,
item_handler<transaction_t>& handler)
{
std::list<transaction_t> xact_temps;
std::list<entry_t> entry_temps;
std::list<account_t> acct_temps;
for (commodities_map::iterator i = commodities.begin();
i != commodities.end();
i++) {
if ((*i).second->flags & COMMODITY_STYLE_NOMARKET)
continue;
entry_temps.push_back(entry_t());
acct_temps.push_back(account_t(NULL, (*i).second->symbol));
for (history_map::iterator j = (*i).second->history.begin();
j != (*i).second->history.end();
j++) {
entry_temps.back().date = (*j).first;
xact_temps.push_back(transaction_t(&acct_temps.back()));
xact_temps.back().entry = &entry_temps.back();
xact_temps.back().amount = (*j).second;
handler(xact_temps.back());
}
}
}
int parse_and_report(int argc, char * argv[], char * envp[])
{
std::auto_ptr<journal_t> journal(new journal_t);
@ -186,6 +216,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
command = "e";
else if (command == "equity")
command = "E";
else if (command == "prices")
command = "P";
else
throw error(std::string("Unrecognized command '") + command + "'");
@ -240,6 +272,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
format = &config.register_format;
else if (command == "E")
format = &config.equity_format;
else if (command == "P")
format = &config.prices_format;
else
format = &config.print_format;
@ -259,6 +293,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
if (command == "e")
walk_transactions(new_entry->transactions, *formatter);
else if (command == "P")
walk_commodities(commodity_t::commodities, *formatter);
else
walk_entries(journal->entries, *formatter);

View file

@ -69,26 +69,37 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
char * p = skip_ws(line);
if (char * cost_str = next_element(p, true)) {
cost_str = skip_ws(cost_str);
bool has_amount = *cost_str;
if (char * note_str = std::strchr(cost_str, ';')) {
if (cost_str == note_str)
has_amount = false;
*note_str++ = '\0';
xact->note = skip_ws(note_str);
}
char * price_str = std::strchr(cost_str, '@');
bool per_unit = true;
if (price_str) {
*price_str++ = '\0';
if (*price_str == '@') {
per_unit = false;
price_str++;
}
xact->cost = new amount_t;
xact->cost->parse(price_str);
}
if (has_amount) {
bool per_unit = true;
char * price_str = std::strchr(cost_str, '@');
if (price_str) {
if (price_str == cost_str)
throw parse_error(path, linenum, "Cost specified without amount");
xact->amount.parse(cost_str);
if (price_str && per_unit)
*xact->cost *= xact->amount;
*price_str++ = '\0';
if (*price_str == '@') {
per_unit = false;
price_str++;
}
xact->cost = new amount_t;
xact->cost->parse(price_str);
}
xact->amount.parse(cost_str);
if (price_str && per_unit)
*xact->cost *= xact->amount;
}
}
if (*p == '[' || *p == '(') {
@ -252,7 +263,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (peek_next_nonws(in) != '\n') {
in.getline(line, MAX_LINE);
linenum++;
throw parse_error(path, linenum, "Line begins with whitespace");
throw parse_error(path, linenum - 1, "Line begins with whitespace");
}
// fall through...
@ -490,6 +501,16 @@ unsigned int textual_parser_t::parse(std::istream& in,
std::cerr << "Error: " << err.what() << std::endl;
errors++;
}
catch (const amount_error& err) {
std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": "
<< err.what() << std::endl;;
errors++;
}
catch (const error& err) {
std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": "
<< err.what() << std::endl;;
errors++;
}
}
done: