Added a DATETIME value type.

This commit is contained in:
John Wiegley 2006-03-06 03:53:36 +00:00
parent d02f74efea
commit b737cd8e6d
20 changed files with 994 additions and 392 deletions

View file

@ -133,4 +133,4 @@ all-clean: maintainer-clean
acconf.h.in aclocal.m4 autom4te config.guess config.sub \ acconf.h.in aclocal.m4 autom4te config.guess config.sub \
configure depcomp install-sh libtool ltconfig ltmain.sh \ configure depcomp install-sh libtool ltconfig ltmain.sh \
missing stamp texinfo.tex Makefile.in mkinstalldirs \ missing stamp texinfo.tex Makefile.in mkinstalldirs \
elisp-comp elc-stamp elisp-comp elc-stamp py-compile

53
NEWS
View file

@ -3,6 +3,59 @@
* 2.5 * 2.5
- Add a new --only predicate, which occurs during transaction
processing between --limit and --display. Here is a summary of how
the three supported predicates are used:
--limit "a>100"
This flag limits computation to *only transactions whose amount
is greater than 100 of a given commodity*. It means that if you
scan your dining expenses, for example, only individual bills
greater than $100 would be caculated by the report.
--only "a>100"
This flag happens much later than --limit, and corresponding
more directly to what one normally expects. If --limit isn't
used, then ALL your dining expenses contribute to the report,
*but only those calculated transactions whose value is greater
than $100 are used*. This becomes important when doing a
monthly costs report, for example, because it makes the
following command possible:
ledger -M --only "a>100" reg ^Expenses:Food
This shows only *months* whose amount is greater than 100. If
--limit had been used, it would have been a monthly summary of
all individual dinner bills greater than 100 -- which is a very
different thing.
--display "a>100"
This predicate does not constrain calculation, but only display.
Consider the same command as above:
ledger -M --display "a>100" reg ^Expenses:Food
This displays only lines whose amount is greater than 100, *yet
the running total still includes amounts from all transactions*.
This command has more particular application, such as showing
the current month's checking register while still giving a
correct ending balance:
ledger --display "d>[this month]" reg Checking
Note that these predicates can be combined. Here is a report that
considers only food bills whose individual cost is greater than
$20, but shows the monthly total only if it is greater than $500.
Finally, we only display the months of the last year, but we
retain an accurate running total with respect to the entire ledger
file:
ledger -M --limit "a>20" --only "a>200" \
--display "year == yearof([last year])" reg ^Expenses:Food
- There have a few changes to value expression syntax. The most - There have a few changes to value expression syntax. The most
significant incompatibilities being: significant incompatibilities being:

View file

@ -10,6 +10,12 @@
namespace ledger { namespace ledger {
bool do_cleanup = true;
bool amount_t::keep_price = false;
bool amount_t::keep_date = false;
bool amount_t::keep_tag = false;
#define BIGINT_BULK_ALLOC 0x0001 #define BIGINT_BULK_ALLOC 0x0001
#define BIGINT_KEEP_PREC 0x0002 #define BIGINT_KEEP_PREC 0x0002
@ -32,10 +38,7 @@ class amount_t::bigint_t {
ref(1), index(0) { ref(1), index(0) {
mpz_init_set(val, other.val); mpz_init_set(val, other.val);
} }
~bigint_t() { ~bigint_t();
assert(ref == 0);
mpz_clear(val);
}
}; };
unsigned int sizeof_bigint_t() { unsigned int sizeof_bigint_t() {
@ -44,10 +47,16 @@ unsigned int sizeof_bigint_t() {
#define MPZ(x) ((x)->val) #define MPZ(x) ((x)->val)
static mpz_t temp; static mpz_t temp; // these are the global temp variables
static mpz_t divisor; static mpz_t divisor;
static amount_t::bigint_t true_value; static amount_t::bigint_t true_value;
inline amount_t::bigint_t::~bigint_t() {
assert(ref == 0 || (! do_cleanup && this == &true_value));
mpz_clear(val);
}
base_commodities_map commodity_base_t::commodities; base_commodities_map commodity_base_t::commodities;
commodity_base_t::updater_t * commodity_base_t::updater = NULL; commodity_base_t::updater_t * commodity_base_t::updater = NULL;
@ -93,6 +102,9 @@ static struct _init_amounts {
} }
~_init_amounts() { ~_init_amounts() {
if (! do_cleanup)
return;
mpz_clear(temp); mpz_clear(temp);
mpz_clear(divisor); mpz_clear(divisor);
@ -1355,19 +1367,19 @@ void amount_t::annotate_commodity(const amount_t& price,
DEBUG_PRINT("amounts.commodities", " Annotated amount is " << *this); DEBUG_PRINT("amounts.commodities", " Annotated amount is " << *this);
} }
amount_t amount_t::reduce_commodity(const bool keep_price, amount_t amount_t::strip_annotations(const bool _keep_price,
const bool keep_date, const bool _keep_date,
const bool keep_tag) const const bool _keep_tag) const
{ {
if (! commodity().annotated || if (! commodity().annotated ||
(keep_price && keep_date && keep_tag)) (_keep_price && _keep_date && _keep_tag))
return *this; return *this;
DEBUG_PRINT("amounts.commodities", "Reducing commodity for amount " DEBUG_PRINT("amounts.commodities", "Reducing commodity for amount "
<< *this << std::endl << *this << std::endl
<< " keep price " << keep_price << " " << " keep price " << _keep_price << " "
<< " keep date " << keep_date << " " << " keep date " << _keep_date << " "
<< " keep tag " << keep_tag); << " keep tag " << _keep_tag);
annotated_commodity_t& annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&>(commodity())); ann_comm(static_cast<annotated_commodity_t&>(commodity()));
@ -1375,13 +1387,13 @@ amount_t amount_t::reduce_commodity(const bool keep_price,
commodity_t * new_comm; commodity_t * new_comm;
if ((keep_price && ann_comm.price) || if ((_keep_price && ann_comm.price) ||
(keep_date && ann_comm.date) || (_keep_date && ann_comm.date) ||
(keep_tag && ! ann_comm.tag.empty())) (_keep_tag && ! ann_comm.tag.empty()))
{ {
new_comm = annotated_commodity_t::find_or_create new_comm = annotated_commodity_t::find_or_create
(*ann_comm.base, keep_price ? ann_comm.price : amount_t(), (*ann_comm.base, _keep_price ? ann_comm.price : amount_t(),
keep_date ? ann_comm.date : 0, keep_tag ? ann_comm.tag : ""); _keep_date ? ann_comm.date : 0, _keep_tag ? ann_comm.tag : "");
} else { } else {
new_comm = commodity_t::find_or_create(ann_comm.base_symbol()); new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
} }
@ -1543,8 +1555,6 @@ amount_t commodity_base_t::value(const std::time_t moment)
return price; return price;
} }
std::string annotated_commodity_t::date_format = "%Y/%m/%d";
void void
annotated_commodity_t::write_annotations(std::ostream& out, annotated_commodity_t::write_annotations(std::ostream& out,
const amount_t& price, const amount_t& price,
@ -1554,12 +1564,8 @@ annotated_commodity_t::write_annotations(std::ostream& out,
if (price) if (price)
out << " {" << price << '}'; out << " {" << price << '}';
if (date) { if (date)
char buf[128]; out << " [" << datetime_t(date) << ']';
std::strftime(buf, 127, annotated_commodity_t::date_format.c_str(),
std::localtime(&date));
out << " [" << buf << ']';
}
if (! tag.empty()) if (! tag.empty())
out << " (" << tag << ')'; out << " (" << tag << ')';

View file

@ -15,6 +15,8 @@
namespace ledger { namespace ledger {
extern bool do_cleanup;
class commodity_t; class commodity_t;
class amount_t class amount_t
@ -22,6 +24,10 @@ class amount_t
public: public:
class bigint_t; class bigint_t;
static bool keep_price;
static bool keep_date;
static bool keep_tag;
protected: protected:
void _init(); void _init();
void _copy(const amount_t& amt); void _copy(const amount_t& amt);
@ -76,9 +82,9 @@ class amount_t
void annotate_commodity(const amount_t& price, void annotate_commodity(const amount_t& price,
const std::time_t date = 0, const std::time_t date = 0,
const std::string& tag = ""); const std::string& tag = "");
amount_t reduce_commodity(const bool keep_price = false, amount_t strip_annotations(const bool _keep_price = keep_price,
const bool keep_date = false, const bool _keep_date = keep_date,
const bool keep_tag = false) const; const bool _keep_tag = keep_tag) const;
void clear_commodity() { void clear_commodity() {
commodity_ = NULL; commodity_ = NULL;
} }
@ -543,8 +549,6 @@ class annotated_commodity_t : public commodity_t
std::time_t date; std::time_t date;
std::string tag; std::string tag;
static std::string date_format;
static void write_annotations(std::ostream& out, static void write_annotations(std::ostream& out,
const amount_t& price, const amount_t& price,
const std::time_t date, const std::time_t date,

View file

@ -13,6 +13,17 @@ amount_t balance_t::amount(const commodity_t& commodity) const
amounts_map::const_iterator i = amounts.begin(); amounts_map::const_iterator i = amounts.begin();
return (*i).second; return (*i).second;
} }
else if (amounts.size() > 1) {
// Try stripping annotations before giving an error.
balance_t temp(strip_annotations());
if (temp.amounts.size() == 1)
return temp.amount(commodity);
std::ostringstream errmsg;
errmsg << "Requested amount of a balance with multiple commodities: "
<< *this;
throw amount_error(errmsg.str());
}
} }
else if (amounts.size() > 0) { else if (amounts.size() > 0) {
amounts_map::const_iterator i = amounts.find(&commodity); amounts_map::const_iterator i = amounts.find(&commodity);
@ -63,15 +74,16 @@ std::time_t balance_t::date() const
return temp; return temp;
} }
balance_t balance_t::reduce(const bool keep_price, const bool keep_date, balance_t balance_t::strip_annotations(const bool keep_price,
const bool keep_tag) const const bool keep_date,
const bool keep_tag) const
{ {
balance_t temp; balance_t temp;
for (amounts_map::const_iterator i = amounts.begin(); for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end(); i != amounts.end();
i++) i++)
temp += (*i).second.reduce_commodity(keep_price, keep_date, keep_tag); temp += (*i).second.strip_annotations(keep_price, keep_date, keep_tag);
return temp; return temp;
} }
@ -176,44 +188,150 @@ void balance_t::write(std::ostream& out,
balance_t& balance_t::operator*=(const balance_t& bal) balance_t& balance_t::operator*=(const balance_t& bal)
{ {
if (! *this || ! bal) { if (realzero() || bal.realzero()) {
return (*this = 0L); return *this = 0L;
} }
else if (amounts.size() == 1 && bal.amounts.size() == 1) { else if (bal.amounts.size() == 1) {
return *this *= (*bal.amounts.begin()).second; return *this *= (*bal.amounts.begin()).second;
} }
else if (amounts.size() == 1) {
return *this = bal * *this;
}
else { else {
// Since we would fail with an error at this point otherwise, try
// stripping annotations to see if we can come up with a
// reasonable result. The user will not notice any annotations
// missing (since they are viewing a stripped report anyway), only
// that some of their value expression may not see any pricing or
// date data because of this operation.
balance_t temp(bal.strip_annotations());
if (temp.amounts.size() == 1)
return *this *= temp;
temp = strip_annotations();
if (temp.amounts.size() == 1)
return *this = bal * temp;
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "It makes no sense to multiply two balances: " errmsg << "Cannot multiply two balances: " << *this << " * " << bal;
<< *this << " * " << bal;
throw amount_error(errmsg.str()); throw amount_error(errmsg.str());
} }
} }
balance_t& balance_t::operator*=(const amount_t& amt)
{
if (realzero() || amt.realzero()) {
return *this = 0L;
}
else if (! amt.commodity()) {
// Multiplying by the null commodity causes all amounts to be
// increased by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second *= amt;
}
else if (amounts.size() == 1 &&
(*amounts.begin()).first == &amt.commodity()) {
(*amounts.begin()).second *= amt;
}
else {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end()) {
(*i).second *= amt;
} else {
// Try stripping annotations before giving an error.
balance_t temp(strip_annotations());
if (temp.amounts.size() == 1 &&
(*temp.amounts.begin()).first == &amt.commodity()) {
return *this = temp * amt;
} else {
i = temp.amounts.find(&amt.commodity());
if (i != temp.amounts.end())
return *this = temp * amt;
}
std::ostringstream errmsg;
errmsg << "Attempt to multiply balance by a commodity"
<< " not found in that balance: "
<< *this << " * " << amt;
throw amount_error(errmsg.str());
}
}
return *this;
}
balance_t& balance_t::operator/=(const balance_t& bal) balance_t& balance_t::operator/=(const balance_t& bal)
{ {
if (! *this) { if (bal.realzero()) {
return (*this = 0L);
}
else if (! bal) {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Attempt to divide by zero: " << *this << " / " << bal; errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
throw amount_error(errmsg.str()); throw amount_error(errmsg.str());
} }
else if (amounts.size() == 1 && bal.amounts.size() == 1) { else if (realzero()) {
return *this = 0L;
}
else if (bal.amounts.size() == 1) {
return *this /= (*bal.amounts.begin()).second; return *this /= (*bal.amounts.begin()).second;
} }
else if (*this == bal) { else if (*this == bal) {
return (*this = 1L); return *this = 1L;
} }
else { else {
// Try stripping annotations before giving an error.
balance_t temp(bal.strip_annotations());
if (temp.amounts.size() == 1)
return *this /= temp;
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "It makes no sense to divide two balances: " errmsg << "Cannot divide between two balances: " << *this << " / " << bal;
<< *this << " / " << bal;
throw amount_error(errmsg.str()); throw amount_error(errmsg.str());
} }
} }
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());
}
else if (realzero()) {
return *this = 0L;
}
else if (! amt.commodity()) {
// Dividing by the null commodity causes all amounts to be
// decreased by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second /= amt;
}
else if (amounts.size() == 1 &&
(*amounts.begin()).first == &amt.commodity()) {
(*amounts.begin()).second /= amt;
}
else {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end()) {
(*i).second /= amt;
} else {
// Try stripping annotations before giving an error.
balance_t temp(strip_annotations());
if (temp.amounts.size() == 1 &&
(*temp.amounts.begin()).first == &amt.commodity())
return *this = temp / amt;
std::ostringstream errmsg;
errmsg << "Attempt to divide balance by a commodity"
<< " not found in that balance: "
<< *this << " * " << amt;
throw amount_error(errmsg.str());
}
}
return *this;
}
balance_t::operator amount_t() const balance_t::operator amount_t() const
{ {
if (amounts.size() == 1) { if (amounts.size() == 1) {
@ -223,6 +341,11 @@ balance_t::operator amount_t() const
return amount_t(); return amount_t();
} }
else { else {
// Try stripping annotations before giving an error.
balance_t temp(strip_annotations());
if (temp.amounts.size() == 1)
return (*temp.amounts.begin()).second;
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "Cannot convert a balance with " errmsg << "Cannot convert a balance with "
<< "multiple commodities to an amount: " << *this; << "multiple commodities to an amount: " << *this;
@ -230,27 +353,6 @@ balance_t::operator amount_t() const
} }
} }
balance_pair_t& balance_pair_t::operator/=(const balance_pair_t& bal_pair)
{
if (bal_pair.cost && ! cost)
cost = new balance_t(quantity);
quantity /= bal_pair.quantity;
if (cost)
*cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
return *this;
}
balance_pair_t& balance_pair_t::add(const amount_t& amount,
const amount_t * a_cost)
{
if (a_cost && ! cost)
cost = new balance_t(quantity);
quantity += amount;
if (cost)
*cost += a_cost ? *a_cost : amount;
return *this;
}
} // namespace ledger } // namespace ledger
#ifdef USE_BOOST_PYTHON #ifdef USE_BOOST_PYTHON
@ -353,13 +455,19 @@ void export_balance()
.def("__len__", balance_len) .def("__len__", balance_len)
.def("__getitem__", balance_getitem) .def("__getitem__", balance_getitem)
.def("negate", &balance_t::negate) .def("valid", &balance_t::valid)
.def("realzero", &balance_t::realzero)
.def("amount", &balance_t::amount) .def("amount", &balance_t::amount)
.def("value", &balance_t::value) .def("value", &balance_t::value)
.def("price", &balance_t::price) .def("price", &balance_t::price)
.def("reduce", &balance_t::reduce) .def("date", &balance_t::date)
.def("strip_annotations", &balance_t::strip_annotations)
.def("write", &balance_t::write) .def("write", &balance_t::write)
.def("valid", &balance_t::valid) .def("abs", &balance_t::abs)
.def("round", &balance_t::round)
.def("negate", &balance_t::negate)
.def("negated", &balance_t::negated)
; ;
class_< balance_pair_t > ("BalancePair") class_< balance_pair_t > ("BalancePair")
@ -436,16 +544,23 @@ void export_balance()
.def("__len__", balance_pair_len) .def("__len__", balance_pair_len)
.def("__getitem__", balance_pair_getitem) .def("__getitem__", balance_pair_getitem)
.def("valid", &balance_pair_t::valid)
.def("realzero", &balance_pair_t::realzero)
.def("amount", &balance_pair_t::amount)
.def("value", &balance_pair_t::value)
.def("price", &balance_pair_t::price)
.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)
.add_property("cost", .add_property("cost",
make_getter(&balance_pair_t::cost, make_getter(&balance_pair_t::cost,
return_value_policy<reference_existing_object>())) return_value_policy<reference_existing_object>()))
.def("negate", &balance_pair_t::negate)
.def("amount", &balance_pair_t::amount)
.def("value", &balance_pair_t::value)
.def("write", &balance_pair_t::write)
.def("valid", &balance_pair_t::valid)
; ;
} }

