Added much better error location.

This commit is contained in:
John Wiegley 2006-03-08 18:24:56 +00:00
parent b737cd8e6d
commit e32d9e64a7
37 changed files with 1108 additions and 447 deletions

View file

@ -20,6 +20,7 @@ libledger_la_SOURCES = \
config.cc \ config.cc \
derive.cc \ derive.cc \
emacs.cc \ emacs.cc \
error.cc \
format.cc \ format.cc \
journal.cc \ journal.cc \
mask.cc \ mask.cc \

View file

@ -378,7 +378,7 @@ amount_t& amount_t::operator+=(const amount_t& amt)
_dup(); _dup();
if (commodity_ != amt.commodity_) if (commodity_ != amt.commodity_)
throw amount_error("Adding amounts with different commodities"); throw new amount_error("Adding amounts with different commodities");
if (quantity->prec == amt.quantity->prec) { if (quantity->prec == amt.quantity->prec) {
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@ -411,7 +411,7 @@ amount_t& amount_t::operator-=(const amount_t& amt)
_dup(); _dup();
if (commodity_ != amt.commodity_) if (commodity_ != amt.commodity_)
throw amount_error("Subtracting amounts with different commodities"); throw new amount_error("Subtracting amounts with different commodities");
if (quantity->prec == amt.quantity->prec) { if (quantity->prec == amt.quantity->prec) {
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@ -453,7 +453,7 @@ amount_t& amount_t::operator*=(const amount_t& amt)
amount_t& amount_t::operator/=(const amount_t& amt) amount_t& amount_t::operator/=(const amount_t& amt)
{ {
if (! amt.quantity || ! amt) if (! amt.quantity || ! amt)
throw amount_error("Divide by zero"); throw new amount_error("Divide by zero");
else if (! quantity) else if (! quantity)
return *this; return *this;
@ -919,7 +919,7 @@ void parse_commodity(std::istream& in, std::string& symbol)
if (c == '"') if (c == '"')
in.get(c); in.get(c);
else else
throw amount_error("Quoted commodity symbol lacks closing quote"); throw new amount_error("Quoted commodity symbol lacks closing quote");
} else { } else {
READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) && READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) &&
c != '-' && c != '.'); c != '-' && c != '.');
@ -939,14 +939,14 @@ void parse_annotations(std::istream& in, const std::string& symbol,
char c = peek_next_nonws(in); char c = peek_next_nonws(in);
if (c == '{') { if (c == '{') {
if (! price.empty()) if (! price.empty())
throw amount_error("Commodity specifies more than one price"); throw new amount_error("Commodity specifies more than one price");
in.get(c); in.get(c);
READ_INTO(in, buf, 255, c, c != '}'); READ_INTO(in, buf, 255, c, c != '}');
if (c == '}') if (c == '}')
in.get(c); in.get(c);
else else
throw amount_error("Commodity price lacks closing brace"); throw new amount_error("Commodity price lacks closing brace");
price = buf; price = buf;
if (! added_name) { if (! added_name) {
added_name = true; added_name = true;
@ -962,14 +962,14 @@ void parse_annotations(std::istream& in, const std::string& symbol,
} }
else if (c == '[') { else if (c == '[') {
if (! date.empty()) if (! date.empty())
throw amount_error("Commodity specifies more than one date"); throw new amount_error("Commodity specifies more than one date");
in.get(c); in.get(c);
READ_INTO(in, buf, 255, c, c != ']'); READ_INTO(in, buf, 255, c, c != ']');
if (c == ']') if (c == ']')
in.get(c); in.get(c);
else else
throw amount_error("Commodity date lacks closing bracket"); throw new amount_error("Commodity date lacks closing bracket");
date = buf; date = buf;
if (! added_name) { if (! added_name) {
added_name = true; added_name = true;
@ -985,14 +985,14 @@ void parse_annotations(std::istream& in, const std::string& symbol,
} }
else if (c == '(') { else if (c == '(') {
if (! tag.empty()) if (! tag.empty())
throw amount_error("Commodity specifies more than one tag"); throw new amount_error("Commodity specifies more than one tag");
in.get(c); in.get(c);
READ_INTO(in, buf, 255, c, c != ')'); READ_INTO(in, buf, 255, c, c != ')');
if (c == ')') if (c == ')')
in.get(c); in.get(c);
else else
throw amount_error("Commodity tag lacks closing parenthesis"); throw new amount_error("Commodity tag lacks closing parenthesis");
tag = buf; tag = buf;
if (! added_name) { if (! added_name) {
added_name = true; added_name = true;
@ -1075,7 +1075,7 @@ void amount_t::parse(std::istream& in, unsigned char flags)
} }
if (quant.empty()) if (quant.empty())
throw amount_error("No quantity specified for amount"); throw new amount_error("No quantity specified for amount");
_init(); _init();
@ -1794,10 +1794,13 @@ void export_amount()
.add_property("commodity", .add_property("commodity",
make_function(&amount_t::commodity, make_function(&amount_t::commodity,
return_value_policy<reference_existing_object>()), return_value_policy<reference_existing_object>()),
&amount_t::set_commodity) make_function(&amount_t::set_commodity,
.add_property("quantity", py_amount_quantity) with_custodian_and_ward<1, 2>()))
.def("strip_annotations", &amount_t::strip_annotations)
.def("negate", &amount_t::negate) .def("negate", &amount_t::negate)
.def("negated", &amount_t::negated)
.def("parse", py_parse_1) .def("parse", py_parse_1)
.def("parse", py_parse_2) .def("parse", py_parse_2)
.def("reduce", &amount_t::reduce) .def("reduce", &amount_t::reduce)
@ -1816,24 +1819,32 @@ void export_amount()
scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN; scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET; scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
scope().attr("COMMODITY_STYLE_BUILTIN") = COMMODITY_STYLE_BUILTIN;
#if 0
class_< commodity_t > ("Commodity") class_< commodity_t > ("Commodity")
.add_property("symbol", &commodity_t::symbol) .add_property("symbol", &commodity_t::symbol)
.add_property("name", &commodity_t::name) #if 0
.add_property("note", &commodity_t::note) .add_property("name", &commodity_t::name, &commodity_t::set_name)
.add_property("precision", &commodity_t::precision) .add_property("note", &commodity_t::note, &commodity_t::set_note)
.add_property("flags", &commodity_t::flags) .add_property("precision", &commodity_t::precision,
&commodity_t::set_precision)
.add_property("flags", &commodity_t::flags, &commodity_t::set_flags)
.add_property("add_flags", &commodity_t::add_flags)
.add_property("drop_flags", &commodity_t::drop_flags)
#if 0 #if 0
.add_property("updater", &commodity_t::updater) .add_property("updater", &commodity_t::updater)
#endif #endif
.add_property("smaller", .add_property("smaller",
make_getter(&commodity_t::smaller, make_getter(&commodity_t::smaller,
return_value_policy<reference_existing_object>()),
make_setter(&commodity_t::smaller,
return_value_policy<reference_existing_object>())) return_value_policy<reference_existing_object>()))
.add_property("larger", .add_property("larger",
make_getter(&commodity_t::larger, make_getter(&commodity_t::larger,
return_value_policy<reference_existing_object>()),
make_setter(&commodity_t::larger,
return_value_policy<reference_existing_object>())) return_value_policy<reference_existing_object>()))
.def(self_ns::str(self)) .def(self_ns::str(self))
@ -1841,6 +1852,7 @@ void export_amount()
.def("find", py_find_commodity, .def("find", py_find_commodity,
return_value_policy<reference_existing_object>()) return_value_policy<reference_existing_object>())
.staticmethod("find") .staticmethod("find")
#endif
.def("add_price", &commodity_t::add_price) .def("add_price", &commodity_t::add_price)
.def("remove_price", &commodity_t::remove_price) .def("remove_price", &commodity_t::remove_price)
@ -1848,7 +1860,6 @@ void export_amount()
.def("valid", &commodity_t::valid) .def("valid", &commodity_t::valid)
; ;
#endif
#define EXC_TRANSLATE(type) \ #define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type); register_exception_translator<type>(&exc_translate_ ## type);

View file

@ -12,6 +12,7 @@
#include <exception> #include <exception>
#include "debug.h" #include "debug.h"
#include "error.h"
namespace ledger { namespace ledger {
@ -624,15 +625,10 @@ inline std::time_t amount_t::date() const {
} }
} }
class amount_error : public std::exception { class amount_error : public error {
std::string reason;
public: public:
amount_error(const std::string& _reason) throw() : reason(_reason) {} amount_error(const std::string& reason) throw() : error(reason) {}
virtual ~amount_error() throw() {} virtual ~amount_error() throw() {}
virtual const char* what() const throw() {
return reason.c_str();
}
}; };
} // namespace ledger } // namespace ledger

View file

@ -22,7 +22,7 @@ amount_t balance_t::amount(const commodity_t& commodity) const
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Requested amount of a balance with multiple commodities: " errmsg << "Requested amount of a balance with multiple commodities: "
<< *this; << *this;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
else if (amounts.size() > 0) { else if (amounts.size() > 0) {
@ -214,7 +214,7 @@ balance_t& balance_t::operator*=(const balance_t& bal)
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Cannot multiply two balances: " << *this << " * " << bal; errmsg << "Cannot multiply two balances: " << *this << " * " << bal;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
@ -255,7 +255,7 @@ balance_t& balance_t::operator*=(const amount_t& amt)
errmsg << "Attempt to multiply balance by a commodity" errmsg << "Attempt to multiply balance by a commodity"
<< " not found in that balance: " << " not found in that balance: "
<< *this << " * " << amt; << *this << " * " << amt;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
return *this; return *this;
@ -266,7 +266,7 @@ balance_t& balance_t::operator/=(const balance_t& bal)
if (bal.realzero()) { if (bal.realzero()) {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Attempt to divide by zero: " << *this << " / " << bal; errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
else if (realzero()) { else if (realzero()) {
return *this = 0L; return *this = 0L;
@ -285,7 +285,7 @@ balance_t& balance_t::operator/=(const balance_t& bal)
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Cannot divide between two balances: " << *this << " / " << bal; errmsg << "Cannot divide between two balances: " << *this << " / " << bal;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
@ -294,7 +294,7 @@ balance_t& balance_t::operator/=(const amount_t& amt)
if (amt.realzero()) { if (amt.realzero()) {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Attempt to divide by zero: " << *this << " / " << amt; errmsg << "Attempt to divide by zero: " << *this << " / " << amt;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
else if (realzero()) { else if (realzero()) {
return *this = 0L; return *this = 0L;
@ -326,7 +326,7 @@ balance_t& balance_t::operator/=(const amount_t& amt)
errmsg << "Attempt to divide balance by a commodity" errmsg << "Attempt to divide balance by a commodity"
<< " not found in that balance: " << " not found in that balance: "
<< *this << " * " << amt; << *this << " * " << amt;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
return *this; return *this;
@ -349,7 +349,7 @@ balance_t::operator amount_t() const
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Cannot convert a balance with " errmsg << "Cannot convert a balance with "
<< "multiple commodities to an amount: " << *this; << "multiple commodities to an amount: " << *this;
throw amount_error(errmsg.str()); throw new amount_error(errmsg.str());
} }
} }
@ -464,7 +464,6 @@ void export_balance()
.def("date", &balance_t::date) .def("date", &balance_t::date)
.def("strip_annotations", &balance_t::strip_annotations) .def("strip_annotations", &balance_t::strip_annotations)
.def("write", &balance_t::write) .def("write", &balance_t::write)
.def("abs", &balance_t::abs)
.def("round", &balance_t::round) .def("round", &balance_t::round)
.def("negate", &balance_t::negate) .def("negate", &balance_t::negate)
.def("negated", &balance_t::negated) .def("negated", &balance_t::negated)
@ -553,7 +552,6 @@ void export_balance()
.def("date", &balance_pair_t::date) .def("date", &balance_pair_t::date)
.def("strip_annotations", &balance_pair_t::strip_annotations) .def("strip_annotations", &balance_pair_t::strip_annotations)
.def("write", &balance_pair_t::write) .def("write", &balance_pair_t::write)
.def("abs", &balance_pair_t::abs)
.def("round", &balance_pair_t::round) .def("round", &balance_pair_t::round)
.def("negate", &balance_pair_t::negate) .def("negate", &balance_pair_t::negate)
.def("negated", &balance_pair_t::negated) .def("negated", &balance_pair_t::negated)

View file

@ -416,6 +416,16 @@ class balance_t
if ((*i).second.commodity()) if ((*i).second.commodity())
(*i).second = (*i).second.round(); (*i).second = (*i).second.round();
} }
balance_t unround() const {
balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second.commodity())
temp += (*i).second.unround();
return temp;
}
}; };
inline balance_t abs(const balance_t& bal) { inline balance_t abs(const balance_t& bal) {
@ -847,6 +857,13 @@ class balance_pair_t
quantity.round(); quantity.round();
if (cost) cost->round(); if (cost) cost->round();
} }
balance_pair_t unround() {
balance_pair_t temp(quantity.unround());
if (cost)
temp.cost = new balance_t(cost->unround());
return temp;
}
}; };
inline balance_pair_t abs(const balance_pair_t& bal_pair) { inline balance_pair_t abs(const balance_pair_t& bal_pair) {

View file

@ -621,9 +621,29 @@ unsigned int read_binary_journal(std::istream& in,
std::pair<base_commodities_map::iterator, bool> result = std::pair<base_commodities_map::iterator, bool> result =
commodity_base_t::commodities.insert commodity_base_t::commodities.insert
(base_commodities_pair(commodity->symbol, commodity)); (base_commodities_pair(commodity->symbol, commodity));
if (! result.second) if (! result.second) {
throw error(std::string("Failed to read base commodity from cache: ") + base_commodities_map::iterator c =
commodity_base_t::commodities.find(commodity->symbol);
// It's possible the user might have used a commodity in a value
// expression passed to an option, we'll just override the
// flags, but keep the commodity pointer intact.
if (c == commodity_base_t::commodities.end() ||
(*c).second->history || (*c).second->smaller ||
(*c).second->larger)
throw new error(std::string("Failed to read base commodity from cache: ") +
commodity->symbol); commodity->symbol);
(*c).second->name = commodity->name;
(*c).second->note = commodity->note;
(*c).second->precision = commodity->precision;
(*c).second->flags = commodity->flags;
(*c).second->history = commodity->history;
(*c).second->smaller = commodity->smaller;
(*c).second->larger = commodity->larger;
*(base_commodities_next - 1) = (*c).second;
}
} }
for (commodity_base_t::ident_t i = 0; i < bc_count; i++) for (commodity_base_t::ident_t i = 0; i < bc_count; i++)
@ -655,8 +675,8 @@ unsigned int read_binary_journal(std::istream& in,
std::pair<commodities_map::iterator, bool> result = std::pair<commodities_map::iterator, bool> result =
commodity_t::commodities.insert(commodities_pair(mapping_key, commodity_t::commodities.insert(commodities_pair(mapping_key,
commodity)); commodity));
if (! result.second) if (! result.second && commodity->annotated)
throw error(std::string("Failed to read commodity from cache: ") + throw new error(std::string("Failed to read commodity from cache: ") +
commodity->ptr->symbol); commodity->ptr->symbol);
} }

View file

@ -52,8 +52,9 @@ namespace {
void config_t::reset() void config_t::reset()
{ {
amount_expr = "a"; ledger::amount_expr.reset(new value_expr("a"));
total_expr = "O"; ledger::total_expr.reset(new value_expr("O"));
pricing_leeway = 24 * 3600; pricing_leeway = 24 * 3600;
budget_flags = BUDGET_NO_BUDGET; budget_flags = BUDGET_NO_BUDGET;
balance_format = "%20T %2_%-a\n"; balance_format = "%20T %2_%-a\n";
@ -293,7 +294,9 @@ void config_t::process_options(const std::string& command,
// Setup the values of %t and %T, used in format strings // Setup the values of %t and %T, used in format strings
if (! amount_expr.empty())
ledger::amount_expr.reset(new value_expr(amount_expr)); ledger::amount_expr.reset(new value_expr(amount_expr));
if (! total_expr.empty())
ledger::total_expr.reset(new value_expr(total_expr)); ledger::total_expr.reset(new value_expr(total_expr));
// If downloading is to be supported, configure the updater // If downloading is to be supported, configure the updater
@ -699,7 +702,7 @@ OPT_BEGIN(file, "f:") {
if (std::string(optarg) == "-" || access(optarg, R_OK) != -1) if (std::string(optarg) == "-" || access(optarg, R_OK) != -1)
config->data_file = optarg; config->data_file = optarg;
else else
throw error(std::string("The ledger file '") + optarg + throw new error(std::string("The ledger file '") + optarg +
"' does not exist or is not readable"); "' does not exist or is not readable");
} OPT_END(file); } OPT_END(file);
@ -747,7 +750,7 @@ OPT_BEGIN(begin, "b:") {
if (interval.begin) if (interval.begin)
std::strftime(buf, 127, formats[0], std::localtime(&interval.begin)); std::strftime(buf, 127, formats[0], std::localtime(&interval.begin));
else else
throw error(std::string("Could not determine beginning of period '") + throw new error(std::string("Could not determine beginning of period '") +
optarg + "'"); optarg + "'");
if (! config->predicate.empty()) if (! config->predicate.empty())
@ -763,7 +766,7 @@ OPT_BEGIN(end, "e:") {
if (interval.end) if (interval.end)
std::strftime(buf, 127, formats[0], std::localtime(&interval.end)); std::strftime(buf, 127, formats[0], std::localtime(&interval.end));
else else
throw error(std::string("Could not determine end of period '") + throw new error(std::string("Could not determine end of period '") +
optarg + "'"); optarg + "'");
if (! config->predicate.empty()) if (! config->predicate.empty())
@ -1048,11 +1051,11 @@ OPT_BEGIN(display, "d:") {
} OPT_END(display); } OPT_END(display);
OPT_BEGIN(amount, "t:") { OPT_BEGIN(amount, "t:") {
config->amount_expr = optarg; ledger::amount_expr.reset(new value_expr(optarg));
} OPT_END(amount); } OPT_END(amount);
OPT_BEGIN(total, "T:") { OPT_BEGIN(total, "T:") {
config->total_expr = optarg; ledger::total_expr.reset(new value_expr(optarg));
} OPT_END(total); } OPT_END(total);
OPT_BEGIN(amount_data, "j") { OPT_BEGIN(amount_data, "j") {
@ -1080,50 +1083,56 @@ OPT_BEGIN(download, "Q") {
} OPT_END(download); } OPT_END(download);
OPT_BEGIN(quantity, "O") { OPT_BEGIN(quantity, "O") {
config->amount_expr = "a"; ledger::amount_expr.reset(new value_expr("a"));
config->total_expr = "O"; ledger::total_expr.reset(new value_expr("O"));
} OPT_END(quantity); } OPT_END(quantity);
OPT_BEGIN(basis, "B") { OPT_BEGIN(basis, "B") {
config->amount_expr = "b"; ledger::amount_expr.reset(new value_expr("b"));
config->total_expr = "B"; ledger::total_expr.reset(new value_expr("B"));
} OPT_END(basis); } OPT_END(basis);
OPT_BEGIN(price, "I") { OPT_BEGIN(price, "I") {
config->amount_expr = "i"; ledger::amount_expr.reset(new value_expr("i"));
config->total_expr = "I"; ledger::total_expr.reset(new value_expr("I"));
} OPT_END(price); } OPT_END(price);
OPT_BEGIN(market, "V") { OPT_BEGIN(market, "V") {
config->show_revalued = true; config->show_revalued = true;
config->amount_expr = "v"; ledger::amount_expr.reset(new value_expr("v"));
config->total_expr = "V"; ledger::total_expr.reset(new value_expr("V"));
} OPT_END(market); } OPT_END(market);
OPT_BEGIN(performance, "g") { OPT_BEGIN(performance, "g") {
config->amount_expr = "P(a,m)-b"; // same as 'g', but priced now ledger::amount_expr.reset(new value_expr("P(a,m)-b"));
config->total_expr = "P(O,m)-B"; ledger::total_expr.reset(new value_expr("P(O,m)-B"));
} OPT_END(performance); } OPT_END(performance);
OPT_BEGIN(gain, "G") { OPT_BEGIN(gain, "G") {
config->show_revalued = config->show_revalued =
config->show_revalued_only = true; config->show_revalued_only = true;
config->amount_expr = "a"; ledger::amount_expr.reset(new value_expr("a"));
config->total_expr = "G"; ledger::total_expr.reset(new value_expr("G"));
} OPT_END(gain); } OPT_END(gain);
OPT_BEGIN(average, "A") { OPT_BEGIN(average, "A") {
config->total_expr = expand_value_expr("A(#)", config->total_expr); ledger::total_expr.reset
(new value_expr(expand_value_expr("A(#)", ledger::total_expr->expr)));
} OPT_END(average); } OPT_END(average);
OPT_BEGIN(deviation, "D") { OPT_BEGIN(deviation, "D") {
config->total_expr = expand_value_expr("t-A(#)", config->total_expr); ledger::total_expr.reset(new value_expr("O"));
ledger::total_expr.reset
(new value_expr(expand_value_expr("t-A(#)", ledger::total_expr->expr)));
} OPT_END(deviation); } OPT_END(deviation);
OPT_BEGIN(percentage, "%") { OPT_BEGIN(percentage, "%") {
config->total_expr = expand_value_expr("^#&{100.0%}*(#/^#)", config->total_expr); ledger::total_expr.reset(new value_expr("O"));
ledger::total_expr.reset
(new value_expr(expand_value_expr("^#&{100.0%}*(#/^#)",
ledger::total_expr->expr)));
} OPT_END(percentage); } OPT_END(percentage);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View file

@ -107,7 +107,7 @@ static void parse_inclusion_specifier(const std::string& word,
struct std::tm when; struct std::tm when;
if (! parse_date_mask(word.c_str(), &when)) if (! parse_date_mask(word.c_str(), &when))
throw datetime_error(std::string("Could not parse date mask: ") + word); throw new datetime_error(std::string("Could not parse date mask: ") + word);
when.tm_hour = 0; when.tm_hour = 0;
when.tm_min = 0; when.tm_min = 0;

View file

@ -4,6 +4,8 @@
#include <ctime> #include <ctime>
#include <sstream> #include <sstream>
#include "error.h"
struct interval_t; struct interval_t;
struct datetime_t struct datetime_t
@ -69,6 +71,10 @@ inline std::ostream& operator<<(std::ostream& out, const datetime_t& moment) {
out << buf; out << buf;
} }
inline long operator-(const datetime_t& left, const datetime_t& right) {
return (long)left.when - (long)right.when;
}
struct interval_t struct interval_t
{ {
unsigned int years; unsigned int years;
@ -120,15 +126,10 @@ bool parse_date(const char * date_str, std::time_t * result,
const int year = -1); const int year = -1);
bool quick_parse_date(const char * date_str, std::time_t * result); bool quick_parse_date(const char * date_str, std::time_t * result);
class datetime_error : public std::exception { class datetime_error : public error {
std::string reason;
public: public:
datetime_error(const std::string& _reason) throw() : reason(_reason) {} datetime_error(const std::string& reason) throw() : error(reason) {}
virtual ~datetime_error() throw() {} virtual ~datetime_error() throw() {}
virtual const char* what() const throw() {
return reason.c_str();
}
}; };
#endif // _DATETIME_H #endif // _DATETIME_H

View file

@ -4,8 +4,6 @@
#include <map> #include <map>
#include <fstream> #include <fstream>
#include <cstdlib>
#include <cstring>
#include <unistd.h> // for the `write' method #include <unistd.h> // for the `write' method
@ -112,3 +110,16 @@ static struct init_streams {
} _debug_init; } _debug_init;
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
#if DEBUG_LEVEL >= BETA
#include <string>
void debug_assert(const std::string& reason,
const std::string& file,
unsigned long line)
{
throw new fatal_assert(reason, new file_context(file, line));
}
#endif

25
debug.h
View file

@ -12,7 +12,28 @@
#endif #endif
#if DEBUG_LEVEL >= RELEASE #if DEBUG_LEVEL >= RELEASE
#include <cassert> #include "error.h"
#ifdef assert
#undef assert
#endif
#if DEBUG_LEVEL >= BETA
void debug_assert(const std::string& reason,
const std::string& file,
unsigned long line);
#define assert(x) \
if (! (x)) \
debug_assert(#x, __FILE__, __LINE__)
#else
#define assert(x) \
if (! (x)) \
throw new fatal_assert(#x, new file_context(__FILE__, __LINE__))
#endif
#else
#ifdef assert
#undef assert
#endif
#define assert(x)
#endif #endif
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -105,7 +126,9 @@ void operator delete[](void*, const std::nothrow_t&) throw();
#if DEBUG_LEVEL == NO_SEATBELT #if DEBUG_LEVEL == NO_SEATBELT
#ifdef assert
#undef assert #undef assert
#endif
#define assert(x) #define assert(x)
#define CONFIRM(x) #define CONFIRM(x)

View file

@ -17,10 +17,10 @@ entry_t * derive_new_entry(journal_t& journal,
entry_t * matching = NULL; entry_t * matching = NULL;
if (! parse_date((*i).c_str(), &added->_date)) if (! parse_date((*i).c_str(), &added->_date))
throw error("Bad date passed to 'entry'"); throw new error("Bad date passed to 'entry'");
if (++i == end) if (++i == end)
throw error("Too few arguments to 'entry'"); throw new error("Too few arguments to 'entry'");
mask_t regexp(*i++); mask_t regexp(*i++);
@ -169,7 +169,7 @@ entry_t * derive_new_entry(journal_t& journal,
done: done:
if (! run_hooks(journal.entry_finalize_hooks, *added) || if (! run_hooks(journal.entry_finalize_hooks, *added) ||
! added->finalize()) ! added->finalize())
throw error("Failed to finalize derived entry (check commodities)"); throw new error("Failed to finalize derived entry (check commodities)");
return added.release(); return added.release();
} }

208
error.h
View file

@ -1,63 +1,231 @@
#ifndef _ERROR_H #ifndef _ERROR_H
#define _ERROR_H #define _ERROR_H
#include "journal.h"
#include <exception> #include <exception>
#include <string> #include <string>
#include <cstring>
#include <sstream> #include <sstream>
#include <list>
namespace ledger { class error_context
{
public:
std::string desc;
class error : public std::exception { error_context(const std::string& _desc) throw() : desc(_desc) {}
virtual ~error_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << std::endl;
}
};
class file_context : public error_context
{
protected:
std::string file;
unsigned long line;
public:
file_context(const std::string& _file, unsigned long _line,
const std::string& desc = "") throw()
: file(_file), line(_line), error_context(desc) {}
virtual ~file_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << " ";
out << "\"" << file << "\", line " << line << ": ";
}
};
namespace ledger { class value_t; }
class value_context : public error_context
{
ledger::value_t * bal;
public:
value_context(const ledger::value_t& _bal,
const std::string& desc = "") throw();
virtual ~value_context() throw();
virtual void describe(std::ostream& out) const throw();
};
namespace ledger { class value_expr_t; }
class valexpr_context : public error_context {
public:
const ledger::value_expr_t * expr;
const ledger::value_expr_t * error_node;
valexpr_context(const ledger::value_expr_t * _expr,
const std::string& desc = "") throw();
virtual ~valexpr_context() throw();
virtual void describe(std::ostream& out) const throw();
};
class line_context : public error_context {
public:
std::string line;
long pos;
line_context(const std::string& _line, long _pos,
const std::string& desc = "") throw()
: line(_line), pos(_pos), error_context(desc) {}
virtual ~line_context() throw() {}
virtual void describe(std::ostream& out) const throw();
};
class include_context : public file_context {
public:
include_context(const std::string& file, unsigned long line,
const std::string& desc = "") throw()
: file_context(file, line, desc) {}
virtual ~include_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << ": ";
out << "\"" << file << "\", line " << line << ":" << std::endl;
}
};
namespace ledger { class entry_base_t; }
class entry_context : public error_context {
public:
const ledger::entry_base_t& entry;
entry_context(const ledger::entry_base_t& _entry,
const std::string& desc = "") throw()
: entry(_entry), error_context(desc) {}
virtual ~entry_context() throw() {}
virtual void describe(std::ostream& out) const throw();
};
namespace ledger { class transaction_t; }
class xact_context : public file_context {
public:
const ledger::transaction_t& xact;
xact_context(const ledger::transaction_t& _xact,
const std::string& desc = "") throw();
virtual ~xact_context() throw() {}
};
//////////////////////////////////////////////////////////////////////
class str_exception : public std::exception {
protected:
std::string reason; std::string reason;
public: public:
error(const std::string& _reason) throw() : reason(_reason) {} std::list<error_context *> context;
virtual ~error() throw() {}
str_exception(const std::string& _reason,
error_context * ctxt = NULL) throw()
: reason(_reason) {
if (ctxt)
context.push_back(ctxt);
}
virtual ~str_exception() throw() {
for (std::list<error_context *>::iterator i = context.begin();
i != context.end();
i++)
delete *i;
}
virtual void reveal_context(std::ostream& out,
const std::string& kind) const throw() {
for (std::list<error_context *>::const_reverse_iterator i =
context.rbegin();
i != context.rend();
i++) {
std::list<error_context *>::const_reverse_iterator x = i;
if (++x == context.rend())
out << kind << ": ";
(*i)->describe(out);
}
}
virtual const char* what() const throw() { virtual const char* what() const throw() {
return reason.c_str(); return reason.c_str();
} }
}; };
class error : public str_exception {
public:
error(const std::string& reason, error_context * ctxt = NULL) throw()
: str_exception(reason, ctxt) {}
virtual ~error() throw() {}
};
class fatal : public str_exception {
public:
fatal(const std::string& reason, error_context * ctxt = NULL) throw()
: str_exception(reason, ctxt) {}
virtual ~fatal() throw() {}
};
class fatal_assert : public fatal {
public:
fatal_assert(const std::string& reason, error_context * ctxt = NULL) throw()
: fatal(std::string("assertion failed '") + reason + "'", ctxt) {}
virtual ~fatal_assert() throw() {}
};
namespace ledger {
class compute_error : public error { class compute_error : public error {
public: public:
compute_error(const std::string& reason) throw() : error(reason) {} compute_error(const std::string& reason, error_context * ctxt = NULL) throw()
: error(reason, ctxt) {}
virtual ~compute_error() throw() {} virtual ~compute_error() throw() {}
}; };
class value_expr_error : public error { class value_expr_error : public error {
public: public:
value_expr_error(const std::string& reason) throw() : error(reason) {} value_expr_error(const std::string& reason,
error_context * ctxt = NULL) throw()
: error(reason, ctxt) {}
virtual ~value_expr_error() throw() {} virtual ~value_expr_error() throw() {}
}; };
class interval_expr_error : public error { class interval_expr_error : public error {
public: public:
interval_expr_error(const std::string& reason) throw() : error(reason) {} interval_expr_error(const std::string& reason,
error_context * ctxt = NULL) throw()
: error(reason, ctxt) {}
virtual ~interval_expr_error() throw() {} virtual ~interval_expr_error() throw() {}
}; };
class format_error : public error { class format_error : public error {
public: public:
format_error(const std::string& reason) throw() : error(reason) {} format_error(const std::string& reason, error_context * ctxt = NULL) throw()
: error(reason, ctxt) {}
virtual ~format_error() throw() {} virtual ~format_error() throw() {}
}; };
class parse_error : public error { class parse_error : public error {
unsigned int line;
std::string file;
public: public:
parse_error(const std::string& _file, const unsigned int _line, parse_error(const std::string& reason, error_context * ctxt = NULL) throw()
const std::string& reason) throw() : error(reason, ctxt) {}
: error(reason), line(_line), file(_file) {}
virtual ~parse_error() throw() {} virtual ~parse_error() throw() {}
};
virtual const char* what() const throw() { class value_error : public error {
std::ostringstream msg; public:
msg << file << ", line " << line << ": " << error::what(); value_error(const std::string& reason, error_context * ctxt = NULL) throw()
return msg.str().c_str(); : error(reason, ctxt) {}
} virtual ~value_error() throw() {}
};
class balance_error : public error {
public:
balance_error(const std::string& reason, error_context * ctxt = NULL) throw()
: error(reason, ctxt) {}
virtual ~balance_error() throw() {}
}; };
} // namespace ledger } // namespace ledger

View file

@ -157,7 +157,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
p++; p++;
} }
if (*p != ')') if (*p != ')')
throw format_error("Missing ')'"); throw new format_error("Missing ')'");
current->type = element_t::VALUE_EXPR; current->type = element_t::VALUE_EXPR;
@ -178,7 +178,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
p++; p++;
} }
if (*p != ']') if (*p != ']')
throw format_error("Missing ']'"); throw new format_error("Missing ']'");
current->type = element_t::DATE_STRING; current->type = element_t::DATE_STRING;
current->chars = std::string(b, p); current->chars = std::string(b, p);
@ -287,7 +287,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::AMOUNT: case element_t::AMOUNT:
case element_t::TOTAL: case element_t::TOTAL:
case element_t::VALUE_EXPR: { case element_t::VALUE_EXPR: {
value_calc * calc = NULL; value_expr * calc = NULL;
switch (elem->type) { switch (elem->type) {
case element_t::AMOUNT: calc = amount_expr.get(); break; case element_t::AMOUNT: calc = amount_expr.get(); break;
case element_t::TOTAL: calc = total_expr.get(); break; case element_t::TOTAL: calc = total_expr.get(); break;
@ -681,18 +681,37 @@ void format_entries::operator()(transaction_t& xact)
last_entry = xact.entry; last_entry = xact.entry;
} }
void print_entry(std::ostream& out, const entry_t& entry) void print_entry(std::ostream& out, const entry_base_t& entry_base,
const std::string& prefix)
{ {
const std::string print_format std::string print_format;
= "\n%D %X%C%P\n %-34A %12o\n%/ %-34A %12o\n";
if (const entry_t * entry = dynamic_cast<const entry_t *>(&entry_base)) {
print_format = (prefix + "%D %X%C%P\n" +
prefix + " %-34A %12o\n%/" +
prefix + " %-34A %12o\n");
}
else if (const auto_entry_t * entry =
dynamic_cast<const auto_entry_t *>(&entry_base)) {
out << "= " << entry->predicate_string << '\n';
print_format = prefix + " %-34A %12o\n";
}
else if (const period_entry_t * entry =
dynamic_cast<const period_entry_t *>(&entry_base)) {
out << "~ " << entry->period_string << '\n';
print_format = prefix + " %-34A %12o\n";
}
else {
assert(0);
}
format_entries formatter(out, print_format); format_entries formatter(out, print_format);
walk_transactions(const_cast<transactions_list&>(entry.transactions), walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
formatter); formatter);
formatter.flush(); formatter.flush();
clear_transaction_xdata cleaner; clear_transaction_xdata cleaner;
walk_transactions(const_cast<transactions_list&>(entry.transactions), walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
cleaner); cleaner);
} }

View file

@ -133,7 +133,8 @@ class format_entries : public format_transactions
virtual void operator()(transaction_t& xact); virtual void operator()(transaction_t& xact);
}; };
void print_entry(std::ostream& out, const entry_t& entry); void print_entry(std::ostream& out, const entry_base_t& entry,
const std::string& prefix = "");
bool disp_subaccounts_p(const account_t& account, bool disp_subaccounts_p(const account_t& account,
const item_predicate<account_t>& disp_pred, const item_predicate<account_t>& disp_pred,

View file

@ -415,12 +415,12 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; unsigned long line = XML_GetCurrentLineNumber(parser) - offset++;
const char * msg = XML_ErrorString(XML_GetErrorCode(parser)); const char * msg = XML_ErrorString(XML_GetErrorCode(parser));
XML_ParserFree(parser); XML_ParserFree(parser);
throw parse_error(path, line, msg); throw new parse_error(msg);
} }
if (! have_error.empty()) { if (! have_error.empty()) {
unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; unsigned long line = XML_GetCurrentLineNumber(parser) - offset++;
parse_error err(path, line, have_error); parse_error err(have_error);
std::cerr << "Error: " << err.what() << std::endl; std::cerr << "Error: " << err.what() << std::endl;
have_error = ""; have_error = "";
} }

View file

@ -79,15 +79,13 @@ bool entry_base_t::remove_transaction(transaction_t * xact)
return true; return true;
} }
value_t entry_balance;
bool entry_base_t::finalize() bool entry_base_t::finalize()
{ {
// Scan through and compute the total balance for the entry. This // Scan through and compute the total balance for the entry. This
// is used for auto-calculating the value of entries with no cost, // is used for auto-calculating the value of entries with no cost,
// and the per-unit price of unpriced commodities. // and the per-unit price of unpriced commodities.
value_t& balance = entry_balance; value_t balance;
bool no_amounts = true; bool no_amounts = true;
for (transactions_list::const_iterator x = transactions.begin(); for (transactions_list::const_iterator x = transactions.begin();
@ -106,7 +104,8 @@ bool entry_base_t::finalize()
if ((*x)->cost && (*x)->amount.commodity().annotated) { if ((*x)->cost && (*x)->amount.commodity().annotated) {
annotated_commodity_t& annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&>((*x)->amount.commodity())); ann_comm(static_cast<annotated_commodity_t&>
((*x)->amount.commodity()));
if (ann_comm.price) if (ann_comm.price)
balance += ann_comm.price * (*x)->amount - *((*x)->cost); balance += ann_comm.price * (*x)->amount - *((*x)->cost);
} }
@ -240,7 +239,16 @@ bool entry_base_t::finalize()
} }
} }
return ! balance; if (balance) {
error * err =
new balance_error("Entry does not balance",
new entry_context(*this, "While balancing entry:"));
err->context.push_front
(new value_context(balance, "Unbalanced remainder is:"));
throw err;
}
return true;
} }
entry_t::entry_t(const entry_t& e) entry_t::entry_t(const entry_t& e)

View file

@ -151,8 +151,6 @@ class entry_base_t
virtual bool valid() const = 0; virtual bool valid() const = 0;
}; };
extern value_t entry_balance;
class entry_t : public entry_base_t class entry_t : public entry_base_t
{ {
public: public:

View file

@ -5,7 +5,7 @@
@dircategory User Applications @dircategory User Applications
@copying @copying
Copyright (c) 2003-2004, John Wiegley. All rights reserved. Copyright (c) 2003-2006, John Wiegley. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

61
main.cc
View file

@ -121,11 +121,16 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
else if (command == "parse") { else if (command == "parse") {
value_auto_ptr expr(ledger::parse_value_expr(*arg)); value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.verbose_mode) { if (config.verbose_mode) {
std::cout << "Value expression tree:" << std::endl;
ledger::dump_value_expr(std::cout, expr.get()); ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl; std::cout << std::endl;
std::cout << "Value expression parsed was:" << std::endl;
ledger::write_value_expr(std::cout, expr.get());
std::cout << std::endl << std::endl;
std::cout << "Result of computation: ";
} }
value_t result = guarded_compute(expr.get());
value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) { if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
switch (result.type) { switch (result.type) {
case value_t::AMOUNT: case value_t::AMOUNT:
@ -138,13 +143,15 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
} }
} }
std::cout << result << std::endl; std::cout << result << std::endl;
return 0; return 0;
} }
else if (command == "expr") { else if (command == "expr") {
// this gets done below... // this gets done below...
} }
else else {
throw error(std::string("Unrecognized command '") + command + "'"); throw new error(std::string("Unrecognized command '") + command + "'");
}
// Parse initialization files, ledger data, price database, etc. // Parse initialization files, ledger data, price database, etc.
@ -153,7 +160,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
{ TRACE_PUSH(parser, "Parsing journal file"); { TRACE_PUSH(parser, "Parsing journal file");
if (parse_ledger_data(config, journal.get()) == 0) if (parse_ledger_data(config, journal.get()) == 0)
throw error("Please specify ledger file using -f" throw new error("Please specify ledger file using -f"
" or LEDGER_FILE environment variable."); " or LEDGER_FILE environment variable.");
TRACE_POP(parser, "Finished parsing"); } TRACE_POP(parser, "Finished parsing"); }
@ -163,7 +170,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
std::string first_arg; std::string first_arg;
if (command == "w") { if (command == "w") {
if (arg == args.end()) if (arg == args.end())
throw error("The 'output' command requires a file argument"); throw new error("The 'output' command requires a file argument");
first_arg = *arg++; first_arg = *arg++;
} }
@ -193,11 +200,11 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
else if (! config.pager.empty()) { else if (! config.pager.empty()) {
status = pipe(pfd); status = pipe(pfd);
if (status == -1) if (status == -1)
throw error("Failed to create pipe"); throw new error("Failed to create pipe");
status = fork(); status = fork();
if (status < 0) { if (status < 0) {
throw error("Failed to fork child process"); throw new error("Failed to fork child process");
} }
else if (status == 0) { // child else if (status == 0) { // child
const char *arg0; const char *arg0;
@ -238,10 +245,16 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
if (command == "expr") { if (command == "expr") {
value_auto_ptr expr(ledger::parse_value_expr(*arg)); value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.verbose_mode) { if (config.verbose_mode) {
std::cout << "Value expression tree:" << std::endl;
ledger::dump_value_expr(std::cout, expr.get()); ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl; std::cout << std::endl;
std::cout << "Value expression parsed was:" << std::endl;
ledger::write_value_expr(std::cout, expr.get(), NULL, 0);
std::cout << std::endl << std::endl;
std::cout << "Result of computation: ";
} }
value_t result = expr->compute(); value_t result = guarded_compute(expr.get());
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) { if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
switch (result.type) { switch (result.type) {
case value_t::AMOUNT: case value_t::AMOUNT:
@ -254,6 +267,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
} }
} }
std::cout << result << std::endl; std::cout << result << std::endl;
return 0; return 0;
} }
@ -293,7 +307,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) #if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
formatter = new format_xml_entries(*out, config.show_totals); formatter = new format_xml_entries(*out, config.show_totals);
#else #else
throw error("XML support was not compiled into this copy of Ledger"); throw new error("XML support was not compiled into this copy of Ledger");
#endif #endif
} else } else
formatter = new format_transactions(*out, *format); formatter = new format_transactions(*out, *format);
@ -392,7 +406,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
// Wait for child to finish // Wait for child to finish
wait(&status); wait(&status);
if (status & 0xffff != 0) if (status & 0xffff != 0)
throw error("Something went wrong in the pager"); throw new error("Something went wrong in the pager");
} }
#endif #endif
@ -411,12 +425,35 @@ int main(int argc, char * argv[], char * envp[])
TRACE_POP(main, "Ledger done"); TRACE_POP(main, "Ledger done");
return status; return status;
} }
catch (error * err) {
std::cout.flush();
// Push a null here since there's no file context
if (err->context.empty() ||
! dynamic_cast<xact_context *>(err->context.front()))
err->context.push_front(new error_context(""));
err->reveal_context(std::cerr, "Error");
std::cerr << err->what() << std::endl;
delete err;
return 1;
}
catch (fatal * err) {
std::cout.flush();
// Push a null here since there's no file context
if (err->context.empty() ||
! dynamic_cast<xact_context *>(err->context.front()))
err->context.push_front(new error_context(""));
err->reveal_context(std::cerr, "Fatal");
std::cerr << err->what() << std::endl;
delete err;
return 1;
}
catch (const std::exception& err) { catch (const std::exception& err) {
std::cout.flush();
std::cerr << "Error: " << err.what() << std::endl; std::cerr << "Error: " << err.what() << std::endl;
return 1; return 1;
} }
catch (int& val) { catch (int status) {
return val; // this acts like a std::setjmp return status;
} }
} }

