improved error checking in parser, in case parts of an amount are missing
This commit is contained in:
parent
93bd16b545
commit
eda733a56e
5 changed files with 87 additions and 31 deletions
14
amount.cc
14
amount.cc
|
|
@ -698,8 +698,6 @@ void amount_t::parse(std::istream& in)
|
||||||
std::string quant;
|
std::string quant;
|
||||||
unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
|
unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
|
||||||
|
|
||||||
_init();
|
|
||||||
|
|
||||||
char c = peek_next_nonws(in);
|
char c = peek_next_nonws(in);
|
||||||
if (std::isdigit(c) || c == '.' || c == '-') {
|
if (std::isdigit(c) || c == '.' || c == '-') {
|
||||||
parse_quantity(in, quant);
|
parse_quantity(in, quant);
|
||||||
|
|
@ -722,6 +720,11 @@ void amount_t::parse(std::istream& in)
|
||||||
parse_quantity(in, quant);
|
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_comma = quant.rfind(',');
|
||||||
std::string::size_type last_period = quant.rfind('.');
|
std::string::size_type last_period = quant.rfind('.');
|
||||||
|
|
||||||
|
|
@ -745,7 +748,9 @@ void amount_t::parse(std::istream& in)
|
||||||
quantity->prec = 0;
|
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_ = commodity_t::find_commodity(symbol, true);
|
||||||
commodity_->flags |= flags;
|
commodity_->flags |= flags;
|
||||||
if (quantity->prec > commodity_->precision)
|
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));
|
mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
|
||||||
unsigned short len = size * sizeof(short);
|
unsigned short len = size * sizeof(short);
|
||||||
out.write((char *)&len, sizeof(len));
|
out.write((char *)&len, sizeof(len));
|
||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
assert(len < 4096);
|
assert(len < 4096);
|
||||||
out.write(buf, len);
|
out.write(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
|
byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
|
||||||
out.write(&byte, sizeof(byte));
|
out.write(&byte, sizeof(byte));
|
||||||
|
|
||||||
out.write((char *)&quantity->prec, sizeof(quantity->prec));
|
out.write((char *)&quantity->prec, sizeof(quantity->prec));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
assert(quantity->ref > 1);
|
assert(quantity->ref > 1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ config_t::config_t()
|
||||||
plot_total_format = "%D %T\n";
|
plot_total_format = "%D %T\n";
|
||||||
print_format = "\n%D %X%C%P\n %-34N %12o\n%/ %-34N %12o\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";
|
equity_format = "\n%D %X%C%P\n%/ %-34N %12t\n";
|
||||||
|
prices_format = "%D %-10N %12t %12T\n";
|
||||||
|
|
||||||
show_collapsed = false;
|
show_collapsed = false;
|
||||||
show_subtotal = false;
|
show_subtotal = false;
|
||||||
|
|
|
||||||
8
config.h
8
config.h
|
|
@ -14,13 +14,6 @@
|
||||||
|
|
||||||
namespace ledger {
|
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
|
struct config_t
|
||||||
{
|
{
|
||||||
// These options can all be set used text fields.
|
// These options can all be set used text fields.
|
||||||
|
|
@ -42,6 +35,7 @@ struct config_t
|
||||||
std::string plot_total_format;
|
std::string plot_total_format;
|
||||||
std::string print_format;
|
std::string print_format;
|
||||||
std::string equity_format;
|
std::string equity_format;
|
||||||
|
std::string prices_format;
|
||||||
std::string date_format;
|
std::string date_format;
|
||||||
std::string sort_string;
|
std::string sort_string;
|
||||||
std::string amount_expr;
|
std::string amount_expr;
|
||||||
|
|
|
||||||
36
main.cc
36
main.cc
|
|
@ -137,6 +137,36 @@ chain_formatters(const std::string& command,
|
||||||
return formatter;
|
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[])
|
int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
{
|
{
|
||||||
std::auto_ptr<journal_t> journal(new journal_t);
|
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";
|
command = "e";
|
||||||
else if (command == "equity")
|
else if (command == "equity")
|
||||||
command = "E";
|
command = "E";
|
||||||
|
else if (command == "prices")
|
||||||
|
command = "P";
|
||||||
else
|
else
|
||||||
throw error(std::string("Unrecognized command '") + command + "'");
|
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;
|
format = &config.register_format;
|
||||||
else if (command == "E")
|
else if (command == "E")
|
||||||
format = &config.equity_format;
|
format = &config.equity_format;
|
||||||
|
else if (command == "P")
|
||||||
|
format = &config.prices_format;
|
||||||
else
|
else
|
||||||
format = &config.print_format;
|
format = &config.print_format;
|
||||||
|
|
||||||
|
|
@ -259,6 +293,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
|
|
||||||
if (command == "e")
|
if (command == "e")
|
||||||
walk_transactions(new_entry->transactions, *formatter);
|
walk_transactions(new_entry->transactions, *formatter);
|
||||||
|
else if (command == "P")
|
||||||
|
walk_commodities(commodity_t::commodities, *formatter);
|
||||||
else
|
else
|
||||||
walk_entries(journal->entries, *formatter);
|
walk_entries(journal->entries, *formatter);
|
||||||
|
|
||||||
|
|
|
||||||
25
textual.cc
25
textual.cc
|
|
@ -69,14 +69,23 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
|
||||||
|
|
||||||
char * p = skip_ws(line);
|
char * p = skip_ws(line);
|
||||||
if (char * cost_str = next_element(p, true)) {
|
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 (char * note_str = std::strchr(cost_str, ';')) {
|
||||||
|
if (cost_str == note_str)
|
||||||
|
has_amount = false;
|
||||||
*note_str++ = '\0';
|
*note_str++ = '\0';
|
||||||
xact->note = skip_ws(note_str);
|
xact->note = skip_ws(note_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
char * price_str = std::strchr(cost_str, '@');
|
if (has_amount) {
|
||||||
bool per_unit = true;
|
bool per_unit = true;
|
||||||
|
char * price_str = std::strchr(cost_str, '@');
|
||||||
if (price_str) {
|
if (price_str) {
|
||||||
|
if (price_str == cost_str)
|
||||||
|
throw parse_error(path, linenum, "Cost specified without amount");
|
||||||
|
|
||||||
*price_str++ = '\0';
|
*price_str++ = '\0';
|
||||||
if (*price_str == '@') {
|
if (*price_str == '@') {
|
||||||
per_unit = false;
|
per_unit = false;
|
||||||
|
|
@ -87,9 +96,11 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
|
||||||
}
|
}
|
||||||
|
|
||||||
xact->amount.parse(cost_str);
|
xact->amount.parse(cost_str);
|
||||||
|
|
||||||
if (price_str && per_unit)
|
if (price_str && per_unit)
|
||||||
*xact->cost *= xact->amount;
|
*xact->cost *= xact->amount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (*p == '[' || *p == '(') {
|
if (*p == '[' || *p == '(') {
|
||||||
xact->flags |= TRANSACTION_VIRTUAL;
|
xact->flags |= TRANSACTION_VIRTUAL;
|
||||||
|
|
@ -252,7 +263,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
if (peek_next_nonws(in) != '\n') {
|
if (peek_next_nonws(in) != '\n') {
|
||||||
in.getline(line, MAX_LINE);
|
in.getline(line, MAX_LINE);
|
||||||
linenum++;
|
linenum++;
|
||||||
throw parse_error(path, linenum, "Line begins with whitespace");
|
throw parse_error(path, linenum - 1, "Line begins with whitespace");
|
||||||
}
|
}
|
||||||
// fall through...
|
// fall through...
|
||||||
|
|
||||||
|
|
@ -490,6 +501,16 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
errors++;
|
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:
|
done:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue