Added much better error location.

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

View file

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

View file

@ -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);

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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;

View file

@ -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);
//////////////////////////////////////////////////////////////////////

View file

@ -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;

View file

@ -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

View file

@ -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

25
debug.h
View file

@ -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)

View file

@ -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();
}

208
error.h
View file

@ -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;
class error : public std::exception {
error_context(const std::string& _desc) throw() : desc(_desc) {}
virtual ~error_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << std::endl;
}
};
class file_context : public error_context
{
protected:
std::string file;
unsigned long line;
public:
file_context(const std::string& _file, unsigned long _line,
const std::string& desc = "") throw()
: file(_file), line(_line), error_context(desc) {}
virtual ~file_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << " ";
out << "\"" << file << "\", line " << line << ": ";
}
};
namespace ledger { class value_t; }
class value_context : public error_context
{
ledger::value_t * bal;
public:
value_context(const ledger::value_t& _bal,
const std::string& desc = "") throw();
virtual ~value_context() throw();
virtual void describe(std::ostream& out) const throw();
};
namespace ledger { class value_expr_t; }
class valexpr_context : public error_context {
public:
const ledger::value_expr_t * expr;
const ledger::value_expr_t * error_node;
valexpr_context(const ledger::value_expr_t * _expr,
const std::string& desc = "") throw();
virtual ~valexpr_context() throw();
virtual void describe(std::ostream& out) const throw();
};
class line_context : public error_context {
public:
std::string line;
long pos;
line_context(const std::string& _line, long _pos,
const std::string& desc = "") throw()
: line(_line), pos(_pos), error_context(desc) {}
virtual ~line_context() throw() {}
virtual void describe(std::ostream& out) const throw();
};
class include_context : public file_context {
public:
include_context(const std::string& file, unsigned long line,
const std::string& desc = "") throw()
: file_context(file, line, desc) {}
virtual ~include_context() throw() {}
virtual void describe(std::ostream& out) const throw() {
if (! desc.empty())
out << desc << ": ";
out << "\"" << file << "\", line " << line << ":" << std::endl;
}
};
namespace ledger { class entry_base_t; }
class entry_context : public error_context {
public:
const ledger::entry_base_t& entry;
entry_context(const ledger::entry_base_t& _entry,
const std::string& desc = "") throw()
: entry(_entry), error_context(desc) {}
virtual ~entry_context() throw() {}
virtual void describe(std::ostream& out) const throw();
};
namespace ledger { class transaction_t; }
class xact_context : public file_context {
public:
const ledger::transaction_t& xact;
xact_context(const ledger::transaction_t& _xact,
const std::string& desc = "") throw();
virtual ~xact_context() throw() {}
};
//////////////////////////////////////////////////////////////////////
class str_exception : public std::exception {
protected:
std::string reason;
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

View file

@ -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);
}

View file

@ -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,

View file

@ -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 = "";
}

View file

@ -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)

View file

@ -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:

View file

@ -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

63
main.cc
View file

@ -121,11 +121,16 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
else if (command == "parse") {
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;
}
}

View file

@ -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)

11
mask.h
View file

@ -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

View file

@ -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;
}
}
}
}

View file

@ -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,

View file

@ -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);

9
qif.cc
View file

@ -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':

View file

@ -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 + "\")");
}
}

View file

@ -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!");
}
}

24
test.py
View file

@ -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

View file

@ -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();

View file

@ -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)
{

164
valexpr.h
View file

@ -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,
scope_t * scope = NULL) {
std::istringstream stream(p);
return parse_boolean_expr(stream, scope);
}
inline value_expr_t * parse_boolean_expr(const std::string& str,
scope_t * scope = NULL) {
return parse_boolean_expr(str.c_str(), scope);
std::istringstream stream(str);
try {
return parse_boolean_expr(stream, scope);
}
catch (error * err) {
err->context.push_back
(new error_context("While parsing value expression: " + str));
throw err;
}
}
inline value_expr_t * parse_boolean_expr(const char * p,
scope_t * scope = NULL) {
return parse_boolean_expr(std::string(p), scope);
}
value_expr_t * parse_value_expr(std::istream& in,
scope_t * scope = NULL,
const bool partial = false);
inline value_expr_t * parse_value_expr(const char * p,
scope_t * scope = NULL,
const bool partial = false) {
std::istringstream stream(p);
return parse_value_expr(stream, scope, partial);
}
inline value_expr_t * parse_value_expr(const std::string& str,
scope_t * scope = NULL,
const bool partial = false) {
return parse_value_expr(str.c_str(), scope);
std::istringstream stream(str);
try {
return parse_value_expr(stream, scope, partial);
}
catch (error * err) {
err->context.push_back
(new line_context(str, (long)stream.tellg() - 1,
"While parsing value expression:"));
throw err;
}
}
inline value_expr_t * parse_value_expr(const char * p,
scope_t * scope = NULL,
const bool partial = false) {
return parse_value_expr(std::string(p), scope, partial);
}
void dump_value_expr(std::ostream& out, const value_expr_t * node,
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()) {

179
value.cc
View file

@ -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)

12
value.h
View file

@ -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) \

25
walk.cc
View file

@ -16,13 +16,13 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
transaction_xdata_t& lxdata(transaction_xdata(*left));
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);

16
walk.h
View file

@ -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() {

8
xml.cc
View file

@ -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);
}
}