View file

@ -27,7 +27,8 @@ mask_t::mask_t(const std::string& pat) : exclude(false)
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS, regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
&error, &erroffset, NULL); &error, &erroffset, NULL);
if (! regexp) if (! regexp)
throw mask_error(std::string("Failed to compile regexp '") + pattern + "'"); throw new mask_error(std::string("Failed to compile regexp '") +
pattern + "'");
} }
mask_t::mask_t(const mask_t& m) : exclude(m.exclude), pattern(m.pattern) mask_t::mask_t(const mask_t& m) : exclude(m.exclude), pattern(m.pattern)

11
mask.h
View file

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <exception> #include <exception>
#include "error.h"
class mask_t class mask_t
{ {
public: public:
@ -18,15 +20,10 @@ class mask_t
bool match(const std::string& str) const; bool match(const std::string& str) const;
}; };
class mask_error : public std::exception { class mask_error : public error {
std::string reason;
public: public:
mask_error(const std::string& _reason) throw() : reason(_reason) {} mask_error(const std::string& reason) throw() : error(reason) {}
virtual ~mask_error() throw() {} virtual ~mask_error() throw() {}
virtual const char* what() const throw() {
return reason.c_str();
}
}; };
#endif // _MASK_H #endif // _MASK_H

View file

@ -1,6 +1,7 @@
#include "option.h" #include "option.h"
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
#include "error.h"
#include <iostream> #include <iostream>
#include <cstdarg> #include <cstdarg>
@ -10,7 +11,17 @@
namespace { namespace {
inline void process_option(option_t * opt, const char * arg = NULL) { inline void process_option(option_t * opt, const char * arg = NULL) {
if (! opt->handled) { if (! opt->handled) {
try {
opt->handler(arg); opt->handler(arg);
}
catch (error * err) {
err->context.push_back
(new error_context
(std::string("While parsing option '--") + opt->long_opt +
"'" + (opt->short_opt != '\0' ?
(std::string(" (-") + opt->short_opt + "):") : ":")));
throw err;
}
opt->handled = true; opt->handled = true;
} }
} }
@ -49,9 +60,8 @@ bool process_option(option_t * options, const std::string& name,
const char * arg) const char * arg)
{ {
option_t * opt = search_options(options, name.c_str()); option_t * opt = search_options(options, name.c_str());
if (opt && ! opt->handled) { if (opt) {
opt->handler(arg); process_option(opt, arg);
opt->handled = true;
return true; return true;
} }
return false; return false;
@ -90,12 +100,12 @@ void process_arguments(option_t * options, int argc, char ** argv,
opt = search_options(options, name); opt = search_options(options, name);
if (! opt) if (! opt)
throw option_error(std::string("illegal option --") + name); throw new option_error(std::string("illegal option --") + name);
if (opt->wants_arg && ! value) { if (opt->wants_arg && ! value) {
value = *++i; value = *++i;
if (! value) if (! value)
throw option_error(std::string("missing option argument for --") + throw new option_error(std::string("missing option argument for --") +
name); name);
} }
process_option(opt, value); process_option(opt, value);
@ -103,12 +113,12 @@ void process_arguments(option_t * options, int argc, char ** argv,
char c = (*i)[1]; char c = (*i)[1];
opt = search_options(options, c); opt = search_options(options, c);
if (! opt) if (! opt)
throw option_error(std::string("illegal option -") + c); throw new option_error(std::string("illegal option -") + c);
if (opt->wants_arg) { if (opt->wants_arg) {
value = *++i; value = *++i;
if (! value) if (! value)
throw option_error(std::string("missing option argument for -") + c); throw new option_error(std::string("missing option argument for -") + c);
} }
} }
@ -141,7 +151,18 @@ void process_environment(option_t * options, char ** envp,
*r++ = std::tolower(*q); *r++ = std::tolower(*q);
*r = '\0'; *r = '\0';
if (*q == '=') if (*q == '=') {
try {
process_option(options, buf, q + 1); process_option(options, buf, q + 1);
} }
catch (error * err) {
err->context.pop_back();
err->context.push_back
(new error_context
(std::string("While parsing environment variable option '") +
*p + "':"));
throw err;
}
}
}
} }

