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 \
configure depcomp install-sh libtool ltconfig ltmain.sh \
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
- 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
significant incompatibilities being:

View file

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

View file

@ -15,6 +15,8 @@
namespace ledger {
extern bool do_cleanup;
class commodity_t;
class amount_t
@ -22,6 +24,10 @@ class amount_t
public:
class bigint_t;
static bool keep_price;
static bool keep_date;
static bool keep_tag;
protected:
void _init();
void _copy(const amount_t& amt);
@ -76,9 +82,9 @@ class amount_t
void annotate_commodity(const amount_t& price,
const std::time_t date = 0,
const std::string& tag = "");
amount_t reduce_commodity(const bool keep_price = false,
const bool keep_date = false,
const bool keep_tag = false) const;
amount_t strip_annotations(const bool _keep_price = keep_price,
const bool _keep_date = keep_date,
const bool _keep_tag = keep_tag) const;
void clear_commodity() {
commodity_ = NULL;
}
@ -543,8 +549,6 @@ class annotated_commodity_t : public commodity_t
std::time_t date;
std::string tag;
static std::string date_format;
static void write_annotations(std::ostream& out,
const amount_t& price,
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();
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) {
amounts_map::const_iterator i = amounts.find(&commodity);
@ -63,15 +74,16 @@ std::time_t balance_t::date() const
return temp;
}
balance_t balance_t::reduce(const bool keep_price, const bool keep_date,
const bool keep_tag) const
balance_t balance_t::strip_annotations(const bool keep_price,
const bool keep_date,
const bool keep_tag) const
{
balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
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;
}
@ -176,44 +188,150 @@ void balance_t::write(std::ostream& out,
balance_t& balance_t::operator*=(const balance_t& bal)
{
if (! *this || ! bal) {
return (*this = 0L);
if (realzero() || bal.realzero()) {
return *this = 0L;
}
else if (amounts.size() == 1 && bal.amounts.size() == 1) {
else if (bal.amounts.size() == 1) {
return *this *= (*bal.amounts.begin()).second;
}
else if (amounts.size() == 1) {
return *this = bal * *this;
}
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;
errmsg << "It makes no sense to multiply two balances: "
<< *this << " * " << bal;
errmsg << "Cannot multiply two balances: " << *this << " * " << bal;
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)
{
if (! *this) {
return (*this = 0L);
}
else if (! bal) {
if (bal.realzero()) {
std::ostringstream errmsg;
errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
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;
}
else if (*this == bal) {
return (*this = 1L);
return *this = 1L;
}
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;
errmsg << "It makes no sense to divide two balances: "
<< *this << " / " << bal;
errmsg << "Cannot divide between two balances: " << *this << " / " << bal;
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
{
if (amounts.size() == 1) {
@ -223,6 +341,11 @@ balance_t::operator amount_t() const
return amount_t();
}
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;
errmsg << "Cannot convert a balance with "
<< "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
#ifdef USE_BOOST_PYTHON
@ -353,13 +455,19 @@ void export_balance()
.def("__len__", balance_len)
.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("value", &balance_t::value)
.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("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")
@ -436,16 +544,23 @@ void export_balance()
.def("__len__", balance_pair_len)
.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",
make_getter(&balance_pair_t::cost,
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
#include "amount.h"
#include "datetime.h"
#include <map>
#include <ctime>
@ -35,13 +36,13 @@ class balance_t
*this += (*i).second;
}
balance_t(const amount_t& amt) {
if (amt)
if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt));
}
template <typename T>
balance_t(T value) {
amount_t amt(value);
if (amt)
if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt));
}
@ -80,7 +81,7 @@ class balance_t
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
(*i).second += amt;
else if (amt)
else if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt));
return *this;
}
@ -148,53 +149,14 @@ class balance_t
// multiplication and divide
balance_t& operator*=(const balance_t& bal);
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;
}
balance_t& operator*=(const amount_t& amt);
template <typename T>
balance_t& operator*=(T val) {
return *this *= amount_t(val);
}
balance_t& operator/=(const balance_t& bal);
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;
}
balance_t& operator/=(const amount_t& amt);
template <typename T>
balance_t& operator/=(T val) {
return *this /= amount_t(val);
@ -415,13 +377,27 @@ class balance_t
return false;
}
amount_t amount(const commodity_t& commodity) const;
balance_t value(const std::time_t moment) const;
balance_t price() const;
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;
}
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;
balance_t reduce(const bool keep_price = false,
const bool keep_date = false,
const bool keep_tag = false) const;
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;
void write(std::ostream& out, const int first_width,
const int latter_width = -1) const;
@ -440,17 +416,6 @@ class balance_t
if ((*i).second.commodity())
(*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) {
@ -496,7 +461,6 @@ class balance_pair_t
delete cost;
cost = NULL;
}
quantity = bal_pair.quantity;
if (bal_pair.cost)
cost = new balance_t(*bal_pair.cost);
@ -651,7 +615,14 @@ class balance_pair_t
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) {
quantity /= bal;
if (cost)
@ -813,34 +784,60 @@ class balance_pair_t
}
// test for non-zero (use ! for zero)
operator bool() const {
return quantity;
}
operator balance_t() const {
return quantity;
}
operator amount_t() const {
return quantity;
}
operator bool() const {
return quantity;
}
bool realzero() const {
return ((! cost || cost->realzero()) && quantity.realzero());
}
void abs() {
quantity.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);
}
balance_t value(const std::time_t moment) const {
balance_t value(const std::time_t moment = now) const {
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,
const int latter_width = -1) const {
quantity.write(out, first_width, latter_width);
}
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() {
return quantity.valid() && (! cost || cost->valid());
@ -850,10 +847,6 @@ class balance_pair_t
quantity.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) {

View file

@ -71,8 +71,9 @@ void config_t::reset()
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";
predicate = "";
display_predicate = "";
predicate = "";
secondary_predicate = "";
display_predicate = "";
head_entries = 0;
tail_entries = 0;
@ -91,6 +92,8 @@ void config_t::reset()
show_revalued_only = false;
download_quotes = false;
debug_mode = false;
verbose_mode = false;
trace_mode = false;
keep_price = false;
keep_date = false;
keep_tag = false;
@ -301,14 +304,12 @@ void config_t::process_options(const std::string& command,
// Now setup the various formatting strings
if (! date_format.empty()) {
format_t::date_format = date_format;
annotated_commodity_t::date_format = date_format;
}
if (! date_format.empty())
datetime_t::date_format = date_format;
format_t::keep_price = keep_price;
format_t::keep_date = keep_date;
format_t::keep_tag = keep_tag;
amount_t::keep_price = keep_price;
amount_t::keep_date = keep_date;
amount_t::keep_tag = keep_tag;
}
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
// (calculated against the transactions which it receives).
if (! reconcile_balance.empty()) {
value_t target_balance(reconcile_balance);
time_t cutoff = now;
std::time_t cutoff = now;
if (! reconcile_date.empty())
parse_date(reconcile_date.c_str(), &cutoff);
ptrs.push_back(formatter =
new reconcile_transactions(formatter, target_balance,
cutoff));
new reconcile_transactions
(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
// on the `sort_order' value expression.
if (! sort_string.empty())
@ -718,6 +725,14 @@ OPT_BEGIN(debug, ":") {
::setenv("DEBUG_CLASS", optarg, 1);
} 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
@ -1016,6 +1031,14 @@ OPT_BEGIN(limit, "l:") {
config->predicate += ")";
} 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:") {
if (! config->display_predicate.empty())
config->display_predicate += "&";
@ -1153,6 +1176,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "market", 'V', false, opt_market, false },
{ "monthly", 'M', false, opt_monthly, false },
{ "no-cache", '\0', false, opt_no_cache, false },
{ "only", '\0', true, opt_only, false },
{ "output", 'o', true, opt_output, false },
{ "pager", '\0', true, opt_pager, false },
{ "percentage", '%', false, opt_percentage, false },
@ -1178,8 +1202,10 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "total", 'T', true, opt_total, false },
{ "total-data", 'J', false, opt_total_data, false },
{ "totals", '\0', false, opt_totals, false },
{ "trace", '\0', false, opt_trace, false },
{ "unbudgeted", '\0', false, opt_unbudgeted, false },
{ "uncleared", 'U', false, opt_uncleared, false },
{ "verbose", '\0', false, opt_verbose, false },
{ "version", 'v', false, opt_version, false },
{ "weekly", 'W', false, opt_weekly, false },
{ "wide", 'w', false, opt_wide, false },
@ -1189,4 +1215,31 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "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

View file

@ -2,6 +2,7 @@
#define _CONFIG_H
#include "ledger.h"
#include "timing.h"
#include <iostream>
#include <memory>
@ -22,6 +23,7 @@ class config_t
std::string output_file;
std::string account;
std::string predicate;
std::string secondary_predicate;
std::string display_predicate;
std::string report_period;
std::string report_period_sort;
@ -66,6 +68,8 @@ class config_t
bool use_cache;
bool cache_dirty;
bool debug_mode;
bool verbose_mode;
bool trace_mode;
bool keep_price;
bool keep_date;
bool keep_tag;
@ -102,7 +106,7 @@ class config_t
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];
void option_help(std::ostream& out);
@ -112,6 +116,29 @@ void option_help(std::ostream& out);
#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
#endif // _CONFIG_H

View file

@ -34,6 +34,8 @@ const char * formats[] = {
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 quant = begin;

View file

@ -4,6 +4,71 @@
#include <ctime>
#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
{
unsigned int years;
@ -36,6 +101,15 @@ struct interval_t
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 int now_year;
extern char input_format[128];

View file

@ -7,10 +7,6 @@
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,
const int style)
{
@ -72,8 +68,6 @@ std::string partial_account_name(const account_t& account)
return name;
}
std::string format_t::date_format = "%Y/%m/%d";
element_t * format_t::parse_elements(const std::string& fmt)
{
std::auto_ptr<element_t> result;
@ -204,11 +198,11 @@ element_t * format_t::parse_elements(const std::string& fmt)
case 'd':
current->type = element_t::COMPLETE_DATE_STRING;
current->chars = format_t::date_format;
current->chars = datetime_t::date_format;
break;
case 'D':
current->type = element_t::DATE_STRING;
current->chars = format_t::date_format;
current->chars = datetime_t::date_format;
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);
if (! keep_price || ! keep_date || ! keep_tag)
value = value.reduce(keep_price, keep_date, keep_tag);
if (! amount_t::keep_price ||
! 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) {
case value_t::BOOLEAN:
out << (*((bool *) value.data) ? "1" : "0");
out << (*((bool *) value.data) ? "true" : "false");
break;
case value_t::INTEGER:
out << *((unsigned int *) value.data);
out << *((long *) value.data);
break;
case value_t::DATETIME:
out << *((datetime_t *) value.data);
break;
case value_t::AMOUNT:
out << *((amount_t *) value.data);

View file

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

123
main.cc
View file

@ -10,6 +10,8 @@
#include <cstring>
#include <ctime>
#include "acconf.h"
#ifdef HAVE_UNIX_PIPES
#include <sys/types.h>
#include <sys/wait.h>
@ -18,27 +20,11 @@
#endif
#include "ledger.h"
#include "timing.h"
using namespace ledger;
namespace {
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[])
int parse_and_report(config_t& config, 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
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();
DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache);
TRACE(main, "Processing options and environment variables");
config.process_environment(envp, "LEDGER_");
#if 1
@ -90,6 +78,14 @@ int parse_and_report(int argc, char * argv[], char * envp[])
config.use_cache = false;
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,
// then configure the system based on the kind of report to be
// generated
@ -124,15 +120,23 @@ int parse_and_report(int argc, char * argv[], char * envp[])
}
else if (command == "parse") {
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());
std::cout << std::endl;
}
value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag)
result = result.reduce(config.keep_price, config.keep_date,
config.keep_tag);
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
switch (result.type) {
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;
return 0;
}
@ -142,22 +146,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else
throw error(std::string("Unrecognized command '") + command + "'");
TIMER_STOP(setup);
// 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)
throw error("Please specify ledger file using -f"
" or LEDGER_FILE environment variable.");
{ TRACE_PUSH(parser, "Parsing journal file");
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
TIMER_START(process);
std::string first_arg;
if (command == "w") {
if (arg == args.end())
@ -165,6 +167,9 @@ int parse_and_report(int argc, char * argv[], char * envp[])
first_arg = *arg++;
}
TRACE(options, std::string("Post-processing options ") +
"for command \"" + command + "\"");
config.process_options(command, arg, args.end());
std::auto_ptr<entry_t> new_entry;
@ -232,14 +237,22 @@ int parse_and_report(int argc, char * argv[], char * envp[])
if (command == "expr") {
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());
std::cout << std::endl;
}
value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag)
result = result.reduce(config.keep_price, config.keep_date,
config.keep_tag);
if (! config.keep_price || ! config.keep_date || ! config.keep_tag) {
switch (result.type) {
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;
return 0;
}
@ -265,12 +278,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else
format = &config.print_format;
TIMER_STOP(process);
// Walk the entries based on the report type and the options
TIMER_START(walk);
item_handler<transaction_t> * formatter;
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);
if (command == "w") {
TRACE_PUSH(text_writer, "Writing journal file");
write_textual_journal(*journal, first_arg, *formatter,
config.write_hdr_format, *out);
TRACE_POP(text_writer, "Finished writing");
} else {
TRACE_PUSH(main, "Walking journal entries");
formatter = config.chain_xact_handlers(command, formatter, journal.get(),
journal->master, formatter_ptrs);
if (command == "e")
@ -304,11 +317,15 @@ int parse_and_report(int argc, char * argv[], char * envp[])
if (command != "P" && command != "D")
formatter->flush();
TRACE_POP(main, "Finished entry walk");
}
// For the balance and equity reports, output the sum totals.
if (command == "b") {
TRACE_PUSH(main, "Walking journal accounts");
format_account acct_formatter(*out, *format, config.display_predicate);
sum_accounts(*journal->master);
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));
}
}
TRACE_POP(main, "Finished account walk");
}
else if (command == "E") {
TRACE_PUSH(main, "Walking journal accounts");
format_equity acct_formatter(*out, *format, config.display_predicate);
sum_accounts(*journal->master);
walk_accounts(*journal->master, acct_formatter, config.sort_string);
acct_formatter.flush();
TRACE_POP(main, "Finished account walk");
}
TIMER_STOP(walk);
TIMER_START(cleanup);
#if DEBUG_LEVEL >= BETA
{ TRACE_PUSH(cleanup, "Cleaning up allocated memory");
clear_transaction_xdata 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++)
delete *i;
#endif
TIMER_STOP(cleanup);
TRACE_POP(cleanup, "Finished cleaning"); }
#endif
// Write out the binary cache, if need be
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());
write_binary_journal(stream, journal.get());
TIMER_STOP(cache_write);
TRACE_POP(binary_cache, "Finished writing");
}
#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[])
{
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) {
std::cerr << "Error: " << err.what() << std::endl;

View file

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

View file

@ -20,6 +20,9 @@ class timing_t
timing_t(const std::string& _symbol, const std::string& _category)
: begin(0), cumulative(0), symbol(_symbol), category(_category) {}
timing_t(const std::string& _symbol)
: begin(0), cumulative(0), symbol(_symbol) {}
~timing_t() {
std::string cls = "timing.results.";
cls += symbol;
@ -33,6 +36,9 @@ class timing_t
line = _line;
begin = std::clock();
}
void start() {
begin = std::clock();
}
void stop() {
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);
break;
case value_t::DATETIME:
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
return false;
@ -68,8 +69,12 @@ value_expr_t::~value_expr_t()
delete constant_a;
break;
case CONSTANT_I:
case CONSTANT_T:
assert(constant_t);
delete constant_t;
break;
case CONSTANT_I:
case CONSTANT_V:
break;
@ -145,7 +150,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
result = constant_i;
break;
case CONSTANT_T:
result = long(constant_t);
result = *constant_t;
break;
case CONSTANT_A:
result = *constant_a;
@ -155,7 +160,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
case F_NOW:
result = long(terminus);
result = datetime_t(terminus);
break;
case AMOUNT:
@ -262,37 +267,37 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case DATE:
if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date);
result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact)
result = long(details.xact->date());
result = datetime_t(details.xact->date());
else if (details.entry)
result = long(details.entry->date());
result = datetime_t(details.entry->date());
else
result = long(terminus);
result = datetime_t(terminus);
break;
case ACT_DATE:
if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date);
result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact)
result = long(details.xact->actual_date());
result = datetime_t(details.xact->actual_date());
else if (details.entry)
result = long(details.entry->actual_date());
result = datetime_t(details.entry->actual_date());
else
result = long(terminus);
result = datetime_t(terminus);
break;
case EFF_DATE:
if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date)
result = long(transaction_xdata_(*details.xact).date);
result = datetime_t(transaction_xdata_(*details.xact).date);
else if (details.xact)
result = long(details.xact->effective_date());
result = datetime_t(details.xact->effective_date());
else if (details.entry)
result = long(details.entry->effective_date());
result = datetime_t(details.entry->effective_date());
else
result = long(terminus);
result = datetime_t(terminus);
break;
case CLEARED:
@ -373,11 +378,40 @@ void value_expr_t::compute(value_t& result, const details_t& details,
index = 0;
expr = find_leaf(context, 1, index);
amount_t moment;
if (compute_amount(expr, moment, NULL, context))
value_t moment;
expr->compute(moment, details, context);
if (moment.type == value_t::DATETIME) {
result.cast(value_t::INTEGER);
moment.cast(value_t::INTEGER);
result -= moment;
else
} else {
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;
}
@ -580,13 +614,12 @@ void value_expr_t::compute(value_t& result, const details_t& details,
index = 0;
expr = find_leaf(context, 1, index);
amount_t moment;
if (compute_amount(expr, moment, details.xact, context))
result = result.value((long)moment);
else
value_t moment;
expr->compute(moment, details, context);
if (moment.type != value_t::DATETIME)
throw compute_error("Invalid date passed to P(value,date)");
result = result.value(*((datetime_t *)moment.data));
break;
}
@ -756,14 +789,18 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
c = peek_next_nonws(in);
}
bool definition = false;
if (c == '=') {
in.get(c);
if (peek_next_nonws(in) == '=') {
in.unget();
c = '\0';
goto parsed; // parse this as == operator
} else {
definition = true;
}
}
if (definition) {
std::auto_ptr<scope_t> params(new scope_t(scope));
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));
interval_t timespan(buf);
node->constant_t = timespan.first();
node->constant_t = new datetime_t(timespan.first());
break;
}
@ -1350,6 +1387,28 @@ void init_value_expr()
node->set_right(new value_expr_t(value_expr_t::F_DATECMP));
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
node = parse_value_expr("P(a,d)");
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;
break;
case value_expr_t::CONSTANT_T:
out << "CONSTANT_T - [" << node->constant_t << ']';
out << "CONSTANT_T - [" << *(node->constant_t) << ']';
break;
case value_expr_t::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_PRICE: out << "F_PRICE"; 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_ARG: out << "O_ARG"; break;

View file

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

74
value.h
View file

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

View file

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