139
balance.h
View file

@ -2,6 +2,7 @@
#define _BALANCE_H #define _BALANCE_H
#include "amount.h" #include "amount.h"
#include "datetime.h"
#include <map> #include <map>
#include <ctime> #include <ctime>
@ -35,13 +36,13 @@ class balance_t
*this += (*i).second; *this += (*i).second;
} }
balance_t(const amount_t& amt) { balance_t(const amount_t& amt) {
if (amt) if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt)); amounts.insert(amounts_pair(&amt.commodity(), amt));
} }
template <typename T> template <typename T>
balance_t(T value) { balance_t(T value) {
amount_t amt(value); amount_t amt(value);
if (amt) if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt)); amounts.insert(amounts_pair(&amt.commodity(), amt));
} }
@ -80,7 +81,7 @@ class balance_t
amounts_map::iterator i = amounts.find(&amt.commodity()); amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end()) if (i != amounts.end())
(*i).second += amt; (*i).second += amt;
else if (amt) else if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt)); amounts.insert(amounts_pair(&amt.commodity(), amt));
return *this; return *this;
} }
@ -148,53 +149,14 @@ class balance_t
// multiplication and divide // multiplication and divide
balance_t& operator*=(const balance_t& bal); balance_t& operator*=(const balance_t& bal);
balance_t& operator*=(const amount_t& amt) { balance_t& operator*=(const amount_t& amt);
// Multiplying by the null commodity causes all amounts to be
// increased by the same factor.
if (amt.realzero()) {
amounts.clear();
}
else if (! amt.commodity()) {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second *= amt;
}
else if (amounts.size() == 1) {
(*amounts.begin()).second *= amt;
}
else {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
(*i).second *= amt;
}
return *this;
}
template <typename T> template <typename T>
balance_t& operator*=(T val) { balance_t& operator*=(T val) {
return *this *= amount_t(val); return *this *= amount_t(val);
} }
balance_t& operator/=(const balance_t& bal); balance_t& operator/=(const balance_t& bal);
balance_t& operator/=(const amount_t& amt) { balance_t& operator/=(const amount_t& amt);
// Dividing by the null commodity causes all amounts to be
// increased by the same factor.
if (! amt.commodity()) {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second /= amt;
}
else if (amounts.size() == 1) {
(*amounts.begin()).second /= amt;
}
else {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
(*i).second /= amt;
}
return *this;
}
template <typename T> template <typename T>
balance_t& operator/=(T val) { balance_t& operator/=(T val) {
return *this /= amount_t(val); return *this /= amount_t(val);
@ -415,13 +377,27 @@ class balance_t
return false; return false;
} }
amount_t amount(const commodity_t& commodity) const; bool realzero() const {
balance_t value(const std::time_t moment) const; if (amounts.size() == 0)
balance_t price() const; return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.realzero())
return false;
return true;
}
amount_t amount(const commodity_t& commodity =
*commodity_t::null_commodity) const;
balance_t value(const std::time_t moment = now) const;
balance_t price() const;
std::time_t date() const; std::time_t date() const;
balance_t reduce(const bool keep_price = false,
const bool keep_date = false, balance_t
const bool keep_tag = false) const; strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const;
void write(std::ostream& out, const int first_width, void write(std::ostream& out, const int first_width,
const int latter_width = -1) const; const int latter_width = -1) const;
@ -440,17 +416,6 @@ class balance_t
if ((*i).second.commodity()) if ((*i).second.commodity())
(*i).second = (*i).second.round(); (*i).second = (*i).second.round();
} }
bool realzero() const {
if (amounts.size() == 0)
return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.realzero())
return false;
return true;
}
}; };
inline balance_t abs(const balance_t& bal) { inline balance_t abs(const balance_t& bal) {
@ -496,7 +461,6 @@ class balance_pair_t
delete cost; delete cost;
cost = NULL; cost = NULL;
} }
quantity = bal_pair.quantity; quantity = bal_pair.quantity;
if (bal_pair.cost) if (bal_pair.cost)
cost = new balance_t(*bal_pair.cost); cost = new balance_t(*bal_pair.cost);
@ -651,7 +615,14 @@ class balance_pair_t
return *this *= amount_t(val); return *this *= amount_t(val);
} }
balance_pair_t& operator/=(const balance_pair_t& bal_pair); balance_pair_t& operator/=(const balance_pair_t& bal_pair) {
if (bal_pair.cost && ! cost)
cost = new balance_t(quantity);
quantity /= bal_pair.quantity;
if (cost)
*cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
return *this;
}
balance_pair_t& operator/=(const balance_t& bal) { balance_pair_t& operator/=(const balance_t& bal) {
quantity /= bal; quantity /= bal;
if (cost) if (cost)
@ -813,34 +784,60 @@ class balance_pair_t
} }
// test for non-zero (use ! for zero) // test for non-zero (use ! for zero)
operator bool() const {
return quantity;
}
operator balance_t() const { operator balance_t() const {
return quantity; return quantity;
} }
operator amount_t() const { operator amount_t() const {
return quantity; return quantity;
} }
operator bool() const {
return quantity;
}
bool realzero() const {
return ((! cost || cost->realzero()) && quantity.realzero());
}
void abs() { void abs() {
quantity.abs(); quantity.abs();
if (cost) cost->abs(); if (cost) cost->abs();
} }
amount_t amount(const commodity_t& commodity) const { amount_t amount(const commodity_t& commodity =
*commodity_t::null_commodity) const {
return quantity.amount(commodity); return quantity.amount(commodity);
} }
balance_t value(const std::time_t moment) const { balance_t value(const std::time_t moment = now) const {
return quantity.value(moment); return quantity.value(moment);
} }
balance_t price() const {
return quantity.price();
}
std::time_t date() const {
return quantity.date();
}
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const {
return quantity.strip_annotations(keep_price, keep_date, keep_tag);
}
void write(std::ostream& out, const int first_width, void write(std::ostream& out, const int first_width,
const int latter_width = -1) const { const int latter_width = -1) const {
quantity.write(out, first_width, latter_width); quantity.write(out, first_width, latter_width);
} }
balance_pair_t& add(const amount_t& amount, balance_pair_t& add(const amount_t& amount,
const amount_t * a_cost = NULL); const amount_t * a_cost = NULL) {
if (a_cost && ! cost)
cost = new balance_t(quantity);
quantity += amount;
if (cost)
*cost += a_cost ? *a_cost : amount;
return *this;
}
bool valid() { bool valid() {
return quantity.valid() && (! cost || cost->valid()); return quantity.valid() && (! cost || cost->valid());
@ -850,10 +847,6 @@ class balance_pair_t
quantity.round(); quantity.round();
if (cost) cost->round(); if (cost) cost->round();
} }
bool realzero() const {
return ((! cost || cost->realzero()) && quantity.realzero());
}
}; };
inline balance_pair_t abs(const balance_pair_t& bal_pair) { inline balance_pair_t abs(const balance_pair_t& bal_pair) {

View file

@ -71,8 +71,9 @@ void config_t::reset()
prices_format = "%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"; prices_format = "%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n";
pricesdb_format = "P %[%Y/%m/%d %H:%M:%S] %A %t\n"; pricesdb_format = "P %[%Y/%m/%d %H:%M:%S] %A %t\n";
predicate = ""; predicate = "";
display_predicate = ""; secondary_predicate = "";
display_predicate = "";
head_entries = 0; head_entries = 0;
tail_entries = 0; tail_entries = 0;
@ -91,6 +92,8 @@ void config_t::reset()
show_revalued_only = false; show_revalued_only = false;
download_quotes = false; download_quotes = false;
debug_mode = false; debug_mode = false;
verbose_mode = false;
trace_mode = false;
keep_price = false; keep_price = false;
keep_date = false; keep_date = false;
keep_tag = false; keep_tag = false;
@ -301,14 +304,12 @@ void config_t::process_options(const std::string& command,
// Now setup the various formatting strings // Now setup the various formatting strings
if (! date_format.empty()) { if (! date_format.empty())
format_t::date_format = date_format; datetime_t::date_format = date_format;
annotated_commodity_t::date_format = date_format;
}
format_t::keep_price = keep_price; amount_t::keep_price = keep_price;
format_t::keep_date = keep_date; amount_t::keep_date = keep_date;
format_t::keep_tag = keep_tag; amount_t::keep_tag = keep_tag;
} }
item_handler<transaction_t> * item_handler<transaction_t> *
@ -348,15 +349,21 @@ config_t::chain_xact_handlers(const std::string& command,
// transactions which can be reconciled to a given balance // transactions which can be reconciled to a given balance
// (calculated against the transactions which it receives). // (calculated against the transactions which it receives).
if (! reconcile_balance.empty()) { if (! reconcile_balance.empty()) {
value_t target_balance(reconcile_balance); std::time_t cutoff = now;
time_t cutoff = now;
if (! reconcile_date.empty()) if (! reconcile_date.empty())
parse_date(reconcile_date.c_str(), &cutoff); parse_date(reconcile_date.c_str(), &cutoff);
ptrs.push_back(formatter = ptrs.push_back(formatter =
new reconcile_transactions(formatter, target_balance, new reconcile_transactions
cutoff)); (formatter, value_t(reconcile_balance), cutoff));
} }
// filter_transactions will only pass through transactions
// matching the `secondary_predicate'.
if (! secondary_predicate.empty())
ptrs.push_back(formatter =
new filter_transactions(formatter,
secondary_predicate));
// sort_transactions will sort all the transactions it sees, based // sort_transactions will sort all the transactions it sees, based
// on the `sort_order' value expression. // on the `sort_order' value expression.
if (! sort_string.empty()) if (! sort_string.empty())
@ -718,6 +725,14 @@ OPT_BEGIN(debug, ":") {
::setenv("DEBUG_CLASS", optarg, 1); ::setenv("DEBUG_CLASS", optarg, 1);
} OPT_END(debug); } OPT_END(debug);
OPT_BEGIN(verbose, "") {
config->verbose_mode = true;
} OPT_END(verbose);
OPT_BEGIN(trace, "") {
config->trace_mode = true;
} OPT_END(trace);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// Report filtering // Report filtering
@ -1016,6 +1031,14 @@ OPT_BEGIN(limit, "l:") {
config->predicate += ")"; config->predicate += ")";
} OPT_END(limit); } OPT_END(limit);
OPT_BEGIN(only, ":") {
if (! config->secondary_predicate.empty())
config->secondary_predicate += "&";
config->secondary_predicate += "(";
config->secondary_predicate += optarg;
config->secondary_predicate += ")";
} OPT_END(only);
OPT_BEGIN(display, "d:") { OPT_BEGIN(display, "d:") {
if (! config->display_predicate.empty()) if (! config->display_predicate.empty())
config->display_predicate += "&"; config->display_predicate += "&";
@ -1153,6 +1176,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "market", 'V', false, opt_market, false }, { "market", 'V', false, opt_market, false },
{ "monthly", 'M', false, opt_monthly, false }, { "monthly", 'M', false, opt_monthly, false },
{ "no-cache", '\0', false, opt_no_cache, false }, { "no-cache", '\0', false, opt_no_cache, false },
{ "only", '\0', true, opt_only, false },
{ "output", 'o', true, opt_output, false }, { "output", 'o', true, opt_output, false },
{ "pager", '\0', true, opt_pager, false }, { "pager", '\0', true, opt_pager, false },
{ "percentage", '%', false, opt_percentage, false }, { "percentage", '%', false, opt_percentage, false },
@ -1178,8 +1202,10 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "total", 'T', true, opt_total, false }, { "total", 'T', true, opt_total, false },
{ "total-data", 'J', false, opt_total_data, false }, { "total-data", 'J', false, opt_total_data, false },
{ "totals", '\0', false, opt_totals, false }, { "totals", '\0', false, opt_totals, false },
{ "trace", '\0', false, opt_trace, false },
{ "unbudgeted", '\0', false, opt_unbudgeted, false }, { "unbudgeted", '\0', false, opt_unbudgeted, false },
{ "uncleared", 'U', false, opt_uncleared, false }, { "uncleared", 'U', false, opt_uncleared, false },
{ "verbose", '\0', false, opt_verbose, false },
{ "version", 'v', false, opt_version, false }, { "version", 'v', false, opt_version, false },
{ "weekly", 'W', false, opt_weekly, false }, { "weekly", 'W', false, opt_weekly, false },
{ "wide", 'w', false, opt_wide, false }, { "wide", 'w', false, opt_wide, false },
@ -1189,4 +1215,31 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "yearly", 'Y', false, opt_yearly, false }, { "yearly", 'Y', false, opt_yearly, false },
}; };
//////////////////////////////////////////////////////////////////////
void trace(const std::string& cat, const std::string& str)
{
char buf[32];
std::time_t now = std::time(NULL);
std::strftime(buf, 31, "%H:%M:%S", std::localtime(&now));
std::cerr << buf << " " << cat << ": " << str << std::endl;
}
void trace_push(const std::string& cat, const std::string& str,
timing_t& timer)
{
timer.start();
trace(cat, str);
}
void trace_pop(const std::string& cat, const std::string& str,
timing_t& timer)
{
timer.stop();
std::ostringstream out;
out << str << ": " << (double(timer.cumulative) / double(CLOCKS_PER_SEC)) << "s";
trace(cat, out.str());
}
} // namespace ledger } // namespace ledger

View file

@ -2,6 +2,7 @@
#define _CONFIG_H #define _CONFIG_H
#include "ledger.h" #include "ledger.h"
#include "timing.h"
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@ -22,6 +23,7 @@ class config_t
std::string output_file; std::string output_file;
std::string account; std::string account;
std::string predicate; std::string predicate;
std::string secondary_predicate;
std::string display_predicate; std::string display_predicate;
std::string report_period; std::string report_period;
std::string report_period_sort; std::string report_period_sort;
@ -66,6 +68,8 @@ class config_t
bool use_cache; bool use_cache;
bool cache_dirty; bool cache_dirty;
bool debug_mode; bool debug_mode;
bool verbose_mode;
bool trace_mode;
bool keep_price; bool keep_price;
bool keep_date; bool keep_date;
bool keep_tag; bool keep_tag;
@ -102,7 +106,7 @@ class config_t
std::list<item_handler<transaction_t> *>& ptrs); std::list<item_handler<transaction_t> *>& ptrs);
}; };
#define CONFIG_OPTIONS_SIZE 81 #define CONFIG_OPTIONS_SIZE 84
extern option_t config_options[CONFIG_OPTIONS_SIZE]; extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out); void option_help(std::ostream& out);
@ -112,6 +116,29 @@ void option_help(std::ostream& out);
#define OPT_END(tag) #define OPT_END(tag)
//////////////////////////////////////////////////////////////////////
void trace(const std::string& cat, const std::string& str);
void trace_push(const std::string& cat, const std::string& str,
timing_t& timer);
void trace_pop(const std::string& cat, const std::string& str,
timing_t& timer);
#define TRACE(cat, msg) if (config.trace_mode) trace(#cat, msg)
#define TRACE_(cat, msg) if (trace_mode) trace(#cat, msg)
#define TRACE_PUSH(cat, msg) \
timing_t timer_ ## cat(#cat); \
if (config.trace_mode) trace_push(#cat, msg, timer_ ## cat)
#define TRACE_PUSH_(cat, msg) \
timing_t timer_ ## cat(#cat); \
if (trace_mode) trace_push(#cat, msg, timer_ ## cat)
#define TRACE_POP(cat, msg) \
if (config.trace_mode) trace_pop(#cat, msg, timer_ ## cat)
#define TRACE_POP_(cat, msg) \
if (trace_mode) trace_pop(#cat, msg, timer_ ## cat)
} // namespace ledger } // namespace ledger
#endif // _CONFIG_H #endif // _CONFIG_H

View file

@ -34,6 +34,8 @@ const char * formats[] = {
NULL NULL
}; };
std::string datetime_t::date_format = "%Y/%m/%d";
std::time_t interval_t::first(const std::time_t moment) const std::time_t interval_t::first(const std::time_t moment) const
{ {
std::time_t quant = begin; std::time_t quant = begin;

View file

@ -4,6 +4,71 @@
#include <ctime> #include <ctime>
#include <sstream> #include <sstream>
struct interval_t;
struct datetime_t
{
std::time_t when;
static std::string date_format;
datetime_t(const std::time_t _when) : when(_when) {}
datetime_t& operator+=(const long secs) {
when += secs;
return *this;
}
datetime_t& operator-=(const long secs) {
when -= secs;
return *this;
}
datetime_t& operator=(const interval_t& period);
datetime_t& operator+=(const interval_t& period);
#define DEF_DATETIME_OP(OP) \
bool operator OP(const datetime_t& other) { \
return when OP other.when; \
}
DEF_DATETIME_OP(<)
DEF_DATETIME_OP(<=)
DEF_DATETIME_OP(>)
DEF_DATETIME_OP(>=)
DEF_DATETIME_OP(==)
DEF_DATETIME_OP(!=)
operator bool() const {
return when != 0;
}
operator long() const {
return (long)when;
}
operator double() const {
return (double)when;
}
int year() const {
struct std::tm * desc = std::localtime(&when);
return desc->tm_year + 1900;
}
int month() const {
struct std::tm * desc = std::localtime(&when);
return desc->tm_mon + 1;
}
int day() const {
struct std::tm * desc = std::localtime(&when);
return desc->tm_mday;
}
};
inline std::ostream& operator<<(std::ostream& out, const datetime_t& moment) {
char buf[32];
std::strftime(buf, 31, datetime_t::date_format.c_str(),
std::localtime(&moment.when));
out << buf;
}
struct interval_t struct interval_t
{ {
unsigned int years; unsigned int years;
@ -36,6 +101,15 @@ struct interval_t
void parse(std::istream& in); void parse(std::istream& in);
}; };
inline datetime_t& datetime_t::operator=(const interval_t& period) {
when = period.first();
return *this;
}
inline datetime_t& datetime_t::operator+=(const interval_t& period) {
when = period.increment(when);
return *this;
}
extern std::time_t now; extern std::time_t now;
extern int now_year; extern int now_year;
extern char input_format[128]; extern char input_format[128];

View file

@ -7,10 +7,6 @@
namespace ledger { namespace ledger {
bool format_t::keep_price = false;
bool format_t::keep_date = false;
bool format_t::keep_tag = false;
std::string truncated(const std::string& str, unsigned int width, std::string truncated(const std::string& str, unsigned int width,
const int style) const int style)
{ {
@ -72,8 +68,6 @@ std::string partial_account_name(const account_t& account)
return name; return name;
} }
std::string format_t::date_format = "%Y/%m/%d";
element_t * format_t::parse_elements(const std::string& fmt) element_t * format_t::parse_elements(const std::string& fmt)
{ {
std::auto_ptr<element_t> result; std::auto_ptr<element_t> result;
@ -204,11 +198,11 @@ element_t * format_t::parse_elements(const std::string& fmt)
case 'd': case 'd':
current->type = element_t::COMPLETE_DATE_STRING; current->type = element_t::COMPLETE_DATE_STRING;
current->chars = format_t::date_format; current->chars = datetime_t::date_format;
break; break;
case 'D': case 'D':
current->type = element_t::DATE_STRING; current->type = element_t::DATE_STRING;
current->chars = format_t::date_format; current->chars = datetime_t::date_format;
break; break;
case 'S': current->type = element_t::SOURCE; break; case 'S': current->type = element_t::SOURCE; break;
@ -310,15 +304,29 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
calc->compute(value, details); calc->compute(value, details);
if (! keep_price || ! keep_date || ! keep_tag) if (! amount_t::keep_price ||
value = value.reduce(keep_price, keep_date, keep_tag); ! amount_t::keep_date ||
! amount_t::keep_tag) {
switch (value.type) {
case value_t::AMOUNT:
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
value = value.strip_annotations();
break;
default:
break;
}
}
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << (*((bool *) value.data) ? "1" : "0"); out << (*((bool *) value.data) ? "true" : "false");
break; break;
case value_t::INTEGER: case value_t::INTEGER:
out << *((unsigned int *) value.data); out << *((long *) value.data);
break;
case value_t::DATETIME:
out << *((datetime_t *) value.data);
break; break;
case value_t::AMOUNT: case value_t::AMOUNT:
out << *((amount_t *) value.data); out << *((amount_t *) value.data);

View file

@ -72,12 +72,6 @@ struct format_t
std::string format_string; std::string format_string;
element_t * elements; element_t * elements;
static bool keep_price;
static bool keep_date;
static bool keep_tag;
static std::string date_format;
format_t() : elements(NULL) { format_t() : elements(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor format_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor format_t");
} }

123
main.cc
View file

@ -10,6 +10,8 @@
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include "acconf.h"
#ifdef HAVE_UNIX_PIPES #ifdef HAVE_UNIX_PIPES
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -18,27 +20,11 @@
#endif #endif
#include "ledger.h" #include "ledger.h"
#include "timing.h"
using namespace ledger; using namespace ledger;
namespace { int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
TIMER_DEF_(setup);
TIMER_DEF_(parse);
TIMER_DEF_(process);
TIMER_DEF_(walk);
TIMER_DEF_(cleanup);
TIMER_DEF_(cache_write);
}
int parse_and_report(int argc, char * argv[], char * envp[])
{ {
TIMER_START(setup);
config_t config;
std::auto_ptr<journal_t> journal(new journal_t);
// Configure the terminus for value expressions // Configure the terminus for value expressions
ledger::terminus = now; ledger::terminus = now;
@ -60,6 +46,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
config.use_cache = config.data_file.empty() && config.price_db.empty(); config.use_cache = config.data_file.empty() && config.price_db.empty();
DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache); DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache);
TRACE(main, "Processing options and environment variables");
config.process_environment(envp, "LEDGER_"); config.process_environment(envp, "LEDGER_");
#if 1 #if 1
@ -90,6 +78,14 @@ int parse_and_report(int argc, char * argv[], char * envp[])
config.use_cache = false; config.use_cache = false;
DEBUG_PRINT("ledger.config.cache", "2. use_cache = " << config.use_cache); DEBUG_PRINT("ledger.config.cache", "2. use_cache = " << config.use_cache);
TRACE(main, std::string("Initialization file is ") + config.init_file);
TRACE(main, std::string("Price database is ") + config.price_db);
TRACE(main, std::string("Binary cache is ") + config.cache_file);
TRACE(main, std::string("Main journal is ") + config.data_file);
TRACE(main, std::string("Based on option settings, binary cache ") +
(config.use_cache ? "WILL " : "will NOT ") + "be used");
// Read the command word, canonicalize it to its one letter form, // Read the command word, canonicalize it to its one letter form,
// then configure the system based on the kind of report to be // then configure the system based on the kind of report to be
// generated // generated
@ -124,15 +120,23 @@ int parse_and_report(int argc, char * argv[], char * envp[])
} }
else if (command == "parse") { else if (command == "parse") {
value_auto_ptr expr(ledger::parse_value_expr(*arg)); value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.debug_mode) { if (config.verbose_mode) {
ledger::dump_value_expr(std::cout, expr.get()); ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl; std::cout << std::endl;
} }
value_t result = expr->compute(); value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
result = result.reduce(config.keep_price, config.keep_date, switch (result.type) {
config.keep_tag); case value_t::AMOUNT:
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
result = result.strip_annotations();
break;
default:
break;
}
}
std::cout << result << std::endl; std::cout << result << std::endl;
return 0; return 0;
} }
@ -142,22 +146,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else else
throw error(std::string("Unrecognized command '") + command + "'"); throw error(std::string("Unrecognized command '") + command + "'");
TIMER_STOP(setup);
// Parse initialization files, ledger data, price database, etc. // Parse initialization files, ledger data, price database, etc.
TIMER_START(parse); std::auto_ptr<journal_t> journal(new journal_t);
if (parse_ledger_data(config, journal.get()) == 0) { TRACE_PUSH(parser, "Parsing journal file");
throw error("Please specify ledger file using -f"
" or LEDGER_FILE environment variable.");
TIMER_STOP(parse); if (parse_ledger_data(config, journal.get()) == 0)
throw error("Please specify ledger file using -f"
" or LEDGER_FILE environment variable.");
TRACE_POP(parser, "Finished parsing"); }
// process the command word and its following arguments // process the command word and its following arguments
TIMER_START(process);
std::string first_arg; std::string first_arg;
if (command == "w") { if (command == "w") {
if (arg == args.end()) if (arg == args.end())
@ -165,6 +167,9 @@ int parse_and_report(int argc, char * argv[], char * envp[])
first_arg = *arg++; first_arg = *arg++;
} }
TRACE(options, std::string("Post-processing options ") +
"for command \"" + command + "\"");
config.process_options(command, arg, args.end()); config.process_options(command, arg, args.end());
std::auto_ptr<entry_t> new_entry; std::auto_ptr<entry_t> new_entry;
@ -232,14 +237,22 @@ int parse_and_report(int argc, char * argv[], char * envp[])
if (command == "expr") { if (command == "expr") {
value_auto_ptr expr(ledger::parse_value_expr(*arg)); value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.debug_mode) { if (config.verbose_mode) {
ledger::dump_value_expr(std::cout, expr.get()); ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl; std::cout << std::endl;
} }
value_t result = expr->compute(); value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
result = result.reduce(config.keep_price, config.keep_date, switch (result.type) {
config.keep_tag); case value_t::AMOUNT:
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
result = result.strip_annotations();
break;
default:
break;
}
}
std::cout << result << std::endl; std::cout << result << std::endl;
return 0; return 0;
} }
@ -265,12 +278,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else else
format = &config.print_format; format = &config.print_format;
TIMER_STOP(process);
// Walk the entries based on the report type and the options // Walk the entries based on the report type and the options
TIMER_START(walk);
item_handler<transaction_t> * formatter; item_handler<transaction_t> * formatter;
std::list<item_handler<transaction_t> *> formatter_ptrs; std::list<item_handler<transaction_t> *> formatter_ptrs;
@ -290,9 +299,13 @@ int parse_and_report(int argc, char * argv[], char * envp[])
formatter = new format_transactions(*out, *format); formatter = new format_transactions(*out, *format);
if (command == "w") { if (command == "w") {
TRACE_PUSH(text_writer, "Writing journal file");
write_textual_journal(*journal, first_arg, *formatter, write_textual_journal(*journal, first_arg, *formatter,
config.write_hdr_format, *out); config.write_hdr_format, *out);
TRACE_POP(text_writer, "Finished writing");
} else { } else {
TRACE_PUSH(main, "Walking journal entries");
formatter = config.chain_xact_handlers(command, formatter, journal.get(), formatter = config.chain_xact_handlers(command, formatter, journal.get(),
journal->master, formatter_ptrs); journal->master, formatter_ptrs);
if (command == "e") if (command == "e")
@ -304,11 +317,15 @@ int parse_and_report(int argc, char * argv[], char * envp[])
if (command != "P" && command != "D") if (command != "P" && command != "D")
formatter->flush(); formatter->flush();
TRACE_POP(main, "Finished entry walk");
} }
// For the balance and equity reports, output the sum totals. // For the balance and equity reports, output the sum totals.
if (command == "b") { if (command == "b") {
TRACE_PUSH(main, "Walking journal accounts");
format_account acct_formatter(*out, *format, config.display_predicate); format_account acct_formatter(*out, *format, config.display_predicate);
sum_accounts(*journal->master); sum_accounts(*journal->master);
walk_accounts(*journal->master, acct_formatter, config.sort_string); walk_accounts(*journal->master, acct_formatter, config.sort_string);
@ -322,19 +339,22 @@ int parse_and_report(int argc, char * argv[], char * envp[])
acct_formatter.format.format(*out, details_t(*journal->master)); acct_formatter.format.format(*out, details_t(*journal->master));
} }
} }
TRACE_POP(main, "Finished account walk");
} }
else if (command == "E") { else if (command == "E") {
TRACE_PUSH(main, "Walking journal accounts");
format_equity acct_formatter(*out, *format, config.display_predicate); format_equity acct_formatter(*out, *format, config.display_predicate);
sum_accounts(*journal->master); sum_accounts(*journal->master);
walk_accounts(*journal->master, acct_formatter, config.sort_string); walk_accounts(*journal->master, acct_formatter, config.sort_string);
acct_formatter.flush(); acct_formatter.flush();
TRACE_POP(main, "Finished account walk");
} }
TIMER_STOP(walk);
TIMER_START(cleanup);
#if DEBUG_LEVEL >= BETA #if DEBUG_LEVEL >= BETA
{ TRACE_PUSH(cleanup, "Cleaning up allocated memory");
clear_transaction_xdata xact_cleaner; clear_transaction_xdata xact_cleaner;
walk_entries(journal->entries, xact_cleaner); walk_entries(journal->entries, xact_cleaner);
@ -349,17 +369,19 @@ int parse_and_report(int argc, char * argv[], char * envp[])
i != formatter_ptrs.end(); i != formatter_ptrs.end();
i++) i++)
delete *i; delete *i;
#endif
TIMER_STOP(cleanup); TRACE_POP(cleanup, "Finished cleaning"); }
#endif
// Write out the binary cache, if need be // Write out the binary cache, if need be
if (config.use_cache && config.cache_dirty && ! config.cache_file.empty()) { if (config.use_cache && config.cache_dirty && ! config.cache_file.empty()) {
TIMER_START(cache_write); TRACE_PUSH(binary_cache, "Writing journal file");
std::ofstream stream(config.cache_file.c_str()); std::ofstream stream(config.cache_file.c_str());
write_binary_journal(stream, journal.get()); write_binary_journal(stream, journal.get());
TIMER_STOP(cache_write);
TRACE_POP(binary_cache, "Finished writing");
} }
#ifdef HAVE_UNIX_PIPES #ifdef HAVE_UNIX_PIPES
@ -380,7 +402,14 @@ int parse_and_report(int argc, char * argv[], char * envp[])
int main(int argc, char * argv[], char * envp[]) int main(int argc, char * argv[], char * envp[])
{ {
try { try {
return parse_and_report(argc, argv, envp); #if DEBUG_LEVEL < BETA
ledger::do_cleanup = false;
#endif
config_t config;
TRACE_PUSH(main, "Starting Ledger " PACKAGE_VERSION);
int status = parse_and_report(config, argc, argv, envp);
TRACE_POP(main, "Ledger done");
return status;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "Error: " << err.what() << std::endl; std::cerr << "Error: " << err.what() << std::endl;

View file

@ -49,6 +49,8 @@ namespace {
startup::~startup() startup::~startup()
{ {
if (! ledger::do_cleanup)
return;
shutdown_parser_support(); shutdown_parser_support();
} }
} }

View file

@ -20,6 +20,9 @@ class timing_t
timing_t(const std::string& _symbol, const std::string& _category) timing_t(const std::string& _symbol, const std::string& _category)
: begin(0), cumulative(0), symbol(_symbol), category(_category) {} : begin(0), cumulative(0), symbol(_symbol), category(_category) {}
timing_t(const std::string& _symbol)
: begin(0), cumulative(0), symbol(_symbol) {}
~timing_t() { ~timing_t() {
std::string cls = "timing.results."; std::string cls = "timing.results.";
cls += symbol; cls += symbol;
@ -33,6 +36,9 @@ class timing_t
line = _line; line = _line;
begin = std::clock(); begin = std::clock();
} }
void start() {
begin = std::clock();
}
void stop() { void stop() {
cumulative += std::clock() - begin; cumulative += std::clock() - begin;

View file

@ -35,6 +35,7 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
amt = *((amount_t *) result.data); amt = *((amount_t *) result.data);
break; break;
case value_t::DATETIME:
case value_t::BALANCE: case value_t::BALANCE:
case value_t::BALANCE_PAIR: case value_t::BALANCE_PAIR:
return false; return false;
@ -68,8 +69,12 @@ value_expr_t::~value_expr_t()
delete constant_a; delete constant_a;
break; break;
case CONSTANT_I:
case CONSTANT_T: case CONSTANT_T:
assert(constant_t);
delete constant_t;
break;
case CONSTANT_I:
case CONSTANT_V: case CONSTANT_V:
break; break;
@ -145,7 +150,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
result = constant_i; result = constant_i;
break; break;
case CONSTANT_T: case CONSTANT_T:
result = long(constant_t); result = *constant_t;
break; break;
case CONSTANT_A: case CONSTANT_A:
result = *constant_a; result = *constant_a;
@ -155,7 +160,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break; break;
case F_NOW: case F_NOW:
result = long(terminus); result = datetime_t(terminus);
break; break;
case AMOUNT: case AMOUNT:
@ -262,37 +267,37 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case DATE: case DATE:
if (details.xact && transaction_has_xdata(*details.xact) && if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date) transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date); result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact) else if (details.xact)
result = long(details.xact->date()); result = datetime_t(details.xact->date());
else if (details.entry) else if (details.entry)
result = long(details.entry->date()); result = datetime_t(details.entry->date());
else else
result = long(terminus); result = datetime_t(terminus);
break; break;
case ACT_DATE: case ACT_DATE:
if (details.xact && transaction_has_xdata(*details.xact) && if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date) transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date); result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact) else if (details.xact)
result = long(details.xact->actual_date()); result = datetime_t(details.xact->actual_date());
else if (details.entry) else if (details.entry)
result = long(details.entry->actual_date()); result = datetime_t(details.entry->actual_date());
else else
result = long(terminus); result = datetime_t(terminus);
break; break;
case EFF_DATE: case EFF_DATE:
if (details.xact && transaction_has_xdata(*details.xact) && if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date) transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date); result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact) else if (details.xact)
result = long(details.xact->effective_date()); result = datetime_t(details.xact->effective_date());
else if (details.entry) else if (details.entry)
result = long(details.entry->effective_date()); result = datetime_t(details.entry->effective_date());
else else
result = long(terminus); result = datetime_t(terminus);
break; break;
case CLEARED: case CLEARED:
@ -373,11 +378,40 @@ void value_expr_t::compute(value_t& result, const details_t& details,
index = 0; index = 0;
expr = find_leaf(context, 1, index); expr = find_leaf(context, 1, index);
amount_t moment; value_t moment;
if (compute_amount(expr, moment, NULL, context)) expr->compute(moment, details, context);
if (moment.type == value_t::DATETIME) {
result.cast(value_t::INTEGER);
moment.cast(value_t::INTEGER);
result -= moment; result -= moment;
else } else {
throw compute_error("Invalid date passed to datecmp(value,date)"); throw compute_error("Invalid date passed to datecmp(value,date)");
}
break;
}
case F_YEAR:
case F_MONTH:
case F_DAY: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context);
// jww (2006-03-05): Generate an error if result is not a DATETIME
std::time_t moment = (long)result;
struct std::tm * desc = std::localtime(&moment);
switch (kind) {
case F_YEAR:
result = (long)desc->tm_year + 1900L;
break;
case F_MONTH:
result = (long)desc->tm_mon + 1L;
break;
case F_DAY:
result = (long)desc->tm_mday;
break;
}
break; break;
} }
@ -580,13 +614,12 @@ void value_expr_t::compute(value_t& result, const details_t& details,
index = 0; index = 0;
expr = find_leaf(context, 1, index); expr = find_leaf(context, 1, index);
value_t moment;
amount_t moment; expr->compute(moment, details, context);
if (compute_amount(expr, moment, details.xact, context)) if (moment.type != value_t::DATETIME)
result = result.value((long)moment);
else
throw compute_error("Invalid date passed to P(value,date)"); throw compute_error("Invalid date passed to P(value,date)");
result = result.value(*((datetime_t *)moment.data));
break; break;
} }
@ -756,14 +789,18 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
c = peek_next_nonws(in); c = peek_next_nonws(in);
} }
bool definition = false;
if (c == '=') { if (c == '=') {
in.get(c); in.get(c);
if (peek_next_nonws(in) == '=') { if (peek_next_nonws(in) == '=') {
in.unget(); in.unget();
c = '\0'; c = '\0';
goto parsed; // parse this as == operator } else {
definition = true;
} }
}
if (definition) {
std::auto_ptr<scope_t> params(new scope_t(scope)); std::auto_ptr<scope_t> params(new scope_t(scope));
int index = 0; int index = 0;
@ -970,7 +1007,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
node.reset(new value_expr_t(value_expr_t::CONSTANT_T)); node.reset(new value_expr_t(value_expr_t::CONSTANT_T));
interval_t timespan(buf); interval_t timespan(buf);
node->constant_t = timespan.first(); node->constant_t = new datetime_t(timespan.first());
break; break;
} }
@ -1350,6 +1387,28 @@ void init_value_expr()
node->set_right(new value_expr_t(value_expr_t::F_DATECMP)); node->set_right(new value_expr_t(value_expr_t::F_DATECMP));
globals->define("datecmp", node); globals->define("datecmp", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_YEAR));
globals->define("yearof", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_MONTH));
globals->define("monthof", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_DAY));
globals->define("dayof", node);
value_auto_ptr year(parse_boolean_expr("year=yearof(d)", globals));
value_auto_ptr month(parse_boolean_expr("month=monthof(d)", globals));
value_auto_ptr day(parse_boolean_expr("day=dayof(d)", globals));
// Macros // Macros
node = parse_value_expr("P(a,d)"); node = parse_value_expr("P(a,d)");
globals->define("v", node); globals->define("v", node);
@ -1437,7 +1496,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
out << "CONSTANT_I - " << node->constant_i; out << "CONSTANT_I - " << node->constant_i;
break; break;
case value_expr_t::CONSTANT_T: case value_expr_t::CONSTANT_T:
out << "CONSTANT_T - [" << node->constant_t << ']'; out << "CONSTANT_T - [" << *(node->constant_t) << ']';
break; break;
case value_expr_t::CONSTANT_A: case value_expr_t::CONSTANT_A:
out << "CONSTANT_A - {" << *(node->constant_a) << '}'; out << "CONSTANT_A - {" << *(node->constant_a) << '}';
@ -1481,6 +1540,10 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
case value_expr_t::F_VALUE: out << "F_VALUE"; break; case value_expr_t::F_VALUE: out << "F_VALUE"; break;
case value_expr_t::F_PRICE: out << "F_PRICE"; break; case value_expr_t::F_PRICE: out << "F_PRICE"; break;
case value_expr_t::F_DATE: out << "F_DATE"; break; case value_expr_t::F_DATE: out << "F_DATE"; break;
case value_expr_t::F_DATECMP: out << "F_DATECMP"; break;
case value_expr_t::F_YEAR: out << "F_YEAR"; break;
case value_expr_t::F_MONTH: out << "F_MONTH"; break;
case value_expr_t::F_DAY: out << "F_DAY"; break;
case value_expr_t::O_NOT: out << "O_NOT"; break; case value_expr_t::O_NOT: out << "O_NOT"; break;
case value_expr_t::O_ARG: out << "O_ARG"; break; case value_expr_t::O_ARG: out << "O_ARG"; break;