View file

@ -5,6 +5,8 @@
#include <string> #include <string>
#include <exception> #include <exception>
#include "error.h"
typedef void (*handler_t)(const char * arg); typedef void (*handler_t)(const char * arg);
struct option_t { struct option_t {
@ -15,15 +17,10 @@ struct option_t {
bool handled; bool handled;
}; };
class option_error : public std::exception { class option_error : public error {
std::string reason;
public: public:
option_error(const std::string& _reason) throw() : reason(_reason) {} option_error(const std::string& reason) throw() : error(reason) {}
virtual ~option_error() throw() {} virtual ~option_error() throw() {}
virtual const char* what() const throw() {
return reason.c_str();
}
}; };
bool process_option(option_t * options, const std::string& opt, bool process_option(option_t * options, const std::string& opt,

View file

@ -83,7 +83,7 @@ unsigned int parse_journal_file(const std::string& path,
journal->sources.push_back(path); journal->sources.push_back(path);
if (access(path.c_str(), R_OK) == -1) if (access(path.c_str(), R_OK) == -1)
throw error(std::string("Cannot read file '") + path + "'"); throw new error(std::string("Cannot read file '") + path + "'");
if (! original_file) if (! original_file)
original_file = &path; original_file = &path;
@ -119,7 +119,7 @@ unsigned int parse_ledger_data(config_t& config,
if (parse_journal_file(config.init_file, config, journal) || if (parse_journal_file(config.init_file, config, journal) ||
journal->auto_entries.size() > 0 || journal->auto_entries.size() > 0 ||
journal->period_entries.size() > 0) journal->period_entries.size() > 0)
throw error(std::string("Entries found in initialization file '") + throw new error(std::string("Entries found in initialization file '") +
config.init_file + "'"); config.init_file + "'");
journal->sources.pop_front(); // remove init file journal->sources.pop_front(); // remove init file
@ -154,7 +154,7 @@ unsigned int parse_ledger_data(config_t& config,
if (! journal->price_db.empty() && if (! journal->price_db.empty() &&
access(journal->price_db.c_str(), R_OK) != -1) { access(journal->price_db.c_str(), R_OK) != -1) {
if (parse_journal_file(journal->price_db, config, journal)) { if (parse_journal_file(journal->price_db, config, journal)) {
throw error("Entries not allowed in price history file"); throw new error("Entries not allowed in price history file");
} else { } else {
DEBUG_PRINT("ledger.config.cache", DEBUG_PRINT("ledger.config.cache",
"read price database " << journal->price_db); "read price database " << journal->price_db);

7
qif.cc
View file

@ -80,7 +80,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
case '\t': case '\t':
if (peek_next_nonws(in) != '\n') { if (peek_next_nonws(in) != '\n') {
get_line(in); get_line(in);
throw parse_error(path, linenum, "Line begins with whitespace"); throw new parse_error("Line begins with whitespace");
} }
// fall through... // fall through...
@ -97,8 +97,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
std::strcmp(line, "Type:Cat") == 0 || std::strcmp(line, "Type:Cat") == 0 ||
std::strcmp(line, "Type:Class") == 0 || std::strcmp(line, "Type:Class") == 0 ||
std::strcmp(line, "Type:Memorized") == 0) std::strcmp(line, "Type:Memorized") == 0)
throw parse_error(path, linenum, throw new parse_error(std::string("QIF files of type ") + line +
std::string("QIF files of type ") + line +
" are not supported."); " are not supported.");
break; break;
@ -106,7 +105,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
SET_BEG_POS_AND_LINE(); SET_BEG_POS_AND_LINE();
get_line(in); get_line(in);
if (! parse_date(line, &entry->_date)) if (! parse_date(line, &entry->_date))
throw parse_error(path, linenum, "Failed to parse date"); throw new parse_error("Failed to parse date");
break; break;
case 'T': case 'T':

View file

@ -75,7 +75,7 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
<< " " << price << endl; << " " << price << endl;
} }
} else { } else {
throw error(std::string("Failed to download price for '") + throw new error(std::string("Failed to download price for '") +
commodity.symbol + "' (command: \"getquote " + commodity.symbol + "' (command: \"getquote " +
commodity.symbol + "\")"); commodity.symbol + "\")");
} }

View file

@ -60,7 +60,7 @@ void reconcile_transactions::flush()
} }
if (cleared_balance.type >= value_t::BALANCE) if (cleared_balance.type >= value_t::BALANCE)
throw error("Cannot reconcile accounts with multiple commodities"); throw new error("Cannot reconcile accounts with multiple commodities");
cleared_balance.cast(value_t::AMOUNT); cleared_balance.cast(value_t::AMOUNT);
balance.cast(value_t::AMOUNT); balance.cast(value_t::AMOUNT);
@ -70,7 +70,7 @@ void reconcile_transactions::flush()
balance -= cleared_balance; balance -= cleared_balance;
if (balance.type >= value_t::BALANCE) if (balance.type >= value_t::BALANCE)
throw error(std::string("Reconcile balance is not of the same commodity ('") + throw new error(std::string("Reconcile balance is not of the same commodity ('") +
b_comm.symbol() + "' != '" + cb_comm.symbol() + "')"); b_comm.symbol() + "' != '" + cb_comm.symbol() + "')");
// If the amount to reconcile is the same as the pending balance, // If the amount to reconcile is the same as the pending balance,
@ -81,7 +81,7 @@ void reconcile_transactions::flush()
search_for_balance(to_reconcile, &first, first)) { search_for_balance(to_reconcile, &first, first)) {
push_to_handler(first); push_to_handler(first);
} else { } else {
throw error("Could not reconcile account!"); throw new error("Could not reconcile account!");
} }
} }

