summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--amount.cc49
-rw-r--r--amount.h10
-rw-r--r--balance.cc18
-rw-r--r--balance.h17
-rw-r--r--binary.cc32
-rw-r--r--config.cc63
-rw-r--r--datetime.cc2
-rw-r--r--datetime.h15
-rw-r--r--debug.cc15
-rw-r--r--debug.h25
-rw-r--r--derive.cc6
-rw-r--r--error.h208
-rw-r--r--format.cc35
-rw-r--r--format.h3
-rw-r--r--gnucash.cc4
-rw-r--r--journal.cc18
-rw-r--r--journal.h2
-rw-r--r--ledger.texi2
-rw-r--r--main.cc63
-rw-r--r--mask.cc3
-rw-r--r--mask.h11
-rw-r--r--option.cc43
-rw-r--r--option.h11
-rw-r--r--parser.cc8
-rw-r--r--qif.cc9
-rw-r--r--quotes.cc6
-rw-r--r--reconcile.cc8
-rwxr-xr-xtest.py24
-rw-r--r--textual.cc111
-rw-r--r--valexpr.cc329
-rw-r--r--valexpr.h154
-rw-r--r--value.cc179
-rw-r--r--value.h12
-rw-r--r--walk.cc25
-rw-r--r--walk.h16
-rw-r--r--xml.cc8
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 \
diff --git a/amount.cc b/amount.cc
index f3b6a801..67a0ec8c 100644
--- a/amount.cc
+++ b/amount.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);
diff --git a/amount.h b/amount.h
index 360da8f9..5598043b 100644
--- a/amount.h
+++ b/amount.h
@@ -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
diff --git a/balance.cc b/balance.cc
index a68f4ffe..45fad2a0 100644
--- a/balance.cc
+++ b/balance.cc
@@ -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)
diff --git a/balance.h b/balance.h
index f2d78233..71929f13 100644
--- a/balance.h
+++ b/balance.h
@@ -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) {
diff --git a/binary.cc b/binary.cc
index 8816078d..9f34af95 100644
--- a/binary.cc
+++ b/binary.cc
@@ -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;
diff --git a/config.cc b/config.cc
index 9016e9f3..dd5a1a62 100644
--- a/config.cc
+++ b/config.cc
@@ -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;
diff --git a/datetime.h b/datetime.h
index de4a39b9..5aed93d7 100644
--- a/datetime.h
+++ b/datetime.h
@@ -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
diff --git a/debug.cc b/debug.cc
index d9fda17c..b3b140bc 100644
--- a/debug.cc
+++ b/debug.cc
@@ -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
diff --git a/debug.h b/debug.h
index 2efbbe78..8fbff28f 100644
--- a/debug.h
+++ b/debug.h
@@ -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)
diff --git a/derive.cc b/derive.cc
index 20083796..837353cc 100644
--- a/derive.cc
+++ b/derive.cc
@@ -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();
}
diff --git a/error.h b/error.h
index 52af358a..d12a3399 100644
--- a/error.h
+++ b/error.h
@@ -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
diff --git a/format.cc b/format.cc
index 33613442..fea25dd3 100644
--- a/format.cc
+++ b/format.cc
@@ -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);
}
diff --git a/format.h b/format.h
index a4620aeb..e125bcb9 100644
--- a/format.h
+++ b/format.h
@@ -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,
diff --git a/gnucash.cc b/gnucash.cc
index 11e4ceec..48250fbf 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -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 = "";
}
diff --git a/journal.cc b/journal.cc
index 0f14d7d6..54d2a82a 100644
--- a/journal.cc
+++ b/journal.cc
@@ -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)
diff --git a/journal.h b/journal.h
index d50a78ea..6d0103dc 100644
--- a/journal.h
+++ b/journal.h
@@ -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
diff --git a/main.cc b/main.cc
index 62ca12df..caed1ab6 100644
--- a/main.cc
+++ b/main.cc
@@ -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;
}
}
diff --git a/mask.cc b/mask.cc
index 33dcf93b..972b24ce 100644
--- a/mask.cc
+++ b/mask.cc
@@ -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)
diff --git a/mask.h b/mask.h
index 7d38f24f..6d4790a3 100644
--- a/mask.h
+++ b/mask.h
@@ -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
diff --git a/option.cc b/option.cc
index 3eb78281..b16f4c6a 100644
--- a/option.cc
+++ b/option.cc
@@ -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;
+ }
+ }
}
}
diff --git a/option.h b/option.h
index 1acc20e9..75990d1b 100644
--- a/option.h
+++ b/option.h
@@ -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,
diff --git a/parser.cc b/parser.cc
index 6d8748b8..86cd9792 100644
--- a/parser.cc
+++ b/parser.cc
@@ -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);
diff --git a/qif.cc b/qif.cc
index cd368dc2..61512036 100644
--- a/qif.cc
+++ b/qif.cc
@@ -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':
diff --git a/quotes.cc b/quotes.cc
index 77a4d929..21612a0f 100644
--- a/quotes.cc
+++ b/quotes.cc
@@ -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!");
}
}
diff --git a/test.py b/test.py
index 389f9d33..49276de0 100755
--- a/test.py
+++ b/test.py
@@ -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
diff --git a/textual.cc b/textual.cc
index 78ff6bf3..35489a79 100644
--- a/textual.cc
+++ b/textual.cc
@@ -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();
diff --git a/valexpr.cc b/valexpr.cc
index 371f94eb..020be32e 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -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)
{
diff --git a/valexpr.h b/valexpr.h
index 7a029650..10e7fe5d 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -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()) {
diff --git a/value.cc b/value.cc
index 1ae88776..758a9c9b 100644
--- a/value.cc
+++ b/value.cc
@@ -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)
diff --git a/value.h b/value.h
index 22110b43..9417b8da 100644
--- a/value.h
+++ b/value.h
@@ -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) \
diff --git a/walk.cc b/walk.cc
index 4d5ff012..f608cb6e 100644
--- a/walk.cc
+++ b/walk.cc
@@ -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);
diff --git a/walk.h b/walk.h
index a9164ad0..69da7bd2 100644
--- a/walk.h
+++ b/walk.h
@@ -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() {
diff --git a/xml.cc b/xml.cc
index 916d4217..5b060296 100644
--- a/xml.cc
+++ b/xml.cc
@@ -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);
}
}