diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | amount.cc | 49 | ||||
-rw-r--r-- | amount.h | 10 | ||||
-rw-r--r-- | balance.cc | 18 | ||||
-rw-r--r-- | balance.h | 17 | ||||
-rw-r--r-- | binary.cc | 32 | ||||
-rw-r--r-- | config.cc | 63 | ||||
-rw-r--r-- | datetime.cc | 2 | ||||
-rw-r--r-- | datetime.h | 15 | ||||
-rw-r--r-- | debug.cc | 15 | ||||
-rw-r--r-- | debug.h | 25 | ||||
-rw-r--r-- | derive.cc | 6 | ||||
-rw-r--r-- | error.h | 208 | ||||
-rw-r--r-- | format.cc | 35 | ||||
-rw-r--r-- | format.h | 3 | ||||
-rw-r--r-- | gnucash.cc | 4 | ||||
-rw-r--r-- | journal.cc | 18 | ||||
-rw-r--r-- | journal.h | 2 | ||||
-rw-r--r-- | ledger.texi | 2 | ||||
-rw-r--r-- | main.cc | 63 | ||||
-rw-r--r-- | mask.cc | 3 | ||||
-rw-r--r-- | mask.h | 11 | ||||
-rw-r--r-- | option.cc | 43 | ||||
-rw-r--r-- | option.h | 11 | ||||
-rw-r--r-- | parser.cc | 8 | ||||
-rw-r--r-- | qif.cc | 9 | ||||
-rw-r--r-- | quotes.cc | 6 | ||||
-rw-r--r-- | reconcile.cc | 8 | ||||
-rwxr-xr-x | test.py | 24 | ||||
-rw-r--r-- | textual.cc | 111 | ||||
-rw-r--r-- | valexpr.cc | 329 | ||||
-rw-r--r-- | valexpr.h | 154 | ||||
-rw-r--r-- | value.cc | 179 | ||||
-rw-r--r-- | value.h | 12 | ||||
-rw-r--r-- | walk.cc | 25 | ||||
-rw-r--r-- | walk.h | 16 | ||||
-rw-r--r-- | xml.cc | 8 |
37 files changed, 1103 insertions, 442 deletions
diff --git a/Makefile.am b/Makefile.am index ba96fd9b..832806a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,6 +20,7 @@ libledger_la_SOURCES = \ config.cc \ derive.cc \ emacs.cc \ + error.cc \ format.cc \ journal.cc \ mask.cc \ @@ -378,7 +378,7 @@ amount_t& amount_t::operator+=(const amount_t& amt) _dup(); 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) { mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); @@ -411,7 +411,7 @@ amount_t& amount_t::operator-=(const amount_t& amt) _dup(); 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) { 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) { if (! amt.quantity || ! amt) - throw amount_error("Divide by zero"); + throw new amount_error("Divide by zero"); else if (! quantity) return *this; @@ -919,7 +919,7 @@ void parse_commodity(std::istream& in, std::string& symbol) if (c == '"') in.get(c); else - throw amount_error("Quoted commodity symbol lacks closing quote"); + throw new amount_error("Quoted commodity symbol lacks closing quote"); } else { READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) && c != '-' && c != '.'); @@ -939,14 +939,14 @@ void parse_annotations(std::istream& in, const std::string& symbol, char c = peek_next_nonws(in); if (c == '{') { 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); READ_INTO(in, buf, 255, c, c != '}'); if (c == '}') in.get(c); else - throw amount_error("Commodity price lacks closing brace"); + throw new amount_error("Commodity price lacks closing brace"); price = buf; if (! added_name) { added_name = true; @@ -962,14 +962,14 @@ void parse_annotations(std::istream& in, const std::string& symbol, } else if (c == '[') { 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); READ_INTO(in, buf, 255, c, c != ']'); if (c == ']') in.get(c); else - throw amount_error("Commodity date lacks closing bracket"); + throw new amount_error("Commodity date lacks closing bracket"); date = buf; if (! added_name) { added_name = true; @@ -985,14 +985,14 @@ void parse_annotations(std::istream& in, const std::string& symbol, } else if (c == '(') { 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); READ_INTO(in, buf, 255, c, c != ')'); if (c == ')') in.get(c); else - throw amount_error("Commodity tag lacks closing parenthesis"); + throw new amount_error("Commodity tag lacks closing parenthesis"); tag = buf; if (! added_name) { added_name = true; @@ -1075,7 +1075,7 @@ void amount_t::parse(std::istream& in, unsigned char flags) } if (quant.empty()) - throw amount_error("No quantity specified for amount"); + throw new amount_error("No quantity specified for amount"); _init(); @@ -1794,10 +1794,13 @@ void export_amount() .add_property("commodity", make_function(&amount_t::commodity, return_value_policy<reference_existing_object>()), - &amount_t::set_commodity) - .add_property("quantity", py_amount_quantity) + make_function(&amount_t::set_commodity, + with_custodian_and_ward<1, 2>())) + + .def("strip_annotations", &amount_t::strip_annotations) .def("negate", &amount_t::negate) + .def("negated", &amount_t::negated) .def("parse", py_parse_1) .def("parse", py_parse_2) .def("reduce", &amount_t::reduce) @@ -1816,24 +1819,32 @@ void export_amount() scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN; scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET; + scope().attr("COMMODITY_STYLE_BUILTIN") = COMMODITY_STYLE_BUILTIN; -#if 0 class_< commodity_t > ("Commodity") .add_property("symbol", &commodity_t::symbol) - .add_property("name", &commodity_t::name) - .add_property("note", &commodity_t::note) - .add_property("precision", &commodity_t::precision) - .add_property("flags", &commodity_t::flags) +#if 0 + .add_property("name", &commodity_t::name, &commodity_t::set_name) + .add_property("note", &commodity_t::note, &commodity_t::set_note) + .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 .add_property("updater", &commodity_t::updater) #endif .add_property("smaller", make_getter(&commodity_t::smaller, + return_value_policy<reference_existing_object>()), + make_setter(&commodity_t::smaller, return_value_policy<reference_existing_object>())) .add_property("larger", make_getter(&commodity_t::larger, + return_value_policy<reference_existing_object>()), + make_setter(&commodity_t::larger, return_value_policy<reference_existing_object>())) .def(self_ns::str(self)) @@ -1841,6 +1852,7 @@ void export_amount() .def("find", py_find_commodity, return_value_policy<reference_existing_object>()) .staticmethod("find") +#endif .def("add_price", &commodity_t::add_price) .def("remove_price", &commodity_t::remove_price) @@ -1848,7 +1860,6 @@ void export_amount() .def("valid", &commodity_t::valid) ; -#endif #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); @@ -12,6 +12,7 @@ #include <exception> #include "debug.h" +#include "error.h" namespace ledger { @@ -624,15 +625,10 @@ inline std::time_t amount_t::date() const { } } -class amount_error : public std::exception { - std::string reason; +class amount_error : public error { public: - amount_error(const std::string& _reason) throw() : reason(_reason) {} + amount_error(const std::string& reason) throw() : error(reason) {} virtual ~amount_error() throw() {} - - virtual const char* what() const throw() { - return reason.c_str(); - } }; } // namespace ledger @@ -22,7 +22,7 @@ amount_t balance_t::amount(const commodity_t& commodity) const std::ostringstream errmsg; errmsg << "Requested amount of a balance with multiple commodities: " << *this; - throw amount_error(errmsg.str()); + throw new amount_error(errmsg.str()); } } else if (amounts.size() > 0) { @@ -214,7 +214,7 @@ balance_t& balance_t::operator*=(const balance_t& bal) std::ostringstream errmsg; 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" << " not found in that balance: " << *this << " * " << amt; - throw amount_error(errmsg.str()); + throw new amount_error(errmsg.str()); } } return *this; @@ -266,7 +266,7 @@ balance_t& balance_t::operator/=(const balance_t& bal) if (bal.realzero()) { std::ostringstream errmsg; errmsg << "Attempt to divide by zero: " << *this << " / " << bal; - throw amount_error(errmsg.str()); + throw new amount_error(errmsg.str()); } else if (realzero()) { return *this = 0L; @@ -285,7 +285,7 @@ balance_t& balance_t::operator/=(const balance_t& bal) std::ostringstream errmsg; 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()) { std::ostringstream errmsg; errmsg << "Attempt to divide by zero: " << *this << " / " << amt; - throw amount_error(errmsg.str()); + throw new amount_error(errmsg.str()); } else if (realzero()) { return *this = 0L; @@ -326,7 +326,7 @@ balance_t& balance_t::operator/=(const amount_t& amt) errmsg << "Attempt to divide balance by a commodity" << " not found in that balance: " << *this << " * " << amt; - throw amount_error(errmsg.str()); + throw new amount_error(errmsg.str()); } } return *this; @@ -349,7 +349,7 @@ balance_t::operator amount_t() const std::ostringstream errmsg; errmsg << "Cannot convert a balance with " << "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("strip_annotations", &balance_t::strip_annotations) .def("write", &balance_t::write) - .def("abs", &balance_t::abs) .def("round", &balance_t::round) .def("negate", &balance_t::negate) .def("negated", &balance_t::negated) @@ -553,7 +552,6 @@ void export_balance() .def("date", &balance_pair_t::date) .def("strip_annotations", &balance_pair_t::strip_annotations) .def("write", &balance_pair_t::write) - .def("abs", &balance_pair_t::abs) .def("round", &balance_pair_t::round) .def("negate", &balance_pair_t::negate) .def("negated", &balance_pair_t::negated) @@ -416,6 +416,16 @@ class balance_t if ((*i).second.commodity()) (*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) { @@ -847,6 +857,13 @@ class balance_pair_t quantity.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) { @@ -621,9 +621,29 @@ unsigned int read_binary_journal(std::istream& in, std::pair<base_commodities_map::iterator, bool> result = commodity_base_t::commodities.insert (base_commodities_pair(commodity->symbol, commodity)); - if (! result.second) - throw error(std::string("Failed to read base commodity from cache: ") + - commodity->symbol); + if (! result.second) { + 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); + + (*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++) @@ -655,9 +675,9 @@ unsigned int read_binary_journal(std::istream& in, std::pair<commodities_map::iterator, bool> result = commodity_t::commodities.insert(commodities_pair(mapping_key, commodity)); - if (! result.second) - throw error(std::string("Failed to read commodity from cache: ") + - commodity->ptr->symbol); + if (! result.second && commodity->annotated) + throw new error(std::string("Failed to read commodity from cache: ") + + commodity->ptr->symbol); } commodity_t::ident_t ident; @@ -52,8 +52,9 @@ namespace { void config_t::reset() { - amount_expr = "a"; - total_expr = "O"; + ledger::amount_expr.reset(new value_expr("a")); + ledger::total_expr.reset(new value_expr("O")); + pricing_leeway = 24 * 3600; budget_flags = BUDGET_NO_BUDGET; balance_format = "%20T %2_%-a\n"; @@ -293,8 +294,10 @@ void config_t::process_options(const std::string& command, // Setup the values of %t and %T, used in format strings - ledger::amount_expr.reset(new value_expr(amount_expr)); - ledger::total_expr.reset(new value_expr(total_expr)); + if (! amount_expr.empty()) + ledger::amount_expr.reset(new value_expr(amount_expr)); + if (! total_expr.empty()) + ledger::total_expr.reset(new value_expr(total_expr)); // If downloading is to be supported, configure the updater @@ -699,8 +702,8 @@ OPT_BEGIN(file, "f:") { if (std::string(optarg) == "-" || access(optarg, R_OK) != -1) config->data_file = optarg; else - throw error(std::string("The ledger file '") + optarg + - "' does not exist or is not readable"); + throw new error(std::string("The ledger file '") + optarg + + "' does not exist or is not readable"); } OPT_END(file); OPT_BEGIN(cache, ":") { @@ -747,8 +750,8 @@ OPT_BEGIN(begin, "b:") { if (interval.begin) std::strftime(buf, 127, formats[0], std::localtime(&interval.begin)); else - throw error(std::string("Could not determine beginning of period '") + - optarg + "'"); + throw new error(std::string("Could not determine beginning of period '") + + optarg + "'"); if (! config->predicate.empty()) config->predicate += "&"; @@ -763,8 +766,8 @@ OPT_BEGIN(end, "e:") { if (interval.end) std::strftime(buf, 127, formats[0], std::localtime(&interval.end)); else - throw error(std::string("Could not determine end of period '") + - optarg + "'"); + throw new error(std::string("Could not determine end of period '") + + optarg + "'"); if (! config->predicate.empty()) config->predicate += "&"; @@ -1048,11 +1051,11 @@ OPT_BEGIN(display, "d:") { } OPT_END(display); OPT_BEGIN(amount, "t:") { - config->amount_expr = optarg; + ledger::amount_expr.reset(new value_expr(optarg)); } OPT_END(amount); OPT_BEGIN(total, "T:") { - config->total_expr = optarg; + ledger::total_expr.reset(new value_expr(optarg)); } OPT_END(total); OPT_BEGIN(amount_data, "j") { @@ -1080,50 +1083,56 @@ OPT_BEGIN(download, "Q") { } OPT_END(download); OPT_BEGIN(quantity, "O") { - config->amount_expr = "a"; - config->total_expr = "O"; + ledger::amount_expr.reset(new value_expr("a")); + ledger::total_expr.reset(new value_expr("O")); } OPT_END(quantity); OPT_BEGIN(basis, "B") { - config->amount_expr = "b"; - config->total_expr = "B"; + ledger::amount_expr.reset(new value_expr("b")); + ledger::total_expr.reset(new value_expr("B")); } OPT_END(basis); OPT_BEGIN(price, "I") { - config->amount_expr = "i"; - config->total_expr = "I"; + ledger::amount_expr.reset(new value_expr("i")); + ledger::total_expr.reset(new value_expr("I")); } OPT_END(price); OPT_BEGIN(market, "V") { config->show_revalued = true; - config->amount_expr = "v"; - config->total_expr = "V"; + ledger::amount_expr.reset(new value_expr("v")); + ledger::total_expr.reset(new value_expr("V")); } OPT_END(market); OPT_BEGIN(performance, "g") { - config->amount_expr = "P(a,m)-b"; // same as 'g', but priced now - config->total_expr = "P(O,m)-B"; + ledger::amount_expr.reset(new value_expr("P(a,m)-b")); + ledger::total_expr.reset(new value_expr("P(O,m)-B")); } OPT_END(performance); OPT_BEGIN(gain, "G") { config->show_revalued = config->show_revalued_only = true; - config->amount_expr = "a"; - config->total_expr = "G"; + ledger::amount_expr.reset(new value_expr("a")); + ledger::total_expr.reset(new value_expr("G")); } OPT_END(gain); 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_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_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); ////////////////////////////////////////////////////////////////////// diff --git a/datetime.cc b/datetime.cc index f5d071c3..74efce89 100644 --- a/datetime.cc +++ b/datetime.cc @@ -107,7 +107,7 @@ static void parse_inclusion_specifier(const std::string& word, struct std::tm 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_min = 0; @@ -4,6 +4,8 @@ #include <ctime> #include <sstream> +#include "error.h" + struct interval_t; struct datetime_t @@ -69,6 +71,10 @@ inline std::ostream& operator<<(std::ostream& out, const datetime_t& moment) { out << buf; } +inline long operator-(const datetime_t& left, const datetime_t& right) { + return (long)left.when - (long)right.when; +} + struct interval_t { unsigned int years; @@ -120,15 +126,10 @@ bool parse_date(const char * date_str, std::time_t * result, const int year = -1); bool quick_parse_date(const char * date_str, std::time_t * result); -class datetime_error : public std::exception { - std::string reason; +class datetime_error : public error { public: - datetime_error(const std::string& _reason) throw() : reason(_reason) {} + datetime_error(const std::string& reason) throw() : error(reason) {} virtual ~datetime_error() throw() {} - - virtual const char* what() const throw() { - return reason.c_str(); - } }; #endif // _DATETIME_H @@ -4,8 +4,6 @@ #include <map> #include <fstream> -#include <cstdlib> -#include <cstring> #include <unistd.h> // for the `write' method @@ -112,3 +110,16 @@ static struct init_streams { } _debug_init; #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 @@ -12,7 +12,28 @@ #endif #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 ////////////////////////////////////////////////////////////////////// @@ -105,7 +126,9 @@ void operator delete[](void*, const std::nothrow_t&) throw(); #if DEBUG_LEVEL == NO_SEATBELT +#ifdef assert #undef assert +#endif #define assert(x) #define CONFIRM(x) @@ -17,10 +17,10 @@ entry_t * derive_new_entry(journal_t& journal, entry_t * matching = NULL; 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) - throw error("Too few arguments to 'entry'"); + throw new error("Too few arguments to 'entry'"); mask_t regexp(*i++); @@ -169,7 +169,7 @@ entry_t * derive_new_entry(journal_t& journal, done: if (! run_hooks(journal.entry_finalize_hooks, *added) || ! added->finalize()) - throw error("Failed to finalize derived entry (check commodities)"); + throw new error("Failed to finalize derived entry (check commodities)"); return added.release(); } @@ -1,63 +1,231 @@ #ifndef _ERROR_H #define _ERROR_H -#include "journal.h" - #include <exception> #include <string> +#include <cstring> #include <sstream> +#include <list> -namespace ledger { +class error_context +{ + public: + std::string desc; + + 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() {} -class error : public std::exception { + 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; public: - error(const std::string& _reason) throw() : reason(_reason) {} - virtual ~error() throw() {} + std::list<error_context *> context; + + 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() { 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 { 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() {} }; class value_expr_error : public error { 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() {} }; class interval_expr_error : public error { 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() {} }; class format_error : public error { 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() {} }; class parse_error : public error { - unsigned int line; - std::string file; public: - parse_error(const std::string& _file, const unsigned int _line, - const std::string& reason) throw() - : error(reason), line(_line), file(_file) {} + parse_error(const std::string& reason, error_context * ctxt = NULL) throw() + : error(reason, ctxt) {} virtual ~parse_error() throw() {} +}; - virtual const char* what() const throw() { - std::ostringstream msg; - msg << file << ", line " << line << ": " << error::what(); - return msg.str().c_str(); - } +class value_error : public error { + public: + value_error(const std::string& reason, error_context * ctxt = NULL) throw() + : 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 @@ -157,7 +157,7 @@ element_t * format_t::parse_elements(const std::string& fmt) p++; } if (*p != ')') - throw format_error("Missing ')'"); + throw new format_error("Missing ')'"); current->type = element_t::VALUE_EXPR; @@ -178,7 +178,7 @@ element_t * format_t::parse_elements(const std::string& fmt) p++; } if (*p != ']') - throw format_error("Missing ']'"); + throw new format_error("Missing ']'"); current->type = element_t::DATE_STRING; 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::TOTAL: case element_t::VALUE_EXPR: { - value_calc * calc = NULL; + value_expr * calc = NULL; switch (elem->type) { case element_t::AMOUNT: calc = amount_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; } -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 - = "\n%D %X%C%P\n %-34A %12o\n%/ %-34A %12o\n"; + std::string print_format; + + 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); - walk_transactions(const_cast<transactions_list&>(entry.transactions), + walk_transactions(const_cast<transactions_list&>(entry_base.transactions), formatter); formatter.flush(); clear_transaction_xdata cleaner; - walk_transactions(const_cast<transactions_list&>(entry.transactions), + walk_transactions(const_cast<transactions_list&>(entry_base.transactions), cleaner); } @@ -133,7 +133,8 @@ class format_entries : public format_transactions 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, const item_predicate<account_t>& disp_pred, @@ -415,12 +415,12 @@ unsigned int gnucash_parser_t::parse(std::istream& in, unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; const char * msg = XML_ErrorString(XML_GetErrorCode(parser)); XML_ParserFree(parser); - throw parse_error(path, line, msg); + throw new parse_error(msg); } if (! have_error.empty()) { 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; have_error = ""; } @@ -79,15 +79,13 @@ bool entry_base_t::remove_transaction(transaction_t * xact) return true; } -value_t entry_balance; - bool entry_base_t::finalize() { // Scan through and compute the total balance for the entry. This // is used for auto-calculating the value of entries with no cost, // and the per-unit price of unpriced commodities. - value_t& balance = entry_balance; + value_t balance; bool no_amounts = true; for (transactions_list::const_iterator x = transactions.begin(); @@ -106,7 +104,8 @@ bool entry_base_t::finalize() if ((*x)->cost && (*x)->amount.commodity().annotated) { 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) 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) @@ -151,8 +151,6 @@ class entry_base_t virtual bool valid() const = 0; }; -extern value_t entry_balance; - class entry_t : public entry_base_t { public: diff --git a/ledger.texi b/ledger.texi index 9fa7ae63..4842e234 100644 --- a/ledger.texi +++ b/ledger.texi @@ -5,7 +5,7 @@ @dircategory User Applications @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 modification, are permitted provided that the following conditions are @@ -121,11 +121,16 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) else if (command == "parse") { value_auto_ptr expr(ledger::parse_value_expr(*arg)); if (config.verbose_mode) { + std::cout << "Value expression tree:" << std::endl; ledger::dump_value_expr(std::cout, expr.get()); 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) { switch (result.type) { 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; + return 0; } else if (command == "expr") { // this gets done below... } - else - throw error(std::string("Unrecognized command '") + command + "'"); + else { + throw new error(std::string("Unrecognized command '") + command + "'"); + } // Parse initialization files, ledger data, price database, etc. @@ -153,8 +160,8 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) { TRACE_PUSH(parser, "Parsing journal file"); if (parse_ledger_data(config, journal.get()) == 0) - throw error("Please specify ledger file using -f" - " or LEDGER_FILE environment variable."); + throw new error("Please specify ledger file using -f" + " or LEDGER_FILE environment variable."); 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; if (command == "w") { 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++; } @@ -193,11 +200,11 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) else if (! config.pager.empty()) { status = pipe(pfd); if (status == -1) - throw error("Failed to create pipe"); + throw new error("Failed to create pipe"); status = fork(); if (status < 0) { - throw error("Failed to fork child process"); + throw new error("Failed to fork child process"); } else if (status == 0) { // child const char *arg0; @@ -238,10 +245,16 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) if (command == "expr") { value_auto_ptr expr(ledger::parse_value_expr(*arg)); if (config.verbose_mode) { + std::cout << "Value expression tree:" << std::endl; ledger::dump_value_expr(std::cout, expr.get()); 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) { switch (result.type) { 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; + 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) formatter = new format_xml_entries(*out, config.show_totals); #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 } else 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(&status); if (status & 0xffff != 0) - throw error("Something went wrong in the pager"); + throw new error("Something went wrong in the pager"); } #endif @@ -411,12 +425,35 @@ int main(int argc, char * argv[], char * envp[]) TRACE_POP(main, "Ledger done"); 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) { + std::cout.flush(); std::cerr << "Error: " << err.what() << std::endl; return 1; } - catch (int& val) { - return val; // this acts like a std::setjmp + catch (int status) { + return status; } } @@ -27,7 +27,8 @@ mask_t::mask_t(const std::string& pat) : exclude(false) regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS, &error, &erroffset, NULL); 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) @@ -4,6 +4,8 @@ #include <string> #include <exception> +#include "error.h" + class mask_t { public: @@ -18,15 +20,10 @@ class mask_t bool match(const std::string& str) const; }; -class mask_error : public std::exception { - std::string reason; +class mask_error : public error { public: - mask_error(const std::string& _reason) throw() : reason(_reason) {} + mask_error(const std::string& reason) throw() : error(reason) {} virtual ~mask_error() throw() {} - - virtual const char* what() const throw() { - return reason.c_str(); - } }; #endif // _MASK_H @@ -1,6 +1,7 @@ #include "option.h" #include "config.h" #include "debug.h" +#include "error.h" #include <iostream> #include <cstdarg> @@ -10,7 +11,17 @@ namespace { inline void process_option(option_t * opt, const char * arg = NULL) { if (! opt->handled) { - opt->handler(arg); + try { + 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; } } @@ -49,9 +60,8 @@ bool process_option(option_t * options, const std::string& name, const char * arg) { option_t * opt = search_options(options, name.c_str()); - if (opt && ! opt->handled) { - opt->handler(arg); - opt->handled = true; + if (opt) { + process_option(opt, arg); return true; } return false; @@ -90,25 +100,25 @@ void process_arguments(option_t * options, int argc, char ** argv, opt = search_options(options, name); if (! opt) - throw option_error(std::string("illegal option --") + name); + throw new option_error(std::string("illegal option --") + name); if (opt->wants_arg && ! value) { value = *++i; if (! value) - throw option_error(std::string("missing option argument for --") + - name); + throw new option_error(std::string("missing option argument for --") + + name); } process_option(opt, value); } else { char c = (*i)[1]; opt = search_options(options, c); if (! opt) - throw option_error(std::string("illegal option -") + c); + throw new option_error(std::string("illegal option -") + c); if (opt->wants_arg) { value = *++i; 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 = '\0'; - if (*q == '=') - process_option(options, buf, q + 1); + if (*q == '=') { + try { + 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; + } + } } } @@ -5,6 +5,8 @@ #include <string> #include <exception> +#include "error.h" + typedef void (*handler_t)(const char * arg); struct option_t { @@ -15,15 +17,10 @@ struct option_t { bool handled; }; -class option_error : public std::exception { - std::string reason; +class option_error : public error { public: - option_error(const std::string& _reason) throw() : reason(_reason) {} + option_error(const std::string& reason) throw() : error(reason) {} virtual ~option_error() throw() {} - - virtual const char* what() const throw() { - return reason.c_str(); - } }; bool process_option(option_t * options, const std::string& opt, @@ -83,7 +83,7 @@ unsigned int parse_journal_file(const std::string& path, journal->sources.push_back(path); 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) original_file = &path; @@ -119,8 +119,8 @@ unsigned int parse_ledger_data(config_t& config, if (parse_journal_file(config.init_file, config, journal) || journal->auto_entries.size() > 0 || journal->period_entries.size() > 0) - throw error(std::string("Entries found in initialization file '") + - config.init_file + "'"); + throw new error(std::string("Entries found in initialization file '") + + config.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() && access(journal->price_db.c_str(), R_OK) != -1) { 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 { DEBUG_PRINT("ledger.config.cache", "read price database " << journal->price_db); @@ -80,7 +80,7 @@ unsigned int qif_parser_t::parse(std::istream& in, case '\t': if (peek_next_nonws(in) != '\n') { get_line(in); - throw parse_error(path, linenum, "Line begins with whitespace"); + throw new parse_error("Line begins with whitespace"); } // fall through... @@ -97,16 +97,15 @@ unsigned int qif_parser_t::parse(std::istream& in, std::strcmp(line, "Type:Cat") == 0 || std::strcmp(line, "Type:Class") == 0 || std::strcmp(line, "Type:Memorized") == 0) - throw parse_error(path, linenum, - std::string("QIF files of type ") + line + - " are not supported."); + throw new parse_error(std::string("QIF files of type ") + line + + " are not supported."); break; case 'D': SET_BEG_POS_AND_LINE(); get_line(in); if (! parse_date(line, &entry->_date)) - throw parse_error(path, linenum, "Failed to parse date"); + throw new parse_error("Failed to parse date"); break; case 'T': @@ -75,9 +75,9 @@ void quotes_by_script::operator()(commodity_base_t& commodity, << " " << price << endl; } } else { - throw error(std::string("Failed to download price for '") + - commodity.symbol + "' (command: \"getquote " + - commodity.symbol + "\")"); + throw new error(std::string("Failed to download price for '") + + commodity.symbol + "' (command: \"getquote " + + commodity.symbol + "\")"); } } diff --git a/reconcile.cc b/reconcile.cc index e2f11198..b56ebcf2 100644 --- a/reconcile.cc +++ b/reconcile.cc @@ -60,7 +60,7 @@ void reconcile_transactions::flush() } 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); balance.cast(value_t::AMOUNT); @@ -70,8 +70,8 @@ void reconcile_transactions::flush() balance -= cleared_balance; if (balance.type >= value_t::BALANCE) - throw error(std::string("Reconcile balance is not of the same commodity ('") + - b_comm.symbol() + "' != '" + cb_comm.symbol() + "')"); + throw new error(std::string("Reconcile balance is not of the same commodity ('") + + b_comm.symbol() + "' != '" + cb_comm.symbol() + "')"); // If the amount to reconcile is the same as the pending balance, // then assume an exact match and return the results right away. @@ -81,7 +81,7 @@ void reconcile_transactions::flush() search_for_balance(to_reconcile, &first, first)) { push_to_handler(first); } else { - throw error("Could not reconcile account!"); + throw new error("Could not reconcile account!"); } } @@ -2,15 +2,21 @@ 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") print val - -amt = Amount("$100.00") - -print amt.commodity -amt.commodity = amt.commodity -print amt -amt.commodity = NullCommodity -print amt -print amt.quantity @@ -69,6 +69,9 @@ transaction_t * parse_transaction(char * line, account_t * account, { std::istringstream in(line); + std::string err_desc; + try { + // The account will be determined later... std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); if (entry) @@ -107,7 +110,7 @@ transaction_t * parse_transaction(char * line, account_t * account, } 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 * e = &line[account_end]; @@ -144,7 +147,13 @@ transaction_t * parse_transaction(char * line, account_t * account, if (p == ';') goto parse_note; if (p == '(') { - xact->amount_expr = parse_value_expr(in)->acquire(); + try { + 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 << ": " << "Parsed an amount expression"); #ifdef DEBUG_ENABLED @@ -156,8 +165,7 @@ transaction_t * parse_transaction(char * line, account_t * account, } #endif if (! compute_amount(xact->amount_expr, xact->amount, xact.get())) - throw parse_error(path, linenum, - "Value expression for amount failed to compute"); + throw new parse_error("Value expression for amount failed to compute"); DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << "The computed amount is " << xact->amount); } else { @@ -209,8 +217,7 @@ transaction_t * parse_transaction(char * line, account_t * account, p = peek_next_nonws(in); if (p == '(') - throw parse_error(path, linenum, - "A transaction's cost may not be a value expression"); + throw new parse_error("A transaction's cost may not be a value expression"); xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE); 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, '=')) { *p++ = '\0'; if (! quick_parse_date(p, &xact->_date_eff)) - throw parse_error(path, linenum, - "Failed to parse effective date"); + throw new parse_error("Failed to parse effective 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: 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, @@ -337,11 +352,11 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master, if (char * p = std::strchr(line, '=')) { *p++ = '\0'; 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)) - throw parse_error(path, linenum, "Failed to parse date"); + throw new parse_error("Failed to parse date"); TIMER_STOP(entry_date); @@ -436,8 +451,7 @@ static inline void parse_symbol(char *& p, std::string& symbol) if (*p == '"') { char * q = std::strchr(p + 1, '"'); if (! q) - throw parse_error(path, linenum, - "Quoted commodity symbol lacks closing quote"); + throw new parse_error("Quoted commodity symbol lacks closing quote"); symbol = std::string(p + 1, 0, q - p - 1); p = q + 2; } else { @@ -449,7 +463,7 @@ static inline void parse_symbol(char *& p, std::string& symbol) p += symbol.length(); } 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 @@ -459,9 +473,9 @@ bool textual_parser_t::test(std::istream& in) const in.read(buf, 5); if (std::strncmp(buf, "<?xml", 5) == 0) { #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 - 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 } @@ -491,12 +505,13 @@ static void clock_out_from_timelog(const std::time_t when, curr->add_transaction(xact); if (! journal->add_entry(curr.get())) - throw parse_error(path, linenum, - "Failed to record 'out' timelog entry"); + throw new parse_error("Failed to record 'out' timelog entry"); else curr.release(); } +static std::list<std::pair<std::string, int> > include_stack; + unsigned int textual_parser_t::parse(std::istream& in, config_t& config, journal_t * journal, @@ -544,7 +559,7 @@ unsigned int textual_parser_t::parse(std::istream& in, case '\t': { char * p = skip_ws(line); if (*p && *p != '\r') - throw parse_error(path, linenum - 1, "Line begins with whitespace"); + throw new parse_error("Line begins with whitespace"); break; } @@ -563,7 +578,7 @@ unsigned int textual_parser_t::parse(std::istream& in, last_account = account_stack.front()->find_account(p); } else { last_account = NULL; - throw parse_error(path, linenum, "Cannot parse timelog entry date"); + throw new parse_error("Cannot parse timelog entry date"); } break; } @@ -582,7 +597,7 @@ unsigned int textual_parser_t::parse(std::istream& in, clock_out_from_timelog(std::mktime(&when), journal); count++; } else { - throw parse_error(path, linenum, "Cannot parse timelog entry date"); + throw new parse_error("Cannot parse timelog entry date"); } 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)) { date = std::mktime(&when); } else { - throw parse_error(path, linenum, "Failed to parse date"); + throw new parse_error("Failed to parse date"); } std::string symbol; @@ -691,8 +706,7 @@ unsigned int textual_parser_t::parse(std::istream& in, case '~': { // period entry period_entry_t * pe = new period_entry_t(skip_ws(line + 1)); if (! pe->period) - throw parse_error(path, linenum, - std::string("Parsing time period '") + line + "'"); + throw new parse_error(std::string("Parsing time period '") + line + "'"); if (parse_transactions(in, account_stack.front(), *pe, "period", end_pos)) { @@ -705,7 +719,7 @@ unsigned int textual_parser_t::parse(std::istream& in, pe->end_pos = end_pos; pe->end_line = linenum; } else { - throw parse_error(path, linenum, "Period entry failed to balance"); + throw new parse_error("Period entry failed to balance"); } } break; @@ -730,11 +744,14 @@ unsigned int textual_parser_t::parse(std::istream& in, if (pos != std::string::npos) path = std::string(save_path.prev, 0, pos + 1) + path; } - DEBUG_PRINT("ledger.textual.include", "line " << linenum << ": " << "Including path '" << path << "'"); + + include_stack.push_back(std::pair<std::string, int> + (journal->sources.back(), linenum - 1)); count += parse_journal_file(path, config, journal, account_stack.front()); + include_stack.pop_back(); } else if (word == "account") { account_t * acct; @@ -784,34 +801,32 @@ unsigned int textual_parser_t::parse(std::istream& in, entry->end_line = linenum; count++; } else { - print_entry(std::cerr, *entry); delete entry; - - std::ostringstream errmsg; - errmsg << "Entry above does not balance; remainder is: " - << entry_balance; - throw parse_error(path, first_line, errmsg.str()); + throw new parse_error("Entry does not balance"); } } else { - throw parse_error(path, first_line, "Failed to parse entry"); + throw new parse_error("Failed to parse entry"); } end_pos = pos; break; } } } - catch (const parse_error& err) { - std::cerr << "Error: " << err.what() << std::endl; - errors++; - } - catch (const amount_error& err) { - std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " - << err.what() << std::endl;; - errors++; - } - catch (const error& err) { - std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " - << err.what() << std::endl;; + catch (error * err) { + for (std::list<std::pair<std::string, int> >::reverse_iterator i = + include_stack.rbegin(); + i != include_stack.rend(); + i++) + err->context.push_back(new include_context((*i).first, (*i).second, + "In file included from")); + err->context.push_front(new file_context(path, linenum - 1)); + + std::cout.flush(); + if (errors > 0) + std::cerr << std::endl; + err->reveal_context(std::cerr, "Error"); + std::cerr << err->what() << std::endl; + delete err; errors++; } beg_pos = end_pos; @@ -827,7 +842,7 @@ unsigned int textual_parser_t::parse(std::istream& in, journal->remove_entry_finalizer(&auto_entry_finalizer); if (errors > 0) - throw error(std::string("Errors parsing file '") + path + "'"); + throw (int)errors; TIMER_STOP(parsing_total); @@ -869,8 +884,8 @@ void write_textual_journal(journal_t& journal, std::string path, #endif if (found.empty()) - throw error(std::string("Journal does not refer to file '") + - path + "'"); + throw new error(std::string("Journal does not refer to file '") + + path + "'"); entries_list::iterator el = journal.entries.begin(); auto_entries_list::iterator al = journal.auto_entries.begin(); @@ -7,8 +7,8 @@ namespace ledger { -std::auto_ptr<value_calc> amount_expr; -std::auto_ptr<value_calc> total_expr; +std::auto_ptr<value_expr> amount_expr; +std::auto_ptr<value_expr> total_expr; std::auto_ptr<scope_t> global_scope; 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) { value_t result; - expr->compute(result, xact ? details_t(*xact) : details_t(), context); - switch (result.type) { - case value_t::BOOLEAN: - amt = *((bool *) result.data); - break; - case value_t::INTEGER: - amt = *((long *) result.data); - break; - case value_t::AMOUNT: + try { + expr->compute(result, xact ? details_t(*xact) : details_t(), context); + result.cast(value_t::AMOUNT); amt = *((amount_t *) result.data); - break; - - case value_t::DATETIME: - case value_t::BALANCE: - case value_t::BALANCE_PAIR: - return false; + } + 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 amount expression:"; + } + throw err; } return true; } @@ -145,6 +144,7 @@ namespace { void value_expr_t::compute(value_t& result, const details_t& details, value_expr_t * context) const { + try { switch (kind) { case 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); result -= moment; } 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; } @@ -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); expr->compute(result, details, context); 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"); temp.set_commodity(((amount_t *) result.data)->commodity()); 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->compute(result, details, context); 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"); one.set_commodity(((amount_t *) result.data)->commodity()); result = one; @@ -594,7 +597,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case O_DEF: - throw compute_error("Cannot compute function definition"); + throw new compute_error("Cannot compute function definition"); case O_REF: { assert(left); @@ -617,7 +620,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, value_t moment; expr->compute(moment, details, context); 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)); break; @@ -718,20 +722,27 @@ void value_expr_t::compute(value_t& result, const details_t& details, assert(0); 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') { if ((unsigned char) c == 0xff) { if (wanted) - throw value_expr_error(std::string("Missing '") + wanted + "'"); + throw new value_expr_error(std::string("Missing '") + wanted + "'"); else - throw value_expr_error("Unexpected end"); + throw new value_expr_error("Unexpected end"); } else { if (wanted) - throw value_expr_error(std::string("Invalid char '") + c + + throw new value_expr_error(std::string("Invalid char '") + c + "' (wanted '" + wanted + "')"); 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 value_auto_ptr def(parse_boolean_expr(in, params.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->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] == 'w' || buf[0] == 'W' || buf[0] == 'e')) 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) { 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 << "': saw " << count << ", wanted " << def->left->constant_i; - throw value_expr_error(errmsg.str()); + throw new value_expr_error(errmsg.str()); } } else { @@ -1467,7 +1478,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, if (! node.get()) { in.get(c); 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 unexpected(c); } else if (! partial) { @@ -1481,6 +1492,264 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, 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, const int depth) { @@ -33,39 +33,6 @@ struct details_t #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 { enum kind_t { @@ -252,8 +219,8 @@ struct scope_t = symbols.insert(symbol_pair(name, def)); if (! result.second) { def->release(); - throw value_expr_error(std::string("Redefinition of '") + - name + "' in same scope"); + throw new compute_error(std::string("Redefinition of '") + + name + "' in same scope"); } } def->acquire(); @@ -282,37 +249,87 @@ bool compute_amount(value_expr_t * expr, amount_t& amt, struct scope_t; value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope); -inline value_expr_t * parse_boolean_expr(const char * p, +inline value_expr_t * parse_boolean_expr(const std::string& str, scope_t * scope = NULL) { - std::istringstream stream(p); - return parse_boolean_expr(stream, 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 std::string& str, +inline value_expr_t * parse_boolean_expr(const char * p, scope_t * scope = NULL) { - return parse_boolean_expr(str.c_str(), scope); + return parse_boolean_expr(std::string(p), scope); } value_expr_t * parse_value_expr(std::istream& in, scope_t * scope = NULL, const bool partial = false); -inline value_expr_t * parse_value_expr(const char * p, +inline value_expr_t * parse_value_expr(const std::string& str, scope_t * scope = NULL, const bool partial = false) { - std::istringstream stream(p); - return parse_value_expr(stream, scope, partial); + 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 std::string& str, +inline value_expr_t * parse_value_expr(const char * p, scope_t * scope = NULL, const bool partial = false) { - return parse_value_expr(str.c_str(), scope); + return parse_value_expr(std::string(p), scope, partial); } void dump_value_expr(std::ostream& out, const value_expr_t * node, 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 @@ -323,10 +340,11 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node, struct value_auto_ptr { value_expr_t * ptr; 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() { - if (ptr && ptr->refc == 0) - delete ptr; + if (ptr) + ptr->release(); } value_expr_t& operator*() const throw() { return *ptr; @@ -342,31 +360,24 @@ struct value_auto_ptr { } void reset(value_expr_t * p = 0) throw() { if (p != ptr) { - if (ptr && ptr->refc == 0) - delete ptr; - ptr = p; + if (ptr) + ptr->release(); + ptr = p->acquire(); } } }; ////////////////////////////////////////////////////////////////////// -class value_expr : public value_calc +class value_expr { - std::string expr; value_expr_t * parsed; - public: + std::string expr; + value_expr(const std::string& _expr) : expr(_expr) { DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); - try { - parsed = parse_value_expr(expr); - parsed->acquire(); - } - catch (const value_expr_error& err) { - throw error(std::string("In value expression '") + - expr + "': " + err.what()); - } + parsed = parse_value_expr(expr)->acquire(); } value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) { DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); @@ -380,18 +391,18 @@ public: virtual void compute(value_t& result, const details_t& details = details_t(), 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(), value_expr_t * context = NULL) { value_t temp; - parsed->compute(temp, details, context); + guarded_compute(parsed, temp, details, context); return temp; } }; -extern std::auto_ptr<value_calc> amount_expr; -extern std::auto_ptr<value_calc> total_expr; +extern std::auto_ptr<value_expr> amount_expr; +extern std::auto_ptr<value_expr> total_expr; inline void compute_amount(value_t& result, const details_t& details = details_t()) { @@ -425,15 +436,8 @@ class item_predicate item_predicate(const std::string& _predicate) : predicate(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>"); - if (! _predicate.empty()) { - try { - predicate = parse_value_expr(_predicate)->acquire(); - } - catch (value_expr_error& err) { - throw value_expr_error(std::string("In predicate '") + - _predicate + "': " + err.what()); - } - } + if (! _predicate.empty()) + predicate = parse_value_expr(_predicate)->acquire(); } item_predicate(const value_expr_t * _predicate = NULL) : predicate(_predicate->acquire()) { @@ -1,5 +1,6 @@ #include "value.h" #include "debug.h" +#include "error.h" namespace ledger { @@ -93,13 +94,13 @@ value_t& value_t::operator=(const value_t& value) value_t& value_t::operator+=(const value_t& value) { 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) - 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) { 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: 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) { if (value.type == BOOLEAN) - throw value_error("Cannot subtract a boolean from a value"); - else if (value.type == DATETIME) - throw value_error("Cannot subtract a date/time from a value"); + throw new value_error("Cannot subtract a boolean from a value"); + else if (value.type == DATETIME && type != DATETIME) + throw new value_error("Cannot subtract a date/time from a value"); switch (type) { 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: switch (value.type) { @@ -262,6 +263,32 @@ value_t& value_t::operator-=(const value_t& value) } 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: switch (value.type) { case INTEGER: @@ -352,9 +379,9 @@ value_t& value_t::operator-=(const value_t& value) value_t& value_t::operator*=(const value_t& value) { 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) - 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()) { *this = 0L; @@ -363,7 +390,7 @@ value_t& value_t::operator*=(const value_t& value) switch (type) { 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: 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) { 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) - 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) { 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: switch (value.type) { @@ -626,7 +653,7 @@ bool value_t::operator OP(const value_t& value) \ case DATETIME: \ switch (value.type) { \ 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: \ return (*((datetime_t *) data) OP \ @@ -637,13 +664,13 @@ bool value_t::operator OP(const value_t& value) \ *((datetime_t *) value.data)); \ \ 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: \ - 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: \ - 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: \ assert(0); \ @@ -654,14 +681,14 @@ bool value_t::operator OP(const value_t& value) \ case AMOUNT: \ switch (value.type) { \ 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: \ return (*((amount_t *) data) OP \ amount_t(*((long *) value.data))); \ \ 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: \ return *((amount_t *) data) OP *((amount_t *) value.data); \ @@ -685,13 +712,13 @@ bool value_t::operator OP(const value_t& value) \ case BALANCE: \ switch (value.type) { \ 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: \ return *((balance_t *) data) OP *((long *) value.data); \ \ 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: \ 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: \ switch (value.type) { \ 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: \ return (((balance_pair_t *) data)->quantity OP \ *((long *) value.data)); \ \ 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: \ return (((balance_pair_t *) data)->quantity OP \ @@ -757,7 +784,7 @@ value_t::operator long() const { switch (type) { 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: return *((long *) data); case DATETIME: @@ -765,9 +792,9 @@ value_t::operator long() const case AMOUNT: return *((amount_t *) data); 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: - throw value_error("Cannot convert a balance pair to an integer"); + throw new value_error("Cannot convert a balance pair to an integer"); default: assert(0); @@ -782,17 +809,17 @@ value_t::operator datetime_t() const { switch (type) { 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: return *((long *) data); case DATETIME: return *((datetime_t *) data); 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: - 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: - 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: assert(0); @@ -807,7 +834,7 @@ value_t::operator double() const { switch (type) { 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: return *((long *) data); case DATETIME: @@ -815,9 +842,9 @@ value_t::operator double() const case AMOUNT: return *((amount_t *) data); 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: - throw value_error("Cannot convert a balance pair to a double"); + throw new value_error("Cannot convert a balance pair to a double"); default: assert(0); @@ -835,15 +862,15 @@ void value_t::cast(type_t cast_type) case BOOLEAN: break; 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: - 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: - throw value_error("Cannot convert a boolean to an amount"); + throw new value_error("Cannot convert a boolean to an amount"); 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: - throw value_error("Cannot convert a boolean to a balance pair"); + throw new value_error("Cannot convert a boolean to a balance pair"); default: assert(0); @@ -888,11 +915,11 @@ void value_t::cast(type_t cast_type) case DATETIME: break; 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: - 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: - 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: assert(0); @@ -915,7 +942,7 @@ void value_t::cast(type_t cast_type) break; } 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: break; case BALANCE: { @@ -946,9 +973,9 @@ void value_t::cast(type_t cast_type) break; } 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: - 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: { balance_t * temp = (balance_t *) data; @@ -961,7 +988,7 @@ void value_t::cast(type_t cast_type) new((amount_t *)data) amount_t(); } else { - throw value_error("Cannot convert a balance with " + throw new value_error("Cannot convert a balance with " "multiple commodities to an amount"); } break; @@ -990,9 +1017,9 @@ void value_t::cast(type_t cast_type) break; } 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: - 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: { 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(); } 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"); } break; @@ -1042,7 +1069,7 @@ void value_t::negate() *((long *) data) = - *((long *) data); break; case DATETIME: - throw value_error("Cannot negate a date/time"); + throw new value_error("Cannot negate a date/time"); case AMOUNT: ((amount_t *) data)->negate(); break; @@ -1090,9 +1117,9 @@ value_t value_t::value(const std::time_t moment) const { switch (type) { 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: - 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: return *this; case AMOUNT: @@ -1108,9 +1135,9 @@ void value_t::round() { switch (type) { case BOOLEAN: - throw value_error("Cannot round a boolean"); + throw new value_error("Cannot round a boolean"); case DATETIME: - throw value_error("Cannot round a date/time"); + throw new value_error("Cannot round a date/time"); case INTEGER: break; 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 { switch (type) { 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: return *this; 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: return ((amount_t *) data)->price(); @@ -1156,7 +1206,7 @@ value_t value_t::date() const { switch (type) { 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: return 0L; case DATETIME: @@ -1185,11 +1235,11 @@ value_t value_t::strip_annotations(const bool keep_price, { switch (type) { 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: return *this; 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: return ((amount_t *) data)->strip_annotations @@ -1213,13 +1263,13 @@ value_t value_t::cost() const { switch (type) { 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 AMOUNT: case BALANCE: return *this; 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: 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) { 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: - 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 AMOUNT: if (cost) { @@ -1329,13 +1379,13 @@ amount_t value_getitem(value_t& value, int i) switch (value.type) { 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: return long(value); 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: return *((amount_t *) value.data); @@ -1555,7 +1605,6 @@ void export_value() .def("__len__", value_len) .def("__getitem__", value_getitem) - .def("abs", &value_t::abs) .def("cast", &value_t::cast) .def("cost", &value_t::cost) .def("price", &value_t::price) @@ -8,17 +8,6 @@ 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 // performance reasons. The alternative is to compute value // 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 value(const std::time_t moment) const; void round(); + value_t unround() const; }; #define DEF_VALUE_AUX_OP(OP) \ @@ -16,13 +16,13 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left, transaction_xdata_t& lxdata(transaction_xdata(*left)); 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; } transaction_xdata_t& rxdata(transaction_xdata(*right)); 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; } @@ -133,6 +133,8 @@ void sort_transactions::post_accumulated_xacts() void calc_transactions::operator()(transaction_t& xact) { + try { + transaction_xdata_t& xdata(transaction_xdata(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); 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) @@ -710,13 +719,13 @@ bool compare_items<account_t>::operator()(const account_t * left, account_xdata_t& lxdata(account_xdata(*left)); 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; } account_xdata_t& rxdata(account_xdata(*right)); 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; } @@ -794,13 +803,7 @@ void walk_accounts(account_t& account, { if (! sort_string.empty()) { value_auto_ptr sort_order; - try { - sort_order.reset(parse_value_expr(sort_string)); - } - catch (value_expr_error& err) { - throw error(std::string("In sort string '" + sort_string + "': " + - err.what())); - } + sort_order.reset(parse_value_expr(sort_string)); walk_accounts(account, handler, sort_order.get()); } else { walk_accounts(account, handler); @@ -23,10 +23,10 @@ struct item_handler { item_handler(item_handler * _handler) : handler(_handler) { DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler<T>"); } - virtual ~item_handler() { DEBUG_PRINT("ledger.memory.dtors", "dtor item_handler<T>"); } + virtual void flush() { if (handler) handler->flush(); @@ -56,8 +56,8 @@ bool compare_items<T>::operator()(const T * left, const T * right) value_t left_result; value_t right_result; - sort_order->compute(left_result, details_t(*left)); - sort_order->compute(right_result, details_t(*right)); + guarded_compute(sort_order, left_result, details_t(*left)); + guarded_compute(sort_order, right_result, details_t(*right)); return left_result < right_result; } @@ -222,14 +222,8 @@ class sort_transactions : public item_handler<transaction_t> sort_transactions(item_handler<transaction_t> * handler, const std::string& _sort_order) : item_handler<transaction_t>(handler) { - try { - sort_order = parse_value_expr(_sort_order); - sort_order->acquire(); - } - catch (value_expr_error& err) { - throw value_expr_error(std::string("In sort string '") + _sort_order + - "': " + err.what()); - } + assert(! _sort_order.empty()); + sort_order = parse_value_expr(_sort_order)->acquire(); } virtual ~sort_transactions() { @@ -223,14 +223,12 @@ unsigned int xml_parser_t::parse(std::istream& in, catch (const std::exception& err) { unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; XML_ParserFree(parser); - throw parse_error(original_file ? *original_file : "<xml>", line, - err.what()); + throw new parse_error(err.what()); } if (! have_error.empty()) { unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; - parse_error err(original_file ? *original_file : "<xml>", line, - have_error); + parse_error err(have_error); std::cerr << "Error: " << err.what() << std::endl; have_error = ""; } @@ -239,7 +237,7 @@ unsigned int xml_parser_t::parse(std::istream& in, unsigned long line = XML_GetCurrentLineNumber(parser) - offset++; const char * err = XML_ErrorString(XML_GetErrorCode(parser)); XML_ParserFree(parser); - throw parse_error(original_file ? *original_file : "<xml>", line, err); + throw new parse_error(err); } } |