Added a DATETIME value type.
This commit is contained in:
parent
d02f74efea
commit
b737cd8e6d
20 changed files with 994 additions and 392 deletions
|
|
@ -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
53
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:
|
||||
|
||||
|
|
|
|||
58
amount.cc
58
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 << ')';
|
||||
|
|
|
|||
14
amount.h
14
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,
|
||||
|
|
|
|||
209
balance.cc
209
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,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
139
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) {
|
||||
|
|
|
|||
79
config.cc
79
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
|
||||
|
|
|
|||
29
config.h
29
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
74
datetime.h
74
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];
|
||||
|
|
|
|||
32
format.cc
32
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);
|
||||
|
|
|
|||
6
format.h
6
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");
|
||||
}
|
||||
|
|
|
|||
123
main.cc
123
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,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;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ namespace {
|
|||
|
||||
startup::~startup()
|
||||
{
|
||||
if (! ledger::do_cleanup)
|
||||
return;
|
||||
shutdown_parser_support();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
timing.h
6
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;
|
||||
|
|
|
|||
115
valexpr.cc
115
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;
|
||||
|
|
|
|||
|
|
@ -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
362
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 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
74
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;
|
||||
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;
|
||||
|
|
|
|||
2
walk.cc
2
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));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue