summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2006-03-06 03:53:36 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 02:41:29 -0400
commitb737cd8e6dd185beeae902caa4eee6c4cee8bc36 (patch)
tree967b198e0d52c8d0f2ae2215c90224a6ff48194d
parentd02f74efea3e6b631810bfd3c3d8adcaa4299902 (diff)
downloadfork-ledger-b737cd8e6dd185beeae902caa4eee6c4cee8bc36.tar.gz
fork-ledger-b737cd8e6dd185beeae902caa4eee6c4cee8bc36.tar.bz2
fork-ledger-b737cd8e6dd185beeae902caa4eee6c4cee8bc36.zip
Added a DATETIME value type.
-rw-r--r--Makefile.am2
-rw-r--r--NEWS53
-rw-r--r--amount.cc58
-rw-r--r--amount.h14
-rw-r--r--balance.cc205
-rw-r--r--balance.h139
-rw-r--r--config.cc79
-rw-r--r--config.h29
-rw-r--r--datetime.cc2
-rw-r--r--datetime.h74
-rw-r--r--format.cc32
-rw-r--r--format.h6
-rw-r--r--main.cc123
-rw-r--r--startup.cc2
-rw-r--r--timing.h6
-rw-r--r--valexpr.cc115
-rw-r--r--valexpr.h5
-rw-r--r--value.cc360
-rw-r--r--value.h72
-rw-r--r--walk.cc2
20 files changed, 990 insertions, 388 deletions
diff --git a/Makefile.am b/Makefile.am
index c3acc1a1..ba96fd9b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/NEWS b/NEWS
index 8f1b5c5b..e3dbbcbe 100644
--- a/NEWS
+++ b/NEWS
@@ -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:
diff --git a/amount.cc b/amount.cc
index 31187a1c..f3b6a801 100644
--- a/amount.cc
+++ b/amount.cc
@@ -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 << ')';
diff --git a/amount.h b/amount.h
index 017f5472..360da8f9 100644
--- a/amount.h
+++ b/amount.h
@@ -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,
diff --git a/balance.cc b/balance.cc
index 5cfd88ca..a68f4ffe 100644
--- a/balance.cc
+++ b/balance.cc
@@ -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,42 +188,148 @@ 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 balance_t& bal)
+balance_t& balance_t::operator*=(const amount_t& amt)
{
- if (! *this) {
- return (*this = 0L);
+ 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 if (! bal) {
+ 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 (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 << "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 << "It makes no sense to divide two balances: "
- << *this << " / " << bal;
+ 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
@@ -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)
- .add_property("cost",
- make_getter(&balance_pair_t::cost,
- return_value_policy<reference_existing_object>()))
+ .def("valid", &balance_pair_t::valid)
- .def("negate", &balance_pair_t::negate)
+ .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)
- .def("valid", &balance_pair_t::valid)
+ .add_property("cost",
+ make_getter(&balance_pair_t::cost,
+ return_value_policy<reference_existing_object>()))
;
}
diff --git a/balance.h b/balance.h
index edc3c19c..f2d78233 100644
--- a/balance.h
+++ b/balance.h
@@ -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) {
diff --git a/config.cc b/config.cc
index b53c8110..9016e9f3 100644
--- a/config.cc
+++ b/config.cc
@@ -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
diff --git a/config.h b/config.h
index bad96c77..f347288c 100644
--- a/config.h
+++ b/config.h
@@ -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
diff --git a/datetime.cc b/datetime.cc
index cf26d0c9..f5d071c3 100644
--- a/datetime.cc
+++ b/datetime.cc
@@ -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;
diff --git a/datetime.h b/datetime.h
index fb3751bf..de4a39b9 100644
--- a/datetime.h
+++ b/datetime.h
@@ -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];
diff --git a/format.cc b/format.cc
index 82662184..33613442 100644
--- a/format.cc
+++ b/format.cc
@@ -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);
diff --git a/format.h b/format.h
index 60f94e65..a4620aeb 100644
--- a/format.h
+++ b/format.h
@@ -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");
}
diff --git a/main.cc b/main.cc
index dd34d7a7..62ca12df 100644
--- a/main.cc
+++ b/main.cc
@@ -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,21 +146,19 @@ 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.");
- // process the command word and its following arguments
+ TRACE_POP(parser, "Finished parsing"); }
- TIMER_START(process);
+ // process the command word and its following arguments
std::string first_arg;
if (command == "w") {
@@ -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();
- }
- TIMER_STOP(walk);
-
- TIMER_START(cleanup);
+ TRACE_POP(main, "Finished account walk");
+ }
#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;
diff --git a/startup.cc b/startup.cc
index ad462e36..9e35adfb 100644
--- a/startup.cc
+++ b/startup.cc
@@ -49,6 +49,8 @@ namespace {
startup::~startup()
{
+ if (! ledger::do_cleanup)
+ return;
shutdown_parser_support();
}
}
diff --git a/timing.h b/timing.h
index 9a80df2f..7e1029ea 100644
--- a/timing.h
+++ b/timing.h
@@ -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;
diff --git a/valexpr.cc b/valexpr.cc
index 88136ee7..371f94eb 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -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;
diff --git a/valexpr.h b/valexpr.h
index fc16a5d1..7a029650 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -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;
diff --git a/value.cc b/value.cc
index 6bd66b6d..1ae88776 100644
--- a/value.cc
+++ b/value.cc
@@ -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 AMOUNT:
+ case DATETIME:
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);
+ 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 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)
diff --git a/value.h b/value.h
index 9c14ed10..22110b43 100644
--- a/value.h
+++ b/value.h
@@ -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;
- value_t& add(const amount_t& amount, const amount_t * cost = NULL);
+ void abs();
+ void cast(type_t cast_type);
+ value_t cost() const;
+ value_t price() const;
+ value_t date() const;
- 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);
- }
- }
+ 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;
- 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& add(const amount_t& amount, const amount_t * cost = NULL);
+ 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;
diff --git a/walk.cc b/walk.cc
index 300988a1..4d5ff012 100644
--- a/walk.cc
+++ b/walk.cc
@@ -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));