View file

@ -113,6 +113,9 @@ struct value_expr_t
F_PRICE, F_PRICE,
F_DATE, F_DATE,
F_DATECMP, F_DATECMP,
F_YEAR,
F_MONTH,
F_DAY,
F_CODE_MASK, F_CODE_MASK,
F_PAYEE_MASK, F_PAYEE_MASK,
F_NOTE_MASK, F_NOTE_MASK,
@ -155,7 +158,7 @@ struct value_expr_t
value_expr_t * left; value_expr_t * left;
union { union {
std::time_t constant_t; datetime_t * constant_t;
long constant_i; long constant_i;
amount_t * constant_a; amount_t * constant_a;
value_t * constant_v; value_t * constant_v;

362
value.cc
View file

@ -64,6 +64,10 @@ value_t& value_t::operator=(const value_t& value)
*((long *) data) = *((long *) value.data); *((long *) data) = *((long *) value.data);
break; break;
case DATETIME:
*((datetime_t *) data) = *((datetime_t *) value.data);
break;
case AMOUNT: case AMOUNT:
new((amount_t *)data) amount_t(*((amount_t *) value.data)); new((amount_t *)data) amount_t(*((amount_t *) value.data));
break; break;
@ -88,14 +92,17 @@ value_t& value_t::operator=(const value_t& value)
value_t& value_t::operator+=(const value_t& value) value_t& value_t::operator+=(const value_t& value)
{ {
if (value.type == BOOLEAN)
throw 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");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot add a value to a boolean");
case INTEGER: case INTEGER:
cast(INTEGER);
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((long *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((long *) data) += *((long *) value.data); *((long *) data) += *((long *) value.data);
break; break;
@ -117,17 +124,28 @@ value_t& value_t::operator+=(const value_t& value)
} }
break; break;
case DATETIME:
switch (value.type) {
case INTEGER:
*((datetime_t *) data) += *((long *) value.data);
break;
case AMOUNT:
*((datetime_t *) data) += long(*((amount_t *) value.data));
break;
case BALANCE:
*((datetime_t *) data) += long(*((balance_t *) value.data));
break;
case BALANCE_PAIR:
*((datetime_t *) data) += long(*((balance_pair_t *) value.data));
break;
default:
assert(0);
break;
}
break;
case AMOUNT: case AMOUNT:
switch (value.type) { switch (value.type) {
case BOOLEAN:
if (*((bool *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this += value;
}
*((amount_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
if (*((long *) value.data) && if (*((long *) value.data) &&
((amount_t *) data)->commodity()) { ((amount_t *) data)->commodity()) {
@ -164,9 +182,6 @@ value_t& value_t::operator+=(const value_t& value)
case BALANCE: case BALANCE:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_t *) data) += *((long *) value.data); *((balance_t *) data) += *((long *) value.data);
break; break;
@ -188,9 +203,6 @@ value_t& value_t::operator+=(const value_t& value)
case BALANCE_PAIR: case BALANCE_PAIR:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_pair_t *) data) += *((long *) value.data); *((balance_pair_t *) data) += *((long *) value.data);
break; break;
@ -218,14 +230,17 @@ value_t& value_t::operator+=(const value_t& value)
value_t& value_t::operator-=(const value_t& value) value_t& value_t::operator-=(const value_t& value)
{ {
if (value.type == BOOLEAN)
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");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot subtract a value from a boolean");
case INTEGER: case INTEGER:
cast(INTEGER);
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((long *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((long *) data) -= *((long *) value.data); *((long *) data) -= *((long *) value.data);
break; break;
@ -249,15 +264,6 @@ value_t& value_t::operator-=(const value_t& value)
case AMOUNT: case AMOUNT:
switch (value.type) { switch (value.type) {
case BOOLEAN:
if (*((bool *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this -= value;
}
*((amount_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
if (*((long *) value.data) && if (*((long *) value.data) &&
((amount_t *) data)->commodity()) { ((amount_t *) data)->commodity()) {
@ -294,9 +300,6 @@ value_t& value_t::operator-=(const value_t& value)
case BALANCE: case BALANCE:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_t *) data) -= *((long *) value.data); *((balance_t *) data) -= *((long *) value.data);
break; break;
@ -318,9 +321,6 @@ value_t& value_t::operator-=(const value_t& value)
case BALANCE_PAIR: case BALANCE_PAIR:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_pair_t *) data) -= *((long *) value.data); *((balance_pair_t *) data) -= *((long *) value.data);
break; break;
@ -351,6 +351,11 @@ value_t& value_t::operator-=(const value_t& value)
value_t& value_t::operator*=(const value_t& value) value_t& value_t::operator*=(const value_t& value)
{ {
if (value.type == BOOLEAN)
throw 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");
if (value.realzero()) { if (value.realzero()) {
*this = 0L; *this = 0L;
return *this; return *this;
@ -358,12 +363,10 @@ value_t& value_t::operator*=(const value_t& value)
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot multiply a value by a boolean");
case INTEGER: case INTEGER:
cast(INTEGER);
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((long *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((long *) data) *= *((long *) value.data); *((long *) data) *= *((long *) value.data);
break; break;
@ -387,9 +390,6 @@ value_t& value_t::operator*=(const value_t& value)
case AMOUNT: case AMOUNT:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((amount_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((amount_t *) data) *= *((long *) value.data); *((amount_t *) data) *= *((long *) value.data);
break; break;
@ -412,9 +412,6 @@ value_t& value_t::operator*=(const value_t& value)
case BALANCE: case BALANCE:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_t *) data) *= *((long *) value.data); *((balance_t *) data) *= *((long *) value.data);
break; break;
@ -436,9 +433,6 @@ value_t& value_t::operator*=(const value_t& value)
case BALANCE_PAIR: case BALANCE_PAIR:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_pair_t *) data) *= *((long *) value.data); *((balance_pair_t *) data) *= *((long *) value.data);
break; break;
@ -466,14 +460,17 @@ value_t& value_t::operator*=(const value_t& value)
value_t& value_t::operator/=(const value_t& value) value_t& value_t::operator/=(const value_t& value)
{ {
if (value.type == BOOLEAN)
throw 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");
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot divide a value by a boolean");
case INTEGER: case INTEGER:
cast(INTEGER);
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((long *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((long *) data) /= *((long *) value.data); *((long *) data) /= *((long *) value.data);
break; break;
@ -497,9 +494,6 @@ value_t& value_t::operator/=(const value_t& value)
case AMOUNT: case AMOUNT:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((amount_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((amount_t *) data) /= *((long *) value.data); *((amount_t *) data) /= *((long *) value.data);
break; break;
@ -522,9 +516,6 @@ value_t& value_t::operator/=(const value_t& value)
case BALANCE: case BALANCE:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_t *) data) /= *((long *) value.data); *((balance_t *) data) /= *((long *) value.data);
break; break;
@ -546,9 +537,6 @@ value_t& value_t::operator/=(const value_t& value)
case BALANCE_PAIR: case BALANCE_PAIR:
switch (value.type) { switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER: case INTEGER:
*((balance_pair_t *) data) /= *((long *) value.data); *((balance_pair_t *) data) /= *((long *) value.data);
break; break;
@ -586,6 +574,9 @@ bool value_t::operator OP(const value_t& value) \
case INTEGER: \ case INTEGER: \
return *((bool *) data) OP bool(*((long *) value.data)); \ return *((bool *) data) OP bool(*((long *) value.data)); \
\ \
case DATETIME: \
return *((bool *) data) OP bool(*((datetime_t *) value.data)); \
\
case AMOUNT: \ case AMOUNT: \
return *((bool *) data) OP bool(*((amount_t *) value.data)); \ return *((bool *) data) OP bool(*((amount_t *) value.data)); \
\ \
@ -608,8 +599,11 @@ bool value_t::operator OP(const value_t& value) \
((long) *((bool *) value.data))); \ ((long) *((bool *) value.data))); \
\ \
case INTEGER: \ case INTEGER: \
return (*((long *) data) OP *((long *) value.data)); \
\
case DATETIME: \
return (*((long *) data) OP \ return (*((long *) data) OP \
*((long *) value.data)); \ ((long) *((datetime_t *) value.data))); \
\ \
case AMOUNT: \ case AMOUNT: \
return (amount_t(*((long *) data)) OP \ return (amount_t(*((long *) data)) OP \
@ -629,15 +623,46 @@ bool value_t::operator OP(const value_t& value) \
} \ } \
break; \ break; \
\ \
case DATETIME: \
switch (value.type) { \
case BOOLEAN: \
throw value_error("Cannot compare a date/time to a boolean"); \
\
case INTEGER: \
return (*((datetime_t *) data) OP \
datetime_t(*((long *) value.data))); \
\
case DATETIME: \
return (*((datetime_t *) data) OP \
*((datetime_t *) value.data)); \
\
case AMOUNT: \
throw value_error("Cannot compare a date/time to an amount"); \
\
case BALANCE: \
throw 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"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case AMOUNT: \ case AMOUNT: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
return *((amount_t *) data) OP amount_t(*((bool *) value.data)); \ throw value_error("Cannot compare an amount to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return (*((amount_t *) data) OP \ return (*((amount_t *) data) OP \
amount_t(*((long *) value.data))); \ amount_t(*((long *) value.data))); \
\ \
case DATETIME: \
throw value_error("Cannot compare an amount to a date/time"); \
\
case AMOUNT: \ case AMOUNT: \
return *((amount_t *) data) OP *((amount_t *) value.data); \ return *((amount_t *) data) OP *((amount_t *) value.data); \
\ \
@ -660,11 +685,14 @@ bool value_t::operator OP(const value_t& value) \
case BALANCE: \ case BALANCE: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
return *((balance_t *) data) OP (long)*((bool *) value.data); \ throw value_error("Cannot compare a balance to a boolean"); \
\ \
case INTEGER: \ case INTEGER: \
return *((balance_t *) data) OP *((long *) value.data); \ return *((balance_t *) data) OP *((long *) value.data); \
\ \
case DATETIME: \
throw value_error("Cannot compare a balance to a date/time"); \
\
case AMOUNT: \ case AMOUNT: \
return *((balance_t *) data) OP *((amount_t *) value.data); \ return *((balance_t *) data) OP *((amount_t *) value.data); \
\ \
@ -684,13 +712,15 @@ bool value_t::operator OP(const value_t& value) \
case BALANCE_PAIR: \ case BALANCE_PAIR: \
switch (value.type) { \ switch (value.type) { \
case BOOLEAN: \ case BOOLEAN: \
return (((balance_pair_t *) data)->quantity OP \ throw value_error("Cannot compare a balance pair to a boolean"); \
(long)*((bool *) value.data)); \
\ \
case INTEGER: \ case INTEGER: \
return (((balance_pair_t *) data)->quantity OP \ return (((balance_pair_t *) data)->quantity OP \
*((long *) value.data)); \ *((long *) value.data)); \
\ \
case DATETIME: \
throw value_error("Cannot compare a balance pair to a date/time"); \
\
case AMOUNT: \ case AMOUNT: \
return (((balance_pair_t *) data)->quantity OP \ return (((balance_pair_t *) data)->quantity OP \
*((amount_t *) value.data)); \ *((amount_t *) value.data)); \
@ -727,15 +757,42 @@ value_t::operator long() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
return *((bool *) data) ? 1L : 0L; throw value_error("Cannot convert a boolean to an integer");
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME:
return *((datetime_t *) data);
case AMOUNT: case AMOUNT:
return *((amount_t *) data); return *((amount_t *) data);
case BALANCE: case BALANCE:
throw value_error("Cannot convert a value balance to a long"); throw value_error("Cannot convert a balance to an integer");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a value balance pair to a long"); throw value_error("Cannot convert a balance pair to an integer");
default:
assert(0);
break;
}
assert(0);
return 0;
}
template <>
value_t::operator datetime_t() const
{
switch (type) {
case BOOLEAN:
throw 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");
case BALANCE:
throw 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");
default: default:
assert(0); assert(0);
@ -750,15 +807,17 @@ value_t::operator double() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
return *((bool *) data) ? 1.0 : 0.0; throw value_error("Cannot convert a boolean to a double");
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME:
return *((datetime_t *) data);
case AMOUNT: case AMOUNT:
return *((amount_t *) data); return *((amount_t *) data);
case BALANCE: case BALANCE:
throw value_error("Cannot convert a value balance to a double"); throw value_error("Cannot convert a balance to a double");
case BALANCE_PAIR: case BALANCE_PAIR:
throw value_error("Cannot convert a value balance pair to a double"); throw value_error("Cannot convert a balance pair to a double");
default: default:
assert(0); assert(0);
@ -776,17 +835,15 @@ void value_t::cast(type_t cast_type)
case BOOLEAN: case BOOLEAN:
break; break;
case INTEGER: case INTEGER:
*((long *) data) = *((bool *) data); throw value_error("Cannot convert a boolean to an integer");
break; case DATETIME:
throw value_error("Cannot convert a boolean to a date/time");
case AMOUNT: case AMOUNT:
new((amount_t *)data) amount_t(*((bool *) data)); throw value_error("Cannot convert a boolean to an amount");
break;
case BALANCE: case BALANCE:
new((balance_t *)data) balance_t(*((bool *) data)); throw value_error("Cannot convert a boolean to a balance");
break;
case BALANCE_PAIR: case BALANCE_PAIR:
new((balance_pair_t *)data) balance_pair_t(*((bool *) data)); throw value_error("Cannot convert a boolean to a balance pair");
break;
default: default:
assert(0); assert(0);
@ -801,6 +858,9 @@ void value_t::cast(type_t cast_type)
break; break;
case INTEGER: case INTEGER:
break; break;
case DATETIME:
*((datetime_t *) data) = datetime_t(*((long *) data));
break;
case AMOUNT: case AMOUNT:
new((amount_t *)data) amount_t(*((long *) data)); new((amount_t *)data) amount_t(*((long *) data));
break; break;
@ -817,6 +877,29 @@ void value_t::cast(type_t cast_type)
} }
break; break;
case DATETIME:
switch (cast_type) {
case BOOLEAN:
*((bool *) data) = *((datetime_t *) data);
break;
case INTEGER:
*((long *) data) = *((datetime_t *) data);
break;
case DATETIME:
break;
case AMOUNT:
throw value_error("Cannot convert a date/time to an amount");
case BALANCE:
throw 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");
default:
assert(0);
break;
}
break;
case AMOUNT: case AMOUNT:
switch (cast_type) { switch (cast_type) {
case BOOLEAN: { case BOOLEAN: {
@ -831,6 +914,8 @@ void value_t::cast(type_t cast_type)
*((long *)data) = temp; *((long *)data) = temp;
break; break;
} }
case DATETIME:
throw value_error("Cannot convert an amount to a date/time");
case AMOUNT: case AMOUNT:
break; break;
case BALANCE: { case BALANCE: {
@ -862,6 +947,9 @@ void value_t::cast(type_t cast_type)
} }
case INTEGER: case INTEGER:
throw value_error("Cannot convert a balance to an integer"); throw value_error("Cannot convert a balance to an integer");
case DATETIME:
throw value_error("Cannot convert a balance to a date/time");
case AMOUNT: { case AMOUNT: {
balance_t * temp = (balance_t *) data; balance_t * temp = (balance_t *) data;
if (temp->amounts.size() == 1) { if (temp->amounts.size() == 1) {
@ -903,6 +991,8 @@ void value_t::cast(type_t cast_type)
} }
case INTEGER: case INTEGER:
throw value_error("Cannot convert a balance pair to an integer"); throw value_error("Cannot convert a balance pair to an integer");
case DATETIME:
throw value_error("Cannot convert a balance pair to a date/time");
case AMOUNT: { case AMOUNT: {
balance_t * temp = &((balance_pair_t *) data)->quantity; balance_t * temp = &((balance_pair_t *) data)->quantity;
@ -951,6 +1041,8 @@ void value_t::negate()
case INTEGER: case INTEGER:
*((long *) data) = - *((long *) data); *((long *) data) = - *((long *) data);
break; break;
case DATETIME:
throw value_error("Cannot negate a date/time");
case AMOUNT: case AMOUNT:
((amount_t *) data)->negate(); ((amount_t *) data)->negate();
break; break;
@ -976,6 +1068,8 @@ void value_t::abs()
if (*((long *) data) < 0) if (*((long *) data) < 0)
*((long *) data) = - *((long *) data); *((long *) data) = - *((long *) data);
break; break;
case DATETIME:
break;
case AMOUNT: case AMOUNT:
((amount_t *) data)->abs(); ((amount_t *) data)->abs();
break; break;
@ -992,12 +1086,54 @@ void value_t::abs()
} }
} }
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");
case DATETIME:
throw value_error("Cannot find the value of a date/time");
case INTEGER:
return *this;
case AMOUNT:
return ((amount_t *) data)->value(moment);
case BALANCE:
return ((balance_t *) data)->value(moment);
case BALANCE_PAIR:
return ((balance_pair_t *) data)->quantity.value(moment);
}
}
void value_t::round()
{
switch (type) {
case BOOLEAN:
throw value_error("Cannot round a boolean");
case DATETIME:
throw value_error("Cannot round a date/time");
case INTEGER:
break;
case AMOUNT:
*((amount_t *) data) = ((amount_t *) data)->round();
break;
case BALANCE:
((balance_t *) data)->round();
break;
case BALANCE_PAIR:
((balance_pair_t *) data)->round();
break;
}
}
value_t value_t::price() const value_t value_t::price() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the price of a boolean");
case INTEGER: case INTEGER:
return *this; return *this;
case DATETIME:
throw value_error("Cannot find the price of a date/time");
case AMOUNT: case AMOUNT:
return ((amount_t *) data)->price(); return ((amount_t *) data)->price();
@ -1020,7 +1156,10 @@ value_t value_t::date() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the date of a boolean");
case INTEGER: case INTEGER:
return 0L;
case DATETIME:
return *this; return *this;
case AMOUNT: case AMOUNT:
@ -1040,23 +1179,28 @@ value_t value_t::date() const
return value_t(); return value_t();
} }
value_t value_t::reduce(const bool keep_price, const bool keep_date, value_t value_t::strip_annotations(const bool keep_price,
const bool keep_tag) const const bool keep_date,
const bool keep_tag) const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot strip commodity annotations from a boolean");
case INTEGER: case INTEGER:
return *this; return *this;
case DATETIME:
throw value_error("Cannot strip commodity annotations from a date/time");
case AMOUNT: case AMOUNT:
return ((amount_t *) data)->reduce_commodity(keep_price, keep_date, return ((amount_t *) data)->strip_annotations
keep_tag); (keep_price, keep_date, keep_tag);
case BALANCE: case BALANCE:
return ((balance_t *) data)->reduce(keep_price, keep_date, keep_tag); return ((balance_t *) data)->strip_annotations
(keep_price, keep_date, keep_tag);
case BALANCE_PAIR: case BALANCE_PAIR:
return ((balance_pair_t *) data)->quantity.reduce(keep_price, keep_date, return ((balance_pair_t *) data)->quantity.strip_annotations
keep_tag); (keep_price, keep_date, keep_tag);
default: default:
assert(0); assert(0);
break; break;
@ -1069,10 +1213,13 @@ value_t value_t::cost() const
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot find the cost of a boolean");
case INTEGER: case INTEGER:
case AMOUNT: case AMOUNT:
case BALANCE: case BALANCE:
return *this; return *this;
case DATETIME:
throw value_error("Cannot find the cost of a date/time");
case BALANCE_PAIR: case BALANCE_PAIR:
assert(((balance_pair_t *) data)->cost); assert(((balance_pair_t *) data)->cost);
@ -1093,6 +1240,9 @@ value_t& value_t::add(const amount_t& amount, const amount_t * cost)
{ {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
throw value_error("Cannot add an amount to a boolean");
case DATETIME:
throw value_error("Cannot add an amount to a date/time");
case INTEGER: case INTEGER:
case AMOUNT: case AMOUNT:
if (cost) { if (cost) {
@ -1150,6 +1300,7 @@ long value_len(value_t& value)
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
case value_t::INTEGER: case value_t::INTEGER:
case value_t::DATETIME:
case value_t::AMOUNT: case value_t::AMOUNT:
return 1; return 1;
@ -1178,9 +1329,14 @@ amount_t value_getitem(value_t& value, int i)
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
throw value_error("Cannot cast a boolean to an amount");
case value_t::INTEGER: case value_t::INTEGER:
return long(value); return long(value);
case value_t::DATETIME:
throw value_error("Cannot cast a date/time to an amount");
case value_t::AMOUNT: case value_t::AMOUNT:
return *((amount_t *) value.data); return *((amount_t *) value.data);
@ -1213,6 +1369,7 @@ void export_value()
.def(init<std::string>()) .def(init<std::string>())
.def(init<double>()) .def(init<double>())
.def(init<long>()) .def(init<long>())
.def(init<datetime_t>())
.def(self + self) .def(self + self)
.def(self + other<balance_pair_t>()) .def(self + other<balance_pair_t>())
@ -1301,12 +1458,14 @@ void export_value()
.def(self < other<balance_t>()) .def(self < other<balance_t>())
.def(self < other<amount_t>()) .def(self < other<amount_t>())
.def(self < long()) .def(self < long())
.def(self < other<datetime_t>())
.def(self < double()) .def(self < double())
.def(other<balance_pair_t>() < self) .def(other<balance_pair_t>() < self)
.def(other<balance_t>() < self) .def(other<balance_t>() < self)
.def(other<amount_t>() < self) .def(other<amount_t>() < self)
.def(long() < self) .def(long() < self)
.def(other<datetime_t>() < self)
.def(double() < self) .def(double() < self)
.def(self <= self) .def(self <= self)
@ -1314,12 +1473,14 @@ void export_value()
.def(self <= other<balance_t>()) .def(self <= other<balance_t>())
.def(self <= other<amount_t>()) .def(self <= other<amount_t>())
.def(self <= long()) .def(self <= long())
.def(self <= other<datetime_t>())
.def(self <= double()) .def(self <= double())
.def(other<balance_pair_t>() <= self) .def(other<balance_pair_t>() <= self)
.def(other<balance_t>() <= self) .def(other<balance_t>() <= self)
.def(other<amount_t>() <= self) .def(other<amount_t>() <= self)
.def(long() <= self) .def(long() <= self)
.def(other<datetime_t>() <= self)
.def(double() <= self) .def(double() <= self)
.def(self > self) .def(self > self)
@ -1327,12 +1488,14 @@ void export_value()
.def(self > other<balance_t>()) .def(self > other<balance_t>())
.def(self > other<amount_t>()) .def(self > other<amount_t>())
.def(self > long()) .def(self > long())
.def(self > other<datetime_t>())
.def(self > double()) .def(self > double())
.def(other<balance_pair_t>() > self) .def(other<balance_pair_t>() > self)
.def(other<balance_t>() > self) .def(other<balance_t>() > self)
.def(other<amount_t>() > self) .def(other<amount_t>() > self)
.def(long() > self) .def(long() > self)
.def(other<datetime_t>() > self)
.def(double() > self) .def(double() > self)
.def(self >= self) .def(self >= self)
@ -1340,12 +1503,14 @@ void export_value()
.def(self >= other<balance_t>()) .def(self >= other<balance_t>())
.def(self >= other<amount_t>()) .def(self >= other<amount_t>())
.def(self >= long()) .def(self >= long())
.def(self >= other<datetime_t>())
.def(self >= double()) .def(self >= double())
.def(other<balance_pair_t>() >= self) .def(other<balance_pair_t>() >= self)
.def(other<balance_t>() >= self) .def(other<balance_t>() >= self)
.def(other<amount_t>() >= self) .def(other<amount_t>() >= self)
.def(long() >= self) .def(long() >= self)
.def(other<datetime_t>() >= self)
.def(double() >= self) .def(double() >= self)
.def(self == self) .def(self == self)
@ -1353,12 +1518,14 @@ void export_value()
.def(self == other<balance_t>()) .def(self == other<balance_t>())
.def(self == other<amount_t>()) .def(self == other<amount_t>())
.def(self == long()) .def(self == long())
.def(self == other<datetime_t>())
.def(self == double()) .def(self == double())
.def(other<balance_pair_t>() == self) .def(other<balance_pair_t>() == self)
.def(other<balance_t>() == self) .def(other<balance_t>() == self)
.def(other<amount_t>() == self) .def(other<amount_t>() == self)
.def(long() == self) .def(long() == self)
.def(other<datetime_t>() == self)
.def(double() == self) .def(double() == self)
.def(self != self) .def(self != self)
@ -1366,12 +1533,14 @@ void export_value()
.def(self != other<balance_t>()) .def(self != other<balance_t>())
.def(self != other<amount_t>()) .def(self != other<amount_t>())
.def(self != long()) .def(self != long())
.def(self != other<datetime_t>())
.def(self != double()) .def(self != double())
.def(other<balance_pair_t>() != self) .def(other<balance_pair_t>() != self)
.def(other<balance_t>() != self) .def(other<balance_t>() != self)
.def(other<amount_t>() != self) .def(other<amount_t>() != self)
.def(long() != self) .def(long() != self)
.def(other<datetime_t>() != self)
.def(double() != self) .def(double() != self)
.def(! self) .def(! self)
@ -1386,16 +1555,23 @@ void export_value()
.def("__len__", value_len) .def("__len__", value_len)
.def("__getitem__", value_getitem) .def("__getitem__", value_getitem)
.def("abs", &value_t::abs)
.def("cast", &value_t::cast) .def("cast", &value_t::cast)
.def("negate", &value_t::negate)
.def("price", &value_t::price)
.def("cost", &value_t::cost) .def("cost", &value_t::cost)
.def("price", &value_t::price)
.def("date", &value_t::date)
.def("strip_annotations", &value_t::strip_annotations)
.def("add", &value_t::add, return_internal_reference<>()) .def("add", &value_t::add, return_internal_reference<>())
.def("value", &value_t::value)
.def("round", &value_t::round)
.def("negate", &value_t::negate)
.def("negated", &value_t::negated)
; ;
enum_< value_t::type_t > ("ValueType") enum_< value_t::type_t > ("ValueType")
.value("BOOLEAN", value_t::BOOLEAN) .value("BOOLEAN", value_t::BOOLEAN)
.value("INTEGER", value_t::INTEGER) .value("INTEGER", value_t::INTEGER)
.value("DATETIME", value_t::DATETIME)
.value("AMOUNT", value_t::AMOUNT) .value("AMOUNT", value_t::AMOUNT)
.value("BALANCE", value_t::BALANCE) .value("BALANCE", value_t::BALANCE)
.value("BALANCE_PAIR", value_t::BALANCE_PAIR) .value("BALANCE_PAIR", value_t::BALANCE_PAIR)

74
value.h
View file

@ -36,6 +36,7 @@ class value_t
enum type_t { enum type_t {
BOOLEAN, BOOLEAN,
INTEGER, INTEGER,
DATETIME,
AMOUNT, AMOUNT,
BALANCE, BALANCE,
BALANCE_PAIR BALANCE_PAIR
@ -57,6 +58,10 @@ class value_t
*((long *) data) = value; *((long *) data) = value;
type = INTEGER; type = INTEGER;
} }
value_t(const datetime_t value) {
*((datetime_t *) data) = value;
type = DATETIME;
}
value_t(const unsigned long value) { value_t(const unsigned long value) {
new((amount_t *) data) amount_t(value); new((amount_t *) data) amount_t(value);
type = AMOUNT; type = AMOUNT;
@ -108,6 +113,14 @@ class value_t
} }
return *this; return *this;
} }
value_t& operator=(const datetime_t value) {
if ((datetime_t *) data != &value) {
destroy();
*((datetime_t *) data) = value;
type = DATETIME;
}
return *this;
}
value_t& operator=(const unsigned long value) { value_t& operator=(const unsigned long value) {
return *this = amount_t(value); return *this = amount_t(value);
} }
@ -284,6 +297,8 @@ class value_t
return ! *((bool *) data); return ! *((bool *) data);
case INTEGER: case INTEGER:
return *((long *) data) == 0; return *((long *) data) == 0;
case DATETIME:
return ! *((datetime_t *) data);
case AMOUNT: case AMOUNT:
return ((amount_t *) data)->realzero(); return ((amount_t *) data)->realzero();
case BALANCE: case BALANCE:
@ -299,46 +314,19 @@ class value_t
return 0; return 0;
} }
void abs(); void abs();
void cast(type_t cast_type); void cast(type_t cast_type);
value_t cost() const; value_t cost() const;
value_t price() const; value_t price() const;
value_t date() const; value_t date() const;
value_t reduce(const bool keep_price = false,
const bool keep_date = false, value_t strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_tag = false) const; const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const;
value_t& add(const amount_t& amount, const amount_t * cost = NULL); value_t& add(const amount_t& amount, const amount_t * cost = NULL);
value_t value(const std::time_t moment) const;
value_t value(const std::time_t moment) const { void round();
switch (type) {
case BOOLEAN:
case INTEGER:
return *this;
case AMOUNT:
return ((amount_t *) data)->value(moment);
case BALANCE:
return ((balance_t *) data)->value(moment);
case BALANCE_PAIR:
return ((balance_pair_t *) data)->quantity.value(moment);
}
}
void round() {
switch (type) {
case BOOLEAN:
case INTEGER:
break;
case AMOUNT:
*((amount_t *) data) = ((amount_t *) data)->round();
break;
case BALANCE:
((balance_t *) data)->round();
break;
case BALANCE_PAIR:
((balance_pair_t *) data)->round();
break;
}
}
}; };
#define DEF_VALUE_AUX_OP(OP) \ #define DEF_VALUE_AUX_OP(OP) \
@ -379,6 +367,8 @@ value_t::operator T() const
return *((bool *) data); return *((bool *) data);
case INTEGER: case INTEGER:
return *((long *) data); return *((long *) data);
case DATETIME:
return *((datetime_t *) data);
case AMOUNT: case AMOUNT:
return *((amount_t *) data); return *((amount_t *) data);
case BALANCE: case BALANCE:
@ -395,6 +385,7 @@ value_t::operator T() const
} }
template <> value_t::operator long() const; template <> value_t::operator long() const;
template <> value_t::operator datetime_t() const;
template <> value_t::operator double() const; template <> value_t::operator double() const;
inline value_t abs(const value_t& value) { inline value_t abs(const value_t& value) {
@ -406,11 +397,14 @@ inline value_t abs(const value_t& value) {
inline std::ostream& operator<<(std::ostream& out, const value_t& value) { inline std::ostream& operator<<(std::ostream& out, const value_t& value) {
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << *((bool *) value.data); out << *((bool *) value.data) ? "true" : "false";
break; break;
case value_t::INTEGER: case value_t::INTEGER:
out << *((long *) value.data); out << *((long *) value.data);
break; break;
case value_t::DATETIME:
out << *((datetime_t *) value.data);
break;
case value_t::AMOUNT: case value_t::AMOUNT:
out << *((amount_t *) value.data); out << *((amount_t *) value.data);
break; break;

View file

@ -340,7 +340,7 @@ void subtotal_transactions::report_subtotal(const char * spec_fmt)
if (! spec_fmt) { if (! spec_fmt) {
std::string fmt = "- "; std::string fmt = "- ";
fmt += format_t::date_format; fmt += datetime_t::date_format;
std::strftime(buf, 255, fmt.c_str(), std::localtime(&finish)); std::strftime(buf, 255, fmt.c_str(), std::localtime(&finish));
} else { } else {
std::strftime(buf, 255, spec_fmt, std::localtime(&finish)); std::strftime(buf, 255, spec_fmt, std::localtime(&finish));