24
test.py
View file

@ -2,15 +2,21 @@
from amounts import * from amounts import *
amt_ncd = Amount("100")
print amt_ncd
amt_nc = Amount("100.00")
print amt_nc
amt = Amount("$100.00")
print amt
namt_ncd = Amount("-100")
print namt_ncd
namt_nc = Amount("-100.00")
print namt_nc
namt = Amount("$-100.00")
print namt
namt2 = Amount("-$100.00")
print namt2
val = Value("$100.00") val = Value("$100.00")
print val print val
amt = Amount("$100.00")
print amt.commodity
amt.commodity = amt.commodity
print amt
amt.commodity = NullCommodity
print amt
print amt.quantity

View file

@ -69,6 +69,9 @@ transaction_t * parse_transaction(char * line, account_t * account,
{ {
std::istringstream in(line); std::istringstream in(line);
std::string err_desc;
try {
// The account will be determined later... // The account will be determined later...
std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); std::auto_ptr<transaction_t> xact(new transaction_t(NULL));
if (entry) if (entry)
@ -107,7 +110,7 @@ transaction_t * parse_transaction(char * line, account_t * account,
} }
if (account_beg == account_end) if (account_beg == account_end)
throw parse_error(path, linenum, "No account was specified"); throw new parse_error("No account was specified");
char * b = &line[account_beg]; char * b = &line[account_beg];
char * e = &line[account_end]; char * e = &line[account_end];
@ -144,7 +147,13 @@ transaction_t * parse_transaction(char * line, account_t * account,
if (p == ';') if (p == ';')
goto parse_note; goto parse_note;
if (p == '(') { if (p == '(') {
try {
xact->amount_expr = parse_value_expr(in)->acquire(); xact->amount_expr = parse_value_expr(in)->acquire();
}
catch (error * err) {
err_desc = "While parsing transaction amount's value expression:";
throw err;
}
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed an amount expression"); "Parsed an amount expression");
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -156,8 +165,7 @@ transaction_t * parse_transaction(char * line, account_t * account,
} }
#endif #endif
if (! compute_amount(xact->amount_expr, xact->amount, xact.get())) if (! compute_amount(xact->amount_expr, xact->amount, xact.get()))
throw parse_error(path, linenum, throw new parse_error("Value expression for amount failed to compute");
"Value expression for amount failed to compute");
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"The computed amount is " << xact->amount); "The computed amount is " << xact->amount);
} else { } else {
@ -209,8 +217,7 @@ transaction_t * parse_transaction(char * line, account_t * account,
p = peek_next_nonws(in); p = peek_next_nonws(in);
if (p == '(') if (p == '(')
throw parse_error(path, linenum, throw new parse_error("A transaction's cost may not be a value expression");
"A transaction's cost may not be a value expression");
xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE); xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
@ -272,18 +279,26 @@ transaction_t * parse_transaction(char * line, account_t * account,
if (char * p = std::strchr(buf, '=')) { if (char * p = std::strchr(buf, '=')) {
*p++ = '\0'; *p++ = '\0';
if (! quick_parse_date(p, &xact->_date_eff)) if (! quick_parse_date(p, &xact->_date_eff))
throw parse_error(path, linenum, throw new parse_error("Failed to parse effective date");
"Failed to parse effective date");
} }
if (buf[0] && ! quick_parse_date(buf, &xact->_date)) if (buf[0] && ! quick_parse_date(buf, &xact->_date))
throw parse_error(path, linenum, "Failed to parse date"); throw new parse_error("Failed to parse date");
} }
} }
} }
finished: finished:
return xact.release(); return xact.release();
}
catch (error * err) {
err->context.push_back
(new line_context(line, (long)in.tellg() - 1,
! err_desc.empty() ?
err_desc : "While parsing transaction:"));
throw err;
}
} }
bool parse_transactions(std::istream& in, bool parse_transactions(std::istream& in,
@ -337,11 +352,11 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master,
if (char * p = std::strchr(line, '=')) { if (char * p = std::strchr(line, '=')) {
*p++ = '\0'; *p++ = '\0';
if (! quick_parse_date(p, &curr->_date_eff)) if (! quick_parse_date(p, &curr->_date_eff))
throw parse_error(path, linenum, "Failed to parse effective date"); throw new parse_error("Failed to parse effective date");
} }
if (! quick_parse_date(line, &curr->_date)) if (! quick_parse_date(line, &curr->_date))
throw parse_error(path, linenum, "Failed to parse date"); throw new parse_error("Failed to parse date");
TIMER_STOP(entry_date); TIMER_STOP(entry_date);
@ -436,8 +451,7 @@ static inline void parse_symbol(char *& p, std::string& symbol)
if (*p == '"') { if (*p == '"') {
char * q = std::strchr(p + 1, '"'); char * q = std::strchr(p + 1, '"');
if (! q) if (! q)
throw parse_error(path, linenum, throw new parse_error("Quoted commodity symbol lacks closing quote");
"Quoted commodity symbol lacks closing quote");
symbol = std::string(p + 1, 0, q - p - 1); symbol = std::string(p + 1, 0, q - p - 1);
p = q + 2; p = q + 2;
} else { } else {
@ -449,7 +463,7 @@ static inline void parse_symbol(char *& p, std::string& symbol)
p += symbol.length(); p += symbol.length();
} }
if (symbol.empty()) if (symbol.empty())
throw parse_error(path, linenum, "Failed to parse commodity"); throw new parse_error("Failed to parse commodity");
} }
bool textual_parser_t::test(std::istream& in) const bool textual_parser_t::test(std::istream& in) const
@ -459,9 +473,9 @@ bool textual_parser_t::test(std::istream& in) const
in.read(buf, 5); in.read(buf, 5);
if (std::strncmp(buf, "<?xml", 5) == 0) { if (std::strncmp(buf, "<?xml", 5) == 0) {
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) #if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
throw parse_error(path, linenum, "Ledger file contains XML data, but format was not recognized"); throw new parse_error("Ledger file contains XML data, but format was not recognized");
#else #else
throw parse_error(path, linenum, "Ledger file contains XML data, but no XML support present"); throw new parse_error("Ledger file contains XML data, but no XML support present");
#endif #endif
} }
@ -491,12 +505,13 @@ static void clock_out_from_timelog(const std::time_t when,
curr->add_transaction(xact); curr->add_transaction(xact);
if (! journal->add_entry(curr.get())) if (! journal->add_entry(curr.get()))
throw parse_error(path, linenum, throw new parse_error("Failed to record 'out' timelog entry");
"Failed to record 'out' timelog entry");
else else
curr.release(); curr.release();
} }
static std::list<std::pair<std::string, int> > include_stack;
unsigned int textual_parser_t::parse(std::istream& in, unsigned int textual_parser_t::parse(std::istream& in,
config_t& config, config_t& config,
journal_t * journal, journal_t * journal,
@ -544,7 +559,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case '\t': { case '\t': {
char * p = skip_ws(line); char * p = skip_ws(line);
if (*p && *p != '\r') if (*p && *p != '\r')
throw parse_error(path, linenum - 1, "Line begins with whitespace"); throw new parse_error("Line begins with whitespace");
break; break;
} }
@ -563,7 +578,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
last_account = account_stack.front()->find_account(p); last_account = account_stack.front()->find_account(p);
} else { } else {
last_account = NULL; last_account = NULL;
throw parse_error(path, linenum, "Cannot parse timelog entry date"); throw new parse_error("Cannot parse timelog entry date");
} }
break; break;
} }
@ -582,7 +597,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
clock_out_from_timelog(std::mktime(&when), journal); clock_out_from_timelog(std::mktime(&when), journal);
count++; count++;
} else { } else {
throw parse_error(path, linenum, "Cannot parse timelog entry date"); throw new parse_error("Cannot parse timelog entry date");
} }
last_account = NULL; last_account = NULL;
@ -625,7 +640,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (strptime(date_buffer, "%Y/%m/%d %H:%M:%S", &when)) { if (strptime(date_buffer, "%Y/%m/%d %H:%M:%S", &when)) {
date = std::mktime(&when); date = std::mktime(&when);
} else { } else {
throw parse_error(path, linenum, "Failed to parse date"); throw new parse_error("Failed to parse date");
} }
std::string symbol; std::string symbol;
@ -691,8 +706,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case '~': { // period entry case '~': { // period entry
period_entry_t * pe = new period_entry_t(skip_ws(line + 1)); period_entry_t * pe = new period_entry_t(skip_ws(line + 1));
if (! pe->period) if (! pe->period)
throw parse_error(path, linenum, throw new parse_error(std::string("Parsing time period '") + line + "'");
std::string("Parsing time period '") + line + "'");
if (parse_transactions(in, account_stack.front(), *pe, if (parse_transactions(in, account_stack.front(), *pe,
"period", end_pos)) { "period", end_pos)) {
@ -705,7 +719,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
pe->end_pos = end_pos; pe->end_pos = end_pos;
pe->end_line = linenum; pe->end_line = linenum;
} else { } else {
throw parse_error(path, linenum, "Period entry failed to balance"); throw new parse_error("Period entry failed to balance");
} }
} }
break; break;
@ -730,11 +744,14 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (pos != std::string::npos) if (pos != std::string::npos)
path = std::string(save_path.prev, 0, pos + 1) + path; path = std::string(save_path.prev, 0, pos + 1) + path;
} }
DEBUG_PRINT("ledger.textual.include", "line " << linenum << ": " << DEBUG_PRINT("ledger.textual.include", "line " << linenum << ": " <<
"Including path '" << path << "'"); "Including path '" << path << "'");
include_stack.push_back(std::pair<std::string, int>
(journal->sources.back(), linenum - 1));
count += parse_journal_file(path, config, journal, count += parse_journal_file(path, config, journal,
account_stack.front()); account_stack.front());
include_stack.pop_back();
} }
else if (word == "account") { else if (word == "account") {
account_t * acct; account_t * acct;
@ -784,34 +801,32 @@ unsigned int textual_parser_t::parse(std::istream& in,
entry->end_line = linenum; entry->end_line = linenum;
count++; count++;
} else { } else {
print_entry(std::cerr, *entry);
delete entry; delete entry;
throw new parse_error("Entry does not balance");
std::ostringstream errmsg;
errmsg << "Entry above does not balance; remainder is: "
<< entry_balance;
throw parse_error(path, first_line, errmsg.str());
} }
} else { } else {
throw parse_error(path, first_line, "Failed to parse entry"); throw new parse_error("Failed to parse entry");
} }
end_pos = pos; end_pos = pos;
break; break;
} }
} }
} }
catch (const parse_error& err) { catch (error * err) {
std::cerr << "Error: " << err.what() << std::endl; for (std::list<std::pair<std::string, int> >::reverse_iterator i =
errors++; include_stack.rbegin();
} i != include_stack.rend();
catch (const amount_error& err) { i++)
std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " err->context.push_back(new include_context((*i).first, (*i).second,
<< err.what() << std::endl;; "In file included from"));
errors++; err->context.push_front(new file_context(path, linenum - 1));
}
catch (const error& err) { std::cout.flush();
std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " if (errors > 0)
<< err.what() << std::endl;; std::cerr << std::endl;
err->reveal_context(std::cerr, "Error");
std::cerr << err->what() << std::endl;
delete err;
errors++; errors++;
} }
beg_pos = end_pos; beg_pos = end_pos;
@ -827,7 +842,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
journal->remove_entry_finalizer(&auto_entry_finalizer); journal->remove_entry_finalizer(&auto_entry_finalizer);
if (errors > 0) if (errors > 0)
throw error(std::string("Errors parsing file '") + path + "'"); throw (int)errors;
TIMER_STOP(parsing_total); TIMER_STOP(parsing_total);
@ -869,7 +884,7 @@ void write_textual_journal(journal_t& journal, std::string path,
#endif #endif
if (found.empty()) if (found.empty())
throw error(std::string("Journal does not refer to file '") + throw new error(std::string("Journal does not refer to file '") +
path + "'"); path + "'");
entries_list::iterator el = journal.entries.begin(); entries_list::iterator el = journal.entries.begin();

View file

@ -7,8 +7,8 @@
namespace ledger { namespace ledger {
std::auto_ptr<value_calc> amount_expr; std::auto_ptr<value_expr> amount_expr;
std::auto_ptr<value_calc> total_expr; std::auto_ptr<value_expr> total_expr;
std::auto_ptr<scope_t> global_scope; std::auto_ptr<scope_t> global_scope;
std::time_t terminus; std::time_t terminus;
@ -23,22 +23,21 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
const transaction_t * xact, value_expr_t * context) const transaction_t * xact, value_expr_t * context)
{ {
value_t result; value_t result;
try {
expr->compute(result, xact ? details_t(*xact) : details_t(), context); expr->compute(result, xact ? details_t(*xact) : details_t(), context);
switch (result.type) { result.cast(value_t::AMOUNT);
case value_t::BOOLEAN:
amt = *((bool *) result.data);
break;
case value_t::INTEGER:
amt = *((long *) result.data);
break;
case value_t::AMOUNT:
amt = *((amount_t *) result.data); amt = *((amount_t *) result.data);
break; }
catch (error * err) {
case value_t::DATETIME: if (err->context.empty() ||
case value_t::BALANCE: ! dynamic_cast<valexpr_context *>(err->context.back()))
case value_t::BALANCE_PAIR: err->context.push_back(new valexpr_context(expr));
return false; error_context * last = err->context.back();
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
ctxt->expr = expr->acquire();
ctxt->desc = "While computing amount expression:";
}
throw err;
} }
return true; return true;
} }
@ -145,6 +144,7 @@ namespace {
void value_expr_t::compute(value_t& result, const details_t& details, void value_expr_t::compute(value_t& result, const details_t& details,
value_expr_t * context) const value_expr_t * context) const
{ {
try {
switch (kind) { switch (kind) {
case CONSTANT_I: case CONSTANT_I:
result = constant_i; result = constant_i;
@ -385,7 +385,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
moment.cast(value_t::INTEGER); moment.cast(value_t::INTEGER);
result -= moment; result -= moment;
} else { } else {
throw compute_error("Invalid date passed to datecmp(value,date)"); throw new compute_error("Invalid date passed to datecmp(value,date)",
new valexpr_context(expr));
} }
break; break;
} }
@ -459,7 +460,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
value_expr_t * expr = find_leaf(context, 0, index); value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context); expr->compute(result, details, context);
if (result.type != value_t::AMOUNT) if (result.type != value_t::AMOUNT)
throw compute_error("Argument to commodity() must be a commoditized amount"); throw new compute_error("Argument to commodity() must be a commoditized amount",
new valexpr_context(expr));
amount_t temp("1"); amount_t temp("1");
temp.set_commodity(((amount_t *) result.data)->commodity()); temp.set_commodity(((amount_t *) result.data)->commodity());
result = temp; result = temp;
@ -476,7 +478,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
expr = find_leaf(context, 1, index); expr = find_leaf(context, 1, index);
expr->compute(result, details, context); expr->compute(result, details, context);
if (result.type != value_t::AMOUNT) if (result.type != value_t::AMOUNT)
throw compute_error("Second argument to set_commodity() must be a commoditized amount"); throw new compute_error("Second argument to set_commodity() must be a commoditized amount",
new valexpr_context(expr));
amount_t one("1"); amount_t one("1");
one.set_commodity(((amount_t *) result.data)->commodity()); one.set_commodity(((amount_t *) result.data)->commodity());
result = one; result = one;
@ -594,7 +597,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break; break;
case O_DEF: case O_DEF:
throw compute_error("Cannot compute function definition"); throw new compute_error("Cannot compute function definition");
case O_REF: { case O_REF: {
assert(left); assert(left);
@ -617,7 +620,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
value_t moment; value_t moment;
expr->compute(moment, details, context); expr->compute(moment, details, context);
if (moment.type != value_t::DATETIME) if (moment.type != value_t::DATETIME)
throw compute_error("Invalid date passed to P(value,date)"); throw new compute_error("Invalid date passed to P(value,date)",
new valexpr_context(expr));
result = result.value(*((datetime_t *)moment.data)); result = result.value(*((datetime_t *)moment.data));
break; break;
@ -719,19 +723,26 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break; break;
} }
} }
catch (error * err) {
if (err->context.empty() ||
! dynamic_cast<valexpr_context *>(err->context.back()))
err->context.push_back(new valexpr_context(this));
throw err;
}
}
static inline void unexpected(char c, char wanted = '\0') { static inline void unexpected(char c, char wanted = '\0') {
if ((unsigned char) c == 0xff) { if ((unsigned char) c == 0xff) {
if (wanted) if (wanted)
throw value_expr_error(std::string("Missing '") + wanted + "'"); throw new value_expr_error(std::string("Missing '") + wanted + "'");
else else
throw value_expr_error("Unexpected end"); throw new value_expr_error("Unexpected end");
} else { } else {
if (wanted) if (wanted)
throw value_expr_error(std::string("Invalid char '") + c + throw new value_expr_error(std::string("Invalid char '") + c +
"' (wanted '" + wanted + "')"); "' (wanted '" + wanted + "')");
else else
throw value_expr_error(std::string("Invalid char '") + c + "'"); throw new value_expr_error(std::string("Invalid char '") + c + "'");
} }
} }
@ -838,7 +849,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
// Define the value associated with the defined identifier // Define the value associated with the defined identifier
value_auto_ptr def(parse_boolean_expr(in, params.get())); value_auto_ptr def(parse_boolean_expr(in, params.get()));
if (! def.get()) if (! def.get())
throw value_expr_error(std::string("Definition failed for '") + buf + "'"); throw new value_expr_error(std::string("Definition failed for '") + buf + "'");
node.reset(new value_expr_t(value_expr_t::O_DEF)); node.reset(new value_expr_t(value_expr_t::O_DEF));
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
@ -858,7 +869,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
(buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' || (buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e')) buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e'))
goto find_term; goto find_term;
throw value_expr_error(std::string("Unknown identifier '") + buf + "'"); throw new value_expr_error(std::string("Unknown identifier '") + buf + "'");
} }
else if (def->kind == value_expr_t::O_DEF) { else if (def->kind == value_expr_t::O_DEF) {
node.reset(new value_expr_t(value_expr_t::O_REF)); node.reset(new value_expr_t(value_expr_t::O_REF));
@ -887,7 +898,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
errmsg << "Wrong number of arguments to '" << buf errmsg << "Wrong number of arguments to '" << buf
<< "': saw " << count << "': saw " << count
<< ", wanted " << def->left->constant_i; << ", wanted " << def->left->constant_i;
throw value_expr_error(errmsg.str()); throw new value_expr_error(errmsg.str());
} }
} }
else { else {
@ -1467,7 +1478,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
if (! node.get()) { if (! node.get()) {
in.get(c); in.get(c);
if (in.eof()) if (in.eof())
throw value_expr_error(std::string("Failed to parse value expression")); throw new value_expr_error(std::string("Failed to parse value expression"));
else else
unexpected(c); unexpected(c);
} else if (! partial) { } else if (! partial) {
@ -1481,6 +1492,264 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
return node.release(); return node.release();
} }
unsigned long write_value_expr(std::ostream& out,
const value_expr_t * node,
const value_expr_t * node_to_find,
unsigned long start_pos)
{
long pos = start_pos;
switch (node->kind) {
case value_expr_t::CONSTANT_I:
out << node->constant_i;
break;
case value_expr_t::CONSTANT_T:
out << "[" << *(node->constant_t) << ']';
break;
case value_expr_t::CONSTANT_A:
out << "{" << *(node->constant_a) << '}';
break;
case value_expr_t::CONSTANT_V:
out << "{" << *(node->constant_v) << '}';
break;
case value_expr_t::AMOUNT: out << "amount"; break;
case value_expr_t::PRICE: out << "price"; break;
case value_expr_t::COST: out << "cost"; break;
case value_expr_t::DATE: out << "date"; break;
case value_expr_t::ACT_DATE: out << "actual_date"; break;
case value_expr_t::EFF_DATE: out << "effective_date"; break;
case value_expr_t::CLEARED: out << "cleared"; break;
case value_expr_t::PENDING: out << "pending"; break;
case value_expr_t::REAL: out << "real"; break;
case value_expr_t::ACTUAL: out << "actual"; break;
case value_expr_t::INDEX: out << "index"; break;
case value_expr_t::COUNT: out << "count"; break;
case value_expr_t::DEPTH: out << "depth"; break;
case value_expr_t::TOTAL: out << "total"; break;
case value_expr_t::PRICE_TOTAL: out << "total_price"; break;
case value_expr_t::COST_TOTAL: out << "total_cost"; break;
case value_expr_t::F_NOW: out << "now"; break;
case value_expr_t::F_ARITH_MEAN:
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_ABS: out << "abs"; break;
out << "abs(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_QUANTITY: out << "quantity"; break;
out << "quantity(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_COMMODITY: out << "commodity"; break;
out << "commodity(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_SET_COMMODITY: out << "set_commodity"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_VALUE: out << "valueof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_PRICE: out << "priceof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_DATE: out << "dateof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_DATECMP: out << "datecmp"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_YEAR: out << "yearof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_MONTH: out << "monthof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_DAY: out << "dayof"; break;
out << "average(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ")";
break;
case value_expr_t::F_CODE_MASK:
out << "c/" << node->mask->pattern << "/";
break;
case value_expr_t::F_PAYEE_MASK:
out << "p/" << node->mask->pattern << "/";
break;
case value_expr_t::F_NOTE_MASK:
out << "e/" << node->mask->pattern << "/";
break;
case value_expr_t::F_ACCOUNT_MASK:
out << "W/" << node->mask->pattern << "/";
break;
case value_expr_t::F_SHORT_ACCOUNT_MASK:
out << "w/" << node->mask->pattern << "/";
break;
case value_expr_t::F_COMMODITY_MASK:
out << "C/" << node->mask->pattern << "/";
break;
case value_expr_t::O_NOT:
out << "!";
pos = write_value_expr(out, node->left, node_to_find, pos);
break;
case value_expr_t::O_NEG:
out << "-";
pos = write_value_expr(out, node->left, node_to_find, pos);
break;
case value_expr_t::O_PERC:
out << "%";
pos = write_value_expr(out, node->left, node_to_find, pos);
break;
case value_expr_t::O_ARG:
out << "arg" << node->constant_i;
break;
case value_expr_t::O_DEF:
out << "O_DEF";
break;
case value_expr_t::O_REF:
break;
case value_expr_t::O_COM:
pos = write_value_expr(out, node->left, node_to_find, pos);
out << ", ";
pos = write_value_expr(out, node->right, node_to_find, pos);
break;
case value_expr_t::O_QUES:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " ? ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_COL:
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " : ";
pos = write_value_expr(out, node->right, node_to_find, pos);
break;
case value_expr_t::O_AND: out << "O_AND"; break;
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " & ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_OR: out << "O_OR"; break;
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " | ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_NEQ:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " != ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_EQ:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " == ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_LT:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " < ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_LTE:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " <= ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_GT:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " > ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_GTE:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " >= ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_ADD:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " + ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_SUB:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " - ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_MUL:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " * ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::O_DIV:
out << "(";
pos = write_value_expr(out, node->left, node_to_find, pos);
out << " / ";
pos = write_value_expr(out, node->right, node_to_find, pos);
out << ")";
break;
case value_expr_t::LAST:
default:
assert(0);
break;
}
if (node == node_to_find)
pos = (long)out.tellp() - 1;
return pos;
}
void dump_value_expr(std::ostream& out, const value_expr_t * node, void dump_value_expr(std::ostream& out, const value_expr_t * node,
const int depth) const int depth)
{ {

160
valexpr.h
View file

@ -33,39 +33,6 @@ struct details_t
#endif #endif
}; };
class value_calc
{
public:
virtual ~value_calc() {}
virtual void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) = 0;
virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) = 0;
};
typedef void (*value_func_t)(value_t& result, const details_t& details,
value_expr_t * context);
class value_func : public value_calc
{
value_func_t func;
public:
value_func(value_func_t _func) : func(_func) {}
virtual void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) {
func(result, details, context);
}
virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) {
value_t temp;
func(temp, details, context);
return temp;
}
};
struct value_expr_t struct value_expr_t
{ {
enum kind_t { enum kind_t {
@ -252,7 +219,7 @@ struct scope_t
= symbols.insert(symbol_pair(name, def)); = symbols.insert(symbol_pair(name, def));
if (! result.second) { if (! result.second) {
def->release(); def->release();
throw value_expr_error(std::string("Redefinition of '") + throw new compute_error(std::string("Redefinition of '") +
name + "' in same scope"); name + "' in same scope");
} }
} }
@ -282,37 +249,87 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
struct scope_t; struct scope_t;
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope); value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope);
inline value_expr_t * parse_boolean_expr(const char * p,
scope_t * scope = NULL) {
std::istringstream stream(p);
return parse_boolean_expr(stream, scope);
}
inline value_expr_t * parse_boolean_expr(const std::string& str, inline value_expr_t * parse_boolean_expr(const std::string& str,
scope_t * scope = NULL) { scope_t * scope = NULL) {
return parse_boolean_expr(str.c_str(), scope); std::istringstream stream(str);
try {
return parse_boolean_expr(stream, scope);
}
catch (error * err) {
err->context.push_back
(new error_context("While parsing value expression: " + str));
throw err;
}
}
inline value_expr_t * parse_boolean_expr(const char * p,
scope_t * scope = NULL) {
return parse_boolean_expr(std::string(p), scope);
} }
value_expr_t * parse_value_expr(std::istream& in, value_expr_t * parse_value_expr(std::istream& in,
scope_t * scope = NULL, scope_t * scope = NULL,
const bool partial = false); const bool partial = false);
inline value_expr_t * parse_value_expr(const char * p,
scope_t * scope = NULL,
const bool partial = false) {
std::istringstream stream(p);
return parse_value_expr(stream, scope, partial);
}
inline value_expr_t * parse_value_expr(const std::string& str, inline value_expr_t * parse_value_expr(const std::string& str,
scope_t * scope = NULL, scope_t * scope = NULL,
const bool partial = false) { const bool partial = false) {
return parse_value_expr(str.c_str(), scope); std::istringstream stream(str);
try {
return parse_value_expr(stream, scope, partial);
}
catch (error * err) {
err->context.push_back
(new line_context(str, (long)stream.tellg() - 1,
"While parsing value expression:"));
throw err;
}
}
inline value_expr_t * parse_value_expr(const char * p,
scope_t * scope = NULL,
const bool partial = false) {
return parse_value_expr(std::string(p), scope, partial);
} }
void dump_value_expr(std::ostream& out, const value_expr_t * node, void dump_value_expr(std::ostream& out, const value_expr_t * node,
const int depth = 0); const int depth = 0);
unsigned long write_value_expr(std::ostream& out,
const value_expr_t * node,
const value_expr_t * node_to_find = NULL,
unsigned long start_pos = 0UL);
//////////////////////////////////////////////////////////////////////
inline void guarded_compute(const value_expr_t * expr,
value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) {
try {
expr->compute(result, details);
}
catch (error * err) {
if (err->context.empty() ||
! dynamic_cast<valexpr_context *>(err->context.back()))
err->context.push_back(new valexpr_context(expr));
error_context * last = err->context.back();
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
ctxt->expr = expr->acquire();
ctxt->desc = "While computing value expression:";
}
throw err;
}
}
inline value_t guarded_compute(const value_expr_t * expr,
const details_t& details = details_t(),
value_expr_t * context = NULL) {
value_t temp;
guarded_compute(expr, temp, details, context);
return temp;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// This class is used so that during the "in between" stages of value // This class is used so that during the "in between" stages of value
@ -323,10 +340,11 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
struct value_auto_ptr { struct value_auto_ptr {
value_expr_t * ptr; value_expr_t * ptr;
value_auto_ptr() : ptr(NULL) {} value_auto_ptr() : ptr(NULL) {}
explicit value_auto_ptr(value_expr_t * _ptr) : ptr(_ptr) {} explicit value_auto_ptr(value_expr_t * _ptr)
: ptr(_ptr ? _ptr->acquire() : NULL) {}
~value_auto_ptr() { ~value_auto_ptr() {
if (ptr && ptr->refc == 0) if (ptr)
delete ptr; ptr->release();
} }
value_expr_t& operator*() const throw() { value_expr_t& operator*() const throw() {
return *ptr; return *ptr;
@ -342,31 +360,24 @@ struct value_auto_ptr {
} }
void reset(value_expr_t * p = 0) throw() { void reset(value_expr_t * p = 0) throw() {
if (p != ptr) { if (p != ptr) {
if (ptr && ptr->refc == 0) if (ptr)
delete ptr; ptr->release();
ptr = p; ptr = p->acquire();
} }
} }
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
class value_expr : public value_calc class value_expr
{ {
std::string expr;
value_expr_t * parsed; value_expr_t * parsed;
public: public:
std::string expr;
value_expr(const std::string& _expr) : expr(_expr) { value_expr(const std::string& _expr) : expr(_expr) {
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
try { parsed = parse_value_expr(expr)->acquire();
parsed = parse_value_expr(expr);
parsed->acquire();
}
catch (const value_expr_error& err) {
throw error(std::string("In value expression '") +
expr + "': " + err.what());
}
} }
value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) { value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) {
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
@ -380,18 +391,18 @@ public:
virtual void compute(value_t& result, virtual void compute(value_t& result,
const details_t& details = details_t(), const details_t& details = details_t(),
value_expr_t * context = NULL) { value_expr_t * context = NULL) {
parsed->compute(result, details, context); guarded_compute(parsed, result, details, context);
} }
virtual value_t compute(const details_t& details = details_t(), virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) { value_expr_t * context = NULL) {
value_t temp; value_t temp;
parsed->compute(temp, details, context); guarded_compute(parsed, temp, details, context);
return temp; return temp;
} }
}; };
extern std::auto_ptr<value_calc> amount_expr; extern std::auto_ptr<value_expr> amount_expr;
extern std::auto_ptr<value_calc> total_expr; extern std::auto_ptr<value_expr> total_expr;
inline void compute_amount(value_t& result, inline void compute_amount(value_t& result,
const details_t& details = details_t()) { const details_t& details = details_t()) {
@ -425,16 +436,9 @@ class item_predicate
item_predicate(const std::string& _predicate) : predicate(NULL) { item_predicate(const std::string& _predicate) : predicate(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>"); DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
if (! _predicate.empty()) { if (! _predicate.empty())
try {
predicate = parse_value_expr(_predicate)->acquire(); predicate = parse_value_expr(_predicate)->acquire();
} }
catch (value_expr_error& err) {
throw value_expr_error(std::string("In predicate '") +
_predicate + "': " + err.what());
}
}
}
item_predicate(const value_expr_t * _predicate = NULL) item_predicate(const value_expr_t * _predicate = NULL)
: predicate(_predicate->acquire()) { : predicate(_predicate->acquire()) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>"); DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");

179
value.cc
View file

@ -1,5 +1,6 @@
#include "value.h" #include "value.h"
#include "debug.h" #include "debug.h"
#include "error.h"
namespace ledger { namespace ledger {
@ -93,13 +94,13 @@ value_t& value_t::operator=(const value_t& value)
value_t& value_t::operator+=(const value_t& value) value_t& value_t::operator+=(const value_t& value)
{ {
if (value.type == BOOLEAN) if (value.type == BOOLEAN)
throw value_error("Cannot add a boolean to a value"); throw new value_error("Cannot add a boolean to a value");
else if (value.type == DATETIME) else if (value.type == DATETIME)
throw value_error("Cannot add a date/time to a value"); throw new value_error("Cannot add a date/time to a value");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot add a value to a boolean"); throw new value_error("Cannot add a value to a boolean");
case INTEGER: case INTEGER:
switch (value.type) { switch (value.type) {
@ -231,13 +232,13 @@ value_t& value_t::operator+=(const value_t& value)
value_t& value_t::operator-=(const value_t& value) value_t& value_t::operator-=(const value_t& value)
{ {
if (value.type == BOOLEAN) if (value.type == BOOLEAN)
throw value_error("Cannot subtract a boolean from a value"); throw new value_error("Cannot subtract a boolean from a value");
else if (value.type == DATETIME) else if (value.type == DATETIME && type != DATETIME)
throw value_error("Cannot subtract a date/time from a value"); throw new value_error("Cannot subtract a date/time from a value");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot subtract a value from a boolean"); throw new value_error("Cannot subtract a value from a boolean");
case INTEGER: case INTEGER:
switch (value.type) { switch (value.type) {
@ -262,6 +263,32 @@ value_t& value_t::operator-=(const value_t& value)
} }
break; break;
case DATETIME:
switch (value.type) {
case INTEGER:
*((datetime_t *) data) -= *((long *) value.data);
break;
case DATETIME: {
long val = *((datetime_t *) data) - *((datetime_t *) value.data);
cast(INTEGER);
*((long *) data) = val;
break;
}
case AMOUNT:
*((datetime_t *) data) -= long(*((amount_t *) value.data));
break;
case BALANCE:
*((datetime_t *) data) -= long(*((balance_t *) value.data));
break;
case BALANCE_PAIR:
*((datetime_t *) data) -= long(*((balance_pair_t *) value.data));
break;
default:
assert(0);
break;
}
break;
case AMOUNT: case AMOUNT:
switch (value.type) { switch (value.type) {
case INTEGER: case INTEGER:
@ -352,9 +379,9 @@ value_t& value_t::operator-=(const value_t& value)
value_t& value_t::operator*=(const value_t& value) value_t& value_t::operator*=(const value_t& value)
{ {
if (value.type == BOOLEAN) if (value.type == BOOLEAN)
throw value_error("Cannot multiply a boolean by a value"); throw new value_error("Cannot multiply a boolean by a value");
else if (value.type == DATETIME) else if (value.type == DATETIME)
throw value_error("Cannot multiply a date/time by a value"); throw new value_error("Cannot multiply a date/time by a value");
if (value.realzero()) { if (value.realzero()) {
*this = 0L; *this = 0L;
@ -363,7 +390,7 @@ value_t& value_t::operator*=(const value_t& value)
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot multiply a value by a boolean"); throw new value_error("Cannot multiply a value by a boolean");
case INTEGER: case INTEGER:
switch (value.type) { switch (value.type) {
@ -461,13 +488,13 @@ value_t& value_t::operator*=(const value_t& value)
value_t& value_t::operator/=(const value_t& value) value_t& value_t::operator/=(const value_t& value)
{ {
if (value.type == BOOLEAN) if (value.type == BOOLEAN)
throw value_error("Cannot divide a boolean by a value"); throw new value_error("Cannot divide a boolean by a value");
else if (value.type == DATETIME) else if (value.type == DATETIME)
throw value_error("Cannot divide a date/time by a value"); throw new value_error("Cannot divide a date/time by a value");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot divide a value by a boolean"); throw new value_error("Cannot divide a value by a boolean");
case INTEGER: case INTEGER:
switch (value.type) { switch (value.type) {
@ -626,7 +653,7 @@ bool value_t::operator OP(const value_t& value) \
case DATETIME: \ case DATETIME: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
throw value_error("Cannot compare a date/time to a boolean"); \ throw new value_error("Cannot compare a date/time to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return (*((datetime_t *) data) OP \ return (*((datetime_t *) data) OP \
@ -637,13 +664,13 @@ bool value_t::operator OP(const value_t& value) \
*((datetime_t *) value.data)); \ *((datetime_t *) value.data)); \
\ \
case AMOUNT: \ case AMOUNT: \
throw value_error("Cannot compare a date/time to an amount"); \ throw new value_error("Cannot compare a date/time to an amount"); \
\ \
case BALANCE: \ case BALANCE: \
throw value_error("Cannot compare a date/time to a balance"); \ throw new value_error("Cannot compare a date/time to a balance"); \
\ \
case BALANCE_PAIR: \ case BALANCE_PAIR: \
throw value_error("Cannot compare a date/time to a balance pair"); \ throw new value_error("Cannot compare a date/time to a balance pair"); \
\ \
default: \ default: \
assert(0); \ assert(0); \
@ -654,14 +681,14 @@ bool value_t::operator OP(const value_t& value) \
case AMOUNT: \ case AMOUNT: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
throw value_error("Cannot compare an amount to a boolean"); \ throw new value_error("Cannot compare an amount to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return (*((amount_t *) data) OP \ return (*((amount_t *) data) OP \
amount_t(*((long *) value.data))); \ amount_t(*((long *) value.data))); \
\ \
case DATETIME: \ case DATETIME: \
throw value_error("Cannot compare an amount to a date/time"); \ throw new value_error("Cannot compare an amount to a date/time"); \
\ \
case AMOUNT: \ case AMOUNT: \
return *((amount_t *) data) OP *((amount_t *) value.data); \ return *((amount_t *) data) OP *((amount_t *) value.data); \
@ -685,13 +712,13 @@ bool value_t::operator OP(const value_t& value) \
case BALANCE: \ case BALANCE: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
throw value_error("Cannot compare a balance to a boolean"); \ throw new value_error("Cannot compare a balance to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return *((balance_t *) data) OP *((long *) value.data); \ return *((balance_t *) data) OP *((long *) value.data); \
\ \
case DATETIME: \ case DATETIME: \
throw value_error("Cannot compare a balance to a date/time"); \ throw new value_error("Cannot compare a balance to a date/time"); \
\ \
case AMOUNT: \ case AMOUNT: \
return *((balance_t *) data) OP *((amount_t *) value.data); \ return *((balance_t *) data) OP *((amount_t *) value.data); \
@ -712,14 +739,14 @@ bool value_t::operator OP(const value_t& value) \
case BALANCE_PAIR: \ case BALANCE_PAIR: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
throw value_error("Cannot compare a balance pair to a boolean"); \ throw new value_error("Cannot compare a balance pair to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return (((balance_pair_t *) data)->quantity OP \ return (((balance_pair_t *) data)->quantity OP \
*((long *) value.data)); \ *((long *) value.data)); \
\ \
case DATETIME: \ case DATETIME: \
throw value_error("Cannot compare a balance pair to a date/time"); \ throw new value_error("Cannot compare a balance pair to a date/time"); \
\ \
case AMOUNT: \ case AMOUNT: \
return (((balance_pair_t *) data)->quantity OP \ return (((balance_pair_t *) data)->quantity OP \
@ -757,7 +784,7 @@ value_t::operator long() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot convert a boolean to an integer"); throw new value_error("Cannot convert a boolean to an integer");
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME: case DATETIME:
@ -765,9 +792,9 @@ value_t::operator long() const
case AMOUNT: case AMOUNT:
return *((amount_t *) data); return *((amount_t *) data);
case BALANCE: case BALANCE:
throw value_error("Cannot convert a balance to an integer"); throw new value_error("Cannot convert a balance to an integer");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a balance pair to an integer"); throw new value_error("Cannot convert a balance pair to an integer");
default: default:
assert(0); assert(0);
@ -782,17 +809,17 @@ value_t::operator datetime_t() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot convert a boolean to a date/time"); throw new value_error("Cannot convert a boolean to a date/time");
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME: case DATETIME:
return *((datetime_t *) data); return *((datetime_t *) data);
case AMOUNT: case AMOUNT:
throw value_error("Cannot convert an amount to a date/time"); throw new value_error("Cannot convert an amount to a date/time");
case BALANCE: case BALANCE:
throw value_error("Cannot convert a balance to a date/time"); throw new value_error("Cannot convert a balance to a date/time");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a balance pair to a date/time"); throw new value_error("Cannot convert a balance pair to a date/time");
default: default:
assert(0); assert(0);
@ -807,7 +834,7 @@ value_t::operator double() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot convert a boolean to a double"); throw new value_error("Cannot convert a boolean to a double");
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME: case DATETIME:
@ -815,9 +842,9 @@ value_t::operator double() const
case AMOUNT: case AMOUNT:
return *((amount_t *) data); return *((amount_t *) data);
case BALANCE: case BALANCE:
throw value_error("Cannot convert a balance to a double"); throw new value_error("Cannot convert a balance to a double");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a balance pair to a double"); throw new value_error("Cannot convert a balance pair to a double");
default: default:
assert(0); assert(0);
@ -835,15 +862,15 @@ void value_t::cast(type_t cast_type)
case BOOLEAN: case BOOLEAN:
break; break;
case INTEGER: case INTEGER:
throw value_error("Cannot convert a boolean to an integer"); throw new value_error("Cannot convert a boolean to an integer");
case DATETIME: case DATETIME:
throw value_error("Cannot convert a boolean to a date/time"); throw new value_error("Cannot convert a boolean to a date/time");
case AMOUNT: case AMOUNT:
throw value_error("Cannot convert a boolean to an amount"); throw new value_error("Cannot convert a boolean to an amount");
case BALANCE: case BALANCE:
throw value_error("Cannot convert a boolean to a balance"); throw new value_error("Cannot convert a boolean to a balance");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a boolean to a balance pair"); throw new value_error("Cannot convert a boolean to a balance pair");
default: default:
assert(0); assert(0);
@ -888,11 +915,11 @@ void value_t::cast(type_t cast_type)
case DATETIME: case DATETIME:
break; break;
case AMOUNT: case AMOUNT:
throw value_error("Cannot convert a date/time to an amount"); throw new value_error("Cannot convert a date/time to an amount");
case BALANCE: case BALANCE:
throw value_error("Cannot convert a date/time to a balance"); throw new value_error("Cannot convert a date/time to a balance");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a date/time to a balance pair"); throw new value_error("Cannot convert a date/time to a balance pair");
default: default:
assert(0); assert(0);
@ -915,7 +942,7 @@ void value_t::cast(type_t cast_type)
break; break;
} }
case DATETIME: case DATETIME:
throw value_error("Cannot convert an amount to a date/time"); throw new value_error("Cannot convert an amount to a date/time");
case AMOUNT: case AMOUNT:
break; break;
case BALANCE: { case BALANCE: {
@ -946,9 +973,9 @@ void value_t::cast(type_t cast_type)
break; break;
} }
case INTEGER: case INTEGER:
throw value_error("Cannot convert a balance to an integer"); throw new value_error("Cannot convert a balance to an integer");
case DATETIME: case DATETIME:
throw value_error("Cannot convert a balance to a date/time"); throw new value_error("Cannot convert a balance to a date/time");
case AMOUNT: { case AMOUNT: {
balance_t * temp = (balance_t *) data; balance_t * temp = (balance_t *) data;
@ -961,7 +988,7 @@ void value_t::cast(type_t cast_type)
new((amount_t *)data) amount_t(); new((amount_t *)data) amount_t();
} }
else { else {
throw value_error("Cannot convert a balance with " throw new value_error("Cannot convert a balance with "
"multiple commodities to an amount"); "multiple commodities to an amount");
} }
break; break;
@ -990,9 +1017,9 @@ void value_t::cast(type_t cast_type)
break; break;
} }
case INTEGER: case INTEGER:
throw value_error("Cannot convert a balance pair to an integer"); throw new value_error("Cannot convert a balance pair to an integer");
case DATETIME: case DATETIME:
throw value_error("Cannot convert a balance pair to a date/time"); throw new value_error("Cannot convert a balance pair to a date/time");
case AMOUNT: { case AMOUNT: {
balance_t * temp = &((balance_pair_t *) data)->quantity; balance_t * temp = &((balance_pair_t *) data)->quantity;
@ -1005,7 +1032,7 @@ void value_t::cast(type_t cast_type)
new((amount_t *)data) amount_t(); new((amount_t *)data) amount_t();
} }
else { else {
throw value_error("Cannot convert a balance pair with " throw new value_error("Cannot convert a balance pair with "
"multiple commodities to an amount"); "multiple commodities to an amount");
} }
break; break;
@ -1042,7 +1069,7 @@ void value_t::negate()
*((long *) data) = - *((long *) data); *((long *) data) = - *((long *) data);
break; break;
case DATETIME: case DATETIME:
throw value_error("Cannot negate a date/time"); throw new value_error("Cannot negate a date/time");
case AMOUNT: case AMOUNT:
((amount_t *) data)->negate(); ((amount_t *) data)->negate();
break; break;
@ -1090,9 +1117,9 @@ value_t value_t::value(const std::time_t moment) const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the value of a boolean"); throw new value_error("Cannot find the value of a boolean");
case DATETIME: case DATETIME:
throw value_error("Cannot find the value of a date/time"); throw new value_error("Cannot find the value of a date/time");
case INTEGER: case INTEGER:
return *this; return *this;
case AMOUNT: case AMOUNT:
@ -1108,9 +1135,9 @@ void value_t::round()
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot round a boolean"); throw new value_error("Cannot round a boolean");
case DATETIME: case DATETIME:
throw value_error("Cannot round a date/time"); throw new value_error("Cannot round a date/time");
case INTEGER: case INTEGER:
break; break;
case AMOUNT: case AMOUNT:
@ -1125,15 +1152,38 @@ void value_t::round()
} }
} }
value_t value_t::unround() const
{
value_t temp;
switch (type) {
case BOOLEAN:
throw new value_error("Cannot un-round a boolean");
case DATETIME:
throw new value_error("Cannot un-round a date/time");
case INTEGER:
break;
case AMOUNT:
temp = ((amount_t *) data)->unround();
break;
case BALANCE:
temp = ((balance_t *) data)->unround();
break;
case BALANCE_PAIR:
temp = ((balance_pair_t *) data)->unround();
break;
}
return temp;
}
value_t value_t::price() const value_t value_t::price() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the price of a boolean"); throw new value_error("Cannot find the price of a boolean");
case INTEGER: case INTEGER:
return *this; return *this;
case DATETIME: case DATETIME:
throw value_error("Cannot find the price of a date/time"); throw new value_error("Cannot find the price of a date/time");
case AMOUNT: case AMOUNT:
return ((amount_t *) data)->price(); return ((amount_t *) data)->price();
@ -1156,7 +1206,7 @@ value_t value_t::date() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the date of a boolean"); throw new value_error("Cannot find the date of a boolean");
case INTEGER: case INTEGER:
return 0L; return 0L;
case DATETIME: case DATETIME:
@ -1185,11 +1235,11 @@ value_t value_t::strip_annotations(const bool keep_price,
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot strip commodity annotations from a boolean"); throw new value_error("Cannot strip commodity annotations from a boolean");
case INTEGER: case INTEGER:
return *this; return *this;
case DATETIME: case DATETIME:
throw value_error("Cannot strip commodity annotations from a date/time"); throw new value_error("Cannot strip commodity annotations from a date/time");
case AMOUNT: case AMOUNT:
return ((amount_t *) data)->strip_annotations return ((amount_t *) data)->strip_annotations
@ -1213,13 +1263,13 @@ value_t value_t::cost() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the cost of a boolean"); throw new value_error("Cannot find the cost of a boolean");
case INTEGER: case INTEGER:
case AMOUNT: case AMOUNT:
case BALANCE: case BALANCE:
return *this; return *this;
case DATETIME: case DATETIME:
throw value_error("Cannot find the cost of a date/time"); throw new value_error("Cannot find the cost of a date/time");
case BALANCE_PAIR: case BALANCE_PAIR:
assert(((balance_pair_t *) data)->cost); assert(((balance_pair_t *) data)->cost);
@ -1240,9 +1290,9 @@ value_t& value_t::add(const amount_t& amount, const amount_t * cost)
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot add an amount to a boolean"); throw new value_error("Cannot add an amount to a boolean");
case DATETIME: case DATETIME:
throw value_error("Cannot add an amount to a date/time"); throw new value_error("Cannot add an amount to a date/time");
case INTEGER: case INTEGER:
case AMOUNT: case AMOUNT:
if (cost) { if (cost) {
@ -1329,13 +1379,13 @@ amount_t value_getitem(value_t& value, int i)
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
throw value_error("Cannot cast a boolean to an amount"); throw new value_error("Cannot cast a boolean to an amount");
case value_t::INTEGER: case value_t::INTEGER:
return long(value); return long(value);
case value_t::DATETIME: case value_t::DATETIME:
throw value_error("Cannot cast a date/time to an amount"); throw new value_error("Cannot cast a date/time to an amount");
case value_t::AMOUNT: case value_t::AMOUNT:
return *((amount_t *) value.data); return *((amount_t *) value.data);
@ -1555,7 +1605,6 @@ void export_value()
.def("__len__", value_len) .def("__len__", value_len)
.def("__getitem__", value_getitem) .def("__getitem__", value_getitem)
.def("abs", &value_t::abs)
.def("cast", &value_t::cast) .def("cast", &value_t::cast)
.def("cost", &value_t::cost) .def("cost", &value_t::cost)
.def("price", &value_t::price) .def("price", &value_t::price)

12
value.h
View file

@ -8,17 +8,6 @@
namespace ledger { namespace ledger {
class value_error : public std::exception {
std::string reason;
public:
value_error(const std::string& _reason) throw() : reason(_reason) {}
virtual ~value_error() throw() {}
virtual const char* what() const throw() {
return reason.c_str();
}
};
// The following type is a polymorphous value type used solely for // The following type is a polymorphous value type used solely for
// performance reasons. The alternative is to compute value // performance reasons. The alternative is to compute value
// expressions (valexpr.cc) in terms of the largest data type, // expressions (valexpr.cc) in terms of the largest data type,
@ -327,6 +316,7 @@ class value_t
value_t& add(const amount_t& amount, const amount_t * cost = NULL); value_t& add(const amount_t& amount, const amount_t * cost = NULL);
value_t value(const std::time_t moment) const; value_t value(const std::time_t moment) const;
void round(); void round();
value_t unround() const;
}; };
#define DEF_VALUE_AUX_OP(OP) \ #define DEF_VALUE_AUX_OP(OP) \

23
walk.cc
View file

@ -16,13 +16,13 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
transaction_xdata_t& lxdata(transaction_xdata(*left)); transaction_xdata_t& lxdata(transaction_xdata(*left));
if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) { if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) {
sort_order->compute(lxdata.sort_value, details_t(*left)); guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
lxdata.dflags |= TRANSACTION_SORT_CALC; lxdata.dflags |= TRANSACTION_SORT_CALC;
} }
transaction_xdata_t& rxdata(transaction_xdata(*right)); transaction_xdata_t& rxdata(transaction_xdata(*right));
if (! (rxdata.dflags & TRANSACTION_SORT_CALC)) { if (! (rxdata.dflags & TRANSACTION_SORT_CALC)) {
sort_order->compute(rxdata.sort_value, details_t(*right)); guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
rxdata.dflags |= TRANSACTION_SORT_CALC; rxdata.dflags |= TRANSACTION_SORT_CALC;
} }
@ -133,6 +133,8 @@ void sort_transactions::post_accumulated_xacts()
void calc_transactions::operator()(transaction_t& xact) void calc_transactions::operator()(transaction_t& xact)
{ {
try {
transaction_xdata_t& xdata(transaction_xdata(xact)); transaction_xdata_t& xdata(transaction_xdata(xact));
if (last_xact && transaction_has_xdata(*last_xact)) { if (last_xact && transaction_has_xdata(*last_xact)) {
@ -148,6 +150,13 @@ void calc_transactions::operator()(transaction_t& xact)
item_handler<transaction_t>::operator()(xact); item_handler<transaction_t>::operator()(xact);
last_xact = &xact; last_xact = &xact;
}
catch (error * err) {
err->context.push_front
(new xact_context(xact, "Calculating transaction at"));
throw err;
}
} }
void invert_transactions::operator()(transaction_t& xact) void invert_transactions::operator()(transaction_t& xact)
@ -710,13 +719,13 @@ bool compare_items<account_t>::operator()(const account_t * left,
account_xdata_t& lxdata(account_xdata(*left)); account_xdata_t& lxdata(account_xdata(*left));
if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) { if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
sort_order->compute(lxdata.sort_value, details_t(*left)); guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
lxdata.dflags |= ACCOUNT_SORT_CALC; lxdata.dflags |= ACCOUNT_SORT_CALC;
} }
account_xdata_t& rxdata(account_xdata(*right)); account_xdata_t& rxdata(account_xdata(*right));
if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) { if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) {
sort_order->compute(rxdata.sort_value, details_t(*right)); guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
rxdata.dflags |= ACCOUNT_SORT_CALC; rxdata.dflags |= ACCOUNT_SORT_CALC;
} }
@ -794,13 +803,7 @@ void walk_accounts(account_t& account,
{ {
if (! sort_string.empty()) { if (! sort_string.empty()) {
value_auto_ptr sort_order; value_auto_ptr sort_order;
try {
sort_order.reset(parse_value_expr(sort_string)); sort_order.reset(parse_value_expr(sort_string));
}
catch (value_expr_error& err) {
throw error(std::string("In sort string '" + sort_string + "': " +
err.what()));
}
walk_accounts(account, handler, sort_order.get()); walk_accounts(account, handler, sort_order.get());
} else { } else {
walk_accounts(account, handler); walk_accounts(account, handler);

16
walk.h
View file

@ -23,10 +23,10 @@ struct item_handler {
item_handler(item_handler * _handler) : handler(_handler) { item_handler(item_handler * _handler) : handler(_handler) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler<T>"); DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler<T>");
} }
virtual ~item_handler() { virtual ~item_handler() {
DEBUG_PRINT("ledger.memory.dtors", "dtor item_handler<T>"); DEBUG_PRINT("ledger.memory.dtors", "dtor item_handler<T>");
} }
virtual void flush() { virtual void flush() {
if (handler) if (handler)
handler->flush(); handler->flush();
@ -56,8 +56,8 @@ bool compare_items<T>::operator()(const T * left, const T * right)
value_t left_result; value_t left_result;
value_t right_result; value_t right_result;
sort_order->compute(left_result, details_t(*left)); guarded_compute(sort_order, left_result, details_t(*left));
sort_order->compute(right_result, details_t(*right)); guarded_compute(sort_order, right_result, details_t(*right));
return left_result < right_result; return left_result < right_result;
} }
@ -222,14 +222,8 @@ class sort_transactions : public item_handler<transaction_t>
sort_transactions(item_handler<transaction_t> * handler, sort_transactions(item_handler<transaction_t> * handler,
const std::string& _sort_order) const std::string& _sort_order)
: item_handler<transaction_t>(handler) { : item_handler<transaction_t>(handler) {
try { assert(! _sort_order.empty());
sort_order = parse_value_expr(_sort_order); sort_order = parse_value_expr(_sort_order)->acquire();
sort_order->acquire();
}
catch (value_expr_error& err) {
throw value_expr_error(std::string("In sort string '") + _sort_order +
"': " + err.what());
}
} }
virtual ~sort_transactions() { virtual ~sort_transactions() {

8
xml.cc
View file

@ -223,14 +223,12 @@ unsigned int xml_parser_t::parse(std::istream& in,
catch (const std::exception& err) { catch (const std::exception& err) {
unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; unsigned long line = XML_GetCurrentLineNumber(parser) - offset++;
XML_ParserFree(parser); XML_ParserFree(parser);
throw parse_error(original_file ? *original_file : "<xml>", line, throw new parse_error(err.what());
err.what());
} }
if (! have_error.empty()) { if (! have_error.empty()) {
unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; unsigned long line = XML_GetCurrentLineNumber(parser) - offset++;
parse_error err(original_file ? *original_file : "<xml>", line, parse_error err(have_error);
have_error);
std::cerr << "Error: " << err.what() << std::endl; std::cerr << "Error: " << err.what() << std::endl;
have_error = ""; have_error = "";
} }
@ -239,7 +237,7 @@ unsigned int xml_parser_t::parse(std::istream& in,
unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; unsigned long line = XML_GetCurrentLineNumber(parser) - offset++;
const char * err = XML_ErrorString(XML_GetErrorCode(parser)); const char * err = XML_ErrorString(XML_GetErrorCode(parser));
XML_ParserFree(parser); XML_ParserFree(parser);
throw parse_error(original_file ? *original_file : "<xml>", line, err); throw new parse_error(err);
} }
} }