summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--amount.cc19
-rw-r--r--amount.h17
-rw-r--r--commodity.cc69
-rw-r--r--commodity.h12
-rw-r--r--journal.cc348
-rw-r--r--journal.h10
-rw-r--r--main.cc5
-rw-r--r--report.cc2
-rw-r--r--valexpr.cc4
-rw-r--r--value.cc73
-rw-r--r--value.h1
11 files changed, 379 insertions, 181 deletions
diff --git a/amount.cc b/amount.cc
index 4fa57b3c..73d75c18 100644
--- a/amount.cc
+++ b/amount.cc
@@ -855,19 +855,18 @@ bool amount_t::commodity_annotated() const
return commodity().annotated;
}
-annotation_t amount_t::annotation_details() const
+annotation_t& amount_t::annotation_details()
{
if (! quantity)
throw_(amount_error,
"Cannot return commodity annotation details of an uninitialized amount");
- assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
+ if (! commodity().is_annotated())
+ throw_(amount_error,
+ "Request for annotation details from an unannotated amount");
- if (commodity().annotated) {
- annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
- return ann_comm.details;
- }
- return annotation_t();
+ annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
+ return ann_comm.details;
}
amount_t amount_t::strip_annotations(const bool _keep_price,
@@ -1097,8 +1096,10 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
{
assert(valid());
- if (! quantity)
- throw_(amount_error, "Cannot write out an uninitialized amount");
+ if (! quantity) {
+ _out << "<null>";
+ return;
+ }
amount_t base(*this);
if (! amount_t::keep_base)
diff --git a/amount.h b/amount.h
index 665dabe9..648965c7 100644
--- a/amount.h
+++ b/amount.h
@@ -538,12 +538,17 @@ public:
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
* have been set to (which all default to false).
*/
- void annotate_commodity(const annotation_t& details);
- bool commodity_annotated() const;
- annotation_t annotation_details() 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 annotate_commodity(const annotation_t& details);
+ bool commodity_annotated() const;
+
+ annotation_t& annotation_details();
+ const annotation_t& annotation_details() const {
+ return const_cast<amount_t&>(*this).annotation_details();
+ }
+
+ amount_t strip_annotations(const bool _keep_price = keep_price,
+ const bool _keep_date = keep_date,
+ const bool _keep_tag = keep_tag) const;
/**
* Parsing methods. The method `parse' is used to parse an amount
diff --git a/commodity.cc b/commodity.cc
index 3bb3ec72..37c4fab9 100644
--- a/commodity.cc
+++ b/commodity.cc
@@ -118,6 +118,75 @@ optional<amount_t> commodity_t::value(const optional<datetime_t>& moment)
return price;
}
+amount_t commodity_t::exchange(const amount_t& amount,
+ amount_t& final_cost, // out
+ amount_t& basis_cost, // out
+ const optional<amount_t>& total_cost_,
+ const optional<amount_t>& per_unit_cost_,
+ const optional<datetime_t>& moment,
+ const optional<string>& tag)
+{
+ // (assert (or (and total-cost (not per-unit-cost))
+ // (and per-unit-cost (not total-cost))))
+
+ assert((total_cost_ && ! per_unit_cost_) || (per_unit_cost_ && ! total_cost_));
+
+ // (let* ((commodity (amount-commodity amount))
+ // (current-annotation
+ // (and (annotated-commodity-p commodity)
+ // (commodity-annotation commodity)))
+ // (base-commodity (if (annotated-commodity-p commodity)
+ // (get-referent commodity)
+ // commodity))
+ // (per-unit-cost (or per-unit-cost
+ // (divide total-cost amount)))
+ // (total-cost (or total-cost
+ // (multiply per-unit-cost amount))))
+
+ commodity_t& commodity(amount.commodity());
+
+ annotation_t * current_annotation = NULL;
+ if (commodity.annotated)
+ current_annotation = &as_annotated_commodity(commodity).details;
+
+ commodity_t& base_commodity
+ (current_annotation ?
+ as_annotated_commodity(commodity).referent() : commodity);
+
+ amount_t per_unit_cost(per_unit_cost_ ?
+ *per_unit_cost_ : *total_cost_ / amount);
+ final_cost = total_cost_ ? *total_cost_ : *per_unit_cost_ * amount;
+
+ // Add a price history entry for this conversion if we know when it took
+ // place
+
+ // (if (and moment (not (commodity-no-market-price-p base-commodity)))
+ // (add-price base-commodity per-unit-cost moment))
+
+ if (moment && ! commodity.has_flags(COMMODITY_STYLE_NOMARKET))
+ base_commodity.add_price(*moment, per_unit_cost);
+
+ // ;; returns: ANNOTATED-AMOUNT TOTAL-COST BASIS-COST
+ // (values (annotate-commodity
+ // amount
+ // (make-commodity-annotation :price per-unit-cost
+ // :date moment
+ // :tag tag))
+ // total-cost
+ // (if current-annotation
+ // (multiply (annotation-price current-annotation) amount)
+ // total-cost))))
+
+ if (current_annotation && current_annotation->price)
+ basis_cost = *current_annotation->price * amount;
+ else
+ basis_cost = final_cost;
+
+ amount_t ann_amount(amount);
+ ann_amount.annotate_commodity(annotation_t(per_unit_cost, moment, tag));
+ return ann_amount;
+}
+
commodity_t::operator bool() const
{
return this != parent().null_commodity;
diff --git a/commodity.h b/commodity.h
index 5a8df20b..7bac7c18 100644
--- a/commodity.h
+++ b/commodity.h
@@ -117,6 +117,10 @@ public:
operator bool() const;
+ bool is_annotated() const {
+ return annotated;
+ }
+
virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated)
return comm == *this;
@@ -185,6 +189,14 @@ public:
optional<amount_t> value(const optional<datetime_t>& moment = none);
+ static amount_t exchange(const amount_t& amount,
+ amount_t& final_cost, // out
+ amount_t& basis_cost, // out
+ const optional<amount_t>& total_cost,
+ const optional<amount_t>& per_unit_cost = none,
+ const optional<datetime_t>& moment = none,
+ const optional<string>& tag = none);
+
static void parse_symbol(std::istream& in, string& symbol);
static void parse_symbol(char *& p, string& symbol);
static string parse_symbol(std::istream& in) {
diff --git a/journal.cc b/journal.cc
index 142eb799..8e99e96b 100644
--- a/journal.cc
+++ b/journal.cc
@@ -118,185 +118,249 @@ bool entry_base_t::remove_transaction(transaction_t * xact)
bool entry_base_t::finalize()
{
- // Scan through and compute the total balance for the entry. This
- // is used for auto-calculating the value of entries with no cost,
- // and the per-unit price of unpriced commodities.
-
- value_t balance;
- bool no_amounts = true;
- bool saw_null = false;
+ // Scan through and compute the total balance for the entry. This is used
+ // for auto-calculating the value of entries with no cost, and the per-unit
+ // price of unpriced commodities.
+
+ // (let ((balance 0)
+ // null-xact)
+
+ value_t balance;
+ transaction_t * null_xact = NULL;
+
+ // (do-transactions (xact entry)
+ // (when (xact-must-balance-p xact)
+ // (let ((amt (xact-amount* xact)))
+ // (if amt
+ // (setf balance (add balance (or (xact-cost xact) amt)))
+ // (if null-xact
+ // (error "Only one transaction with null amount allowed ~
+ // per entry (beg ~S end ~S)"
+ // (item-position-begin-line (entry-position entry))
+ // (item-position-end-line (entry-position entry)))
+ // (setf null-xact xact))))))
+ //
for (transactions_list::const_iterator x = transactions.begin();
x != transactions.end();
x++) {
- if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
- (*x)->has_flags(TRANSACTION_BALANCE)) {
+ if ((*x)->must_balance()) {
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
if (! p.is_null()) {
- if (no_amounts) {
+ if (balance.is_null())
balance = p;
- no_amounts = false;
- } else {
+ else
balance += p;
- }
-
- assert(! (*x)->amount.is_null());
-
- if ((*x)->cost && (*x)->amount.commodity().annotated) {
- annotated_commodity_t&
- ann_comm(static_cast<annotated_commodity_t&>
- ((*x)->amount.commodity()));
- if (ann_comm.details.price)
- balance += (*ann_comm.details.price * (*x)->amount.number() -
- *((*x)->cost));
- }
} else {
- saw_null = true;
+ if (null_xact)
+ throw_(std::logic_error,
+ "Only one transaction with null amount allowed per entry");
+ else
+ null_xact = *x;
}
}
}
-
assert(balance.valid());
- // If it's a null entry, then let the user have their fun
- if (no_amounts)
- return true;
+ DEBUG("ledger.journal.finalize", "initial balance = " << balance);
- // If there is only one transaction, balance against the basket
- // account if one has been set.
+ // If there is only one transaction, balance against the default account if
+ // one has been set.
+
+ // (when (= 1 (length (entry-transactions entry)))
+ // (if-let ((default-account
+ // (journal-default-account (entry-journal entry))))
+ // (setf null-xact
+ // (make-transaction :entry entry
+ // :status (xact-status
+ // (first (entry-transactions entry)))
+ // :account default-account
+ // :generatedp t))
+ // (add-transaction entry null-xact)))
if (journal && journal->basket && transactions.size() == 1) {
- assert(balance.is_amount());
- transaction_t * nxact = new transaction_t(journal->basket);
- // The amount doesn't need to be set because the code below will
- // balance this transaction against the other.
- add_transaction(nxact);
- nxact->add_flags(TRANSACTION_CALCULATED);
+ // jww (2008-07-24): Need to make the rest of the code aware of what to do
+ // when it sees a generated transaction.
+ null_xact = new transaction_t(journal->basket, TRANSACTION_GENERATED);
+ null_xact->state = (*transactions.begin())->state;
+ add_transaction(null_xact);
}
- // If the first transaction of a two-transaction entry is of a
- // different commodity than the other, and it has no per-unit price,
- // determine its price by dividing the unit count into the value of
- // the balance. This is done for the last eligible commodity.
+ if (null_xact != NULL) {
+ // If one transaction has no value at all, its value will become the
+ // inverse of the rest. If multiple commodities are involved, multiple
+ // transactions are generated to balance them all.
+
+ // (progn
+ // (if (balance-p balance)
+ // (let ((first t))
+ // (dolist (amount (balance-amounts balance))
+ // (if first
+ // (setf (xact-amount* null-xact) (negate amount)
+ // first nil)
+ // (add-transaction
+ // entry
+ // (make-transaction :entry entry
+ // :account (xact-account null-xact)
+ // :amount (negate amount)
+ // :generatedp t)))))
+ // (setf (xact-amount* null-xact) (negate balance)
+ // (xact-calculatedp null-xact) t))
+ //
+ // (setf balance 0))
+
+ if (balance.is_balance()) {
+ bool first = true;
+ const balance_t& bal(balance.as_balance());
+ for (balance_t::amounts_map::const_iterator i = bal.amounts.begin();
+ i != bal.amounts.end();
+ i++) {
+ if (first) {
+ null_xact->amount = (*i).second.negate();
+ first = false;
+ } else {
+ add_transaction(new transaction_t(null_xact->account,
+ (*i).second.negate(),
+ TRANSACTION_GENERATED));
+ }
+ }
+ } else {
+ null_xact->amount = balance.as_amount().negate();
+ null_xact->add_flags(TRANSACTION_CALCULATED);
+ }
+ balance = NULL_VALUE;
+
+ }
+ else if (balance.is_balance() &&
+ balance.as_balance().amounts.size() == 2) {
+ // When an entry involves two different commodities (regardless of how
+ // many transactions there are) determine the conversion ratio by dividing
+ // the total value of one commodity by the total value of the other. This
+ // establishes the per-unit cost for this transaction for both
+ // commodities.
+
+ // (when (and (balance-p balance)
+ // (= 2 (balance-commodity-count balance)))
+ // (destructuring-bind (x y) (balance-amounts balance)
+ // (let ((a-commodity (amount-commodity x))
+ // (per-unit-cost (value-abs (divide x y))))
+ // (do-transactions (xact entry)
+ // (let ((amount (xact-amount* xact)))
+ // (unless (or (xact-cost xact)
+ // (not (xact-must-balance-p xact))
+ // (commodity-equal (amount-commodity amount)
+ // a-commodity))
+ // (setf balance (subtract balance amount)
+ // (xact-cost xact) (multiply per-unit-cost amount)
+ // balance (add balance (xact-cost xact))))))))))
- if (! saw_null && balance && balance.is_balance()) {
const balance_t& bal(balance.as_balance());
- if (bal.amounts.size() == 2) {
- transactions_list::const_iterator x = transactions.begin();
- assert(! (*x)->amount.is_null());
- commodity_t& this_comm = (*x)->amount.commodity();
-
- balance_t::amounts_map::const_iterator this_bal =
- bal.amounts.find(&this_comm);
- assert(this_bal != bal.amounts.end());
-
- balance_t::amounts_map::const_iterator other_bal =
- bal.amounts.begin();
-
- if (this_bal == other_bal)
- other_bal++;
-
- amount_t per_unit_cost =
- ((*other_bal).second / (*this_bal).second.number()).unround();
-
- for (; x != transactions.end(); x++) {
- if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
- (*x)->amount.commodity() != this_comm)
- continue;
-
- balance -= (*x)->amount;
-
- entry_t * entry = dynamic_cast<entry_t *>(this);
-
- if ((*x)->amount.commodity() &&
- ! (*x)->amount.commodity().annotated)
- (*x)->amount.annotate_commodity
- (annotation_t(per_unit_cost.abs(),
- entry ? entry->actual_date() : optional<datetime_t>(),
- entry ? entry->code : optional<string>()));
-
- (*x)->cost = - (per_unit_cost * (*x)->amount.number());
- balance += *(*x)->cost;
+
+ balance_t::amounts_map::const_iterator a = bal.amounts.begin();
+
+ const amount_t& x((*a++).second);
+ const amount_t& y((*a++).second);
+
+ if (! y.is_realzero()) {
+ amount_t per_unit_cost = (x / y).abs();
+
+ commodity_t& comm(x.commodity());
+
+ for (transactions_list::const_iterator x = transactions.begin();
+ x != transactions.end();
+ x++) {
+ const amount_t& x_amt((*x)->amount);
+
+ if (! ((*x)->cost ||
+ ! (*x)->must_balance() ||
+ x_amt.commodity() == comm)) {
+ DEBUG("ledger.journal.finalize", "before operation 1 = " << balance);
+ balance -= x_amt;
+ DEBUG("ledger.journal.finalize", "after operation 1 = " << balance);
+ DEBUG("ledger.journal.finalize", "x_amt = " << x_amt);
+ DEBUG("ledger.journal.finalize", "per_unit_cost = " << per_unit_cost);
+
+ (*x)->cost = per_unit_cost * x_amt;
+ DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost);
+
+ balance += *(*x)->cost;
+ DEBUG("ledger.journal.finalize", "after operation 2 = " << balance);
+ }
+
}
}
- }
- // Walk through each of the transactions, fixing up any that we
- // can, and performing any on-the-fly calculations.
+ DEBUG("ledger.journal.finalize", "resolved balance = " << balance);
+ }
- bool empty_allowed = true;
+ // Now that the transaction list has its final form, calculate the balance
+ // once more in terms of total cost, accounting for any possible gain/loss
+ // amounts.
+
+ // (do-transactions (xact entry)
+ // (when (xact-cost xact)
+ // (let ((amount (xact-amount* xact)))
+ // (assert (not (commodity-equal (amount-commodity amount)
+ // (amount-commodity (xact-cost xact)))))
+ // (multiple-value-bind (annotated-amount total-cost basis-cost)
+ // (exchange-commodity amount :total-cost (xact-cost xact)
+ // :moment (entry-date entry)
+ // :tag (entry-code entry))
+ // (if (annotated-commodity-p (amount-commodity amount))
+ // (if-let ((price (annotation-price
+ // (commodity-annotation
+ // (amount-commodity amount)))))
+ // (setf balance
+ // (add balance (subtract basis-cost total-cost))))
+ // (setf (xact-amount* xact) annotated-amount))))))
for (transactions_list::const_iterator x = transactions.begin();
x != transactions.end();
x++) {
- if (! (*x)->amount.is_null() ||
- ((*x)->has_flags(TRANSACTION_VIRTUAL) &&
- ! (*x)->has_flags(TRANSACTION_BALANCE)))
- continue;
-
- if (! empty_allowed)
- throw_(std::logic_error,
- "Only one transaction with null amount allowed per entry");
- empty_allowed = false;
-
- // If one transaction gives no value at all, its value will become
- // the inverse of the value of the others. If multiple
- // commodities are involved, multiple transactions will be
- // generated to balance them all.
-
- const balance_t * bal = NULL;
- switch (balance.type()) {
- case value_t::BALANCE_PAIR:
- bal = &balance.as_balance_pair().quantity();
- // fall through...
-
- case value_t::BALANCE:
- if (! bal)
- bal = &balance.as_balance();
-
- if (bal->amounts.size() < 2) {
- balance.cast(value_t::AMOUNT);
- } else {
- bool first = true;
- for (balance_t::amounts_map::const_iterator
- i = bal->amounts.begin();
- i != bal->amounts.end();
- i++) {
- amount_t amt = (*i).second.negate();
-
- if (first) {
- (*x)->amount = amt;
- first = false;
- } else {
- transaction_t * nxact = new transaction_t((*x)->account);
- add_transaction(nxact);
- nxact->add_flags(TRANSACTION_CALCULATED);
- nxact->amount = amt;
- }
-
- balance += amt;
+ if ((*x)->cost) {
+ const amount_t& x_amt((*x)->amount);
+
+ assert(x_amt.commodity() != (*x)->cost->commodity());
+
+ entry_t * entry = dynamic_cast<entry_t *>(this);
+
+ // jww (2008-07-24): Pass the entry's code here if we can, as the
+ // auto-tag
+ amount_t final_cost;
+ amount_t basis_cost;
+ amount_t ann_amount =
+ commodity_t::exchange(x_amt, final_cost, basis_cost,
+ (*x)->cost, none, (*x)->actual_date(),
+ entry ? entry->code : optional<string>());
+
+ if ((*x)->amount.commodity_annotated()) {
+ if (ann_amount.annotation_details().price) {
+ if (balance.is_null())
+ balance = basis_cost - final_cost;
+ else
+ balance += basis_cost - final_cost;
}
- break;
+ } else {
+ (*x)->amount = ann_amount;
}
- // fall through...
-
- case value_t::AMOUNT:
- (*x)->amount = balance.as_amount().negate();
- (*x)->add_flags(TRANSACTION_CALCULATED);
-
- balance += (*x)->amount;
- break;
-
- default:
- break;
}
}
- if (balance) {
+ DEBUG("ledger.journal.finalize", "final balance = " << balance);
+
+ // (if (value-zerop balance)
+ // (prog1
+ // entry
+ // (setf (entry-normalizedp entry) t))
+ // (error "Entry does not balance (beg ~S end ~S); remaining balance is:~%~A"
+ // (item-position-begin-line (entry-position entry))
+ // (item-position-end-line (entry-position entry))
+ // (format-value balance :width 20)))
+
+ if (! balance.is_null() && ! balance.is_zero()) {
error * err =
new balance_error("Entry does not balance",
new entry_context(*this, "While balancing entry:"));
- DEBUG("ledger.journal.unbalanced_remainder", "balance = " << balance);
balance.round();
err->context.push_front
(new value_context(balance, "Unbalanced remainder is:"));
diff --git a/journal.h b/journal.h
index 48b59d29..305d2961 100644
--- a/journal.h
+++ b/journal.h
@@ -46,6 +46,7 @@ namespace ledger {
#define TRANSACTION_AUTO 0x0004
#define TRANSACTION_BULK_ALLOC 0x0008
#define TRANSACTION_CALCULATED 0x0010
+#define TRANSACTION_GENERATED 0x0020
class entry_t;
class account_t;
@@ -73,8 +74,9 @@ class transaction_t : public supports_flags<>
mutable void * data;
static bool use_effective_date;
- transaction_t(account_t * _account = NULL)
- : supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
+ transaction_t(account_t * _account = NULL,
+ unsigned int _flags = TRANSACTION_NORMAL)
+ : supports_flags<>(_flags), entry(NULL),
state(UNCLEARED), account(_account),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
{
@@ -120,6 +122,10 @@ class transaction_t : public supports_flags<>
return actual_date();
}
+ bool must_balance() const {
+ return ! has_flags(TRANSACTION_VIRTUAL) || has_flags(TRANSACTION_BALANCE);
+ }
+
bool valid() const;
};
diff --git a/main.cc b/main.cc
index 0bea426c..28b8edc3 100644
--- a/main.cc
+++ b/main.cc
@@ -205,12 +205,15 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
journal_t& journal(*session.create_journal());
- if (! session.read_data(journal, report.account))
+ std::size_t count = session.read_data(journal, report.account);
+ if (count == 0)
throw_(parse_error, "Failed to locate any journal entries; "
"did you specify a valid file with -f?");
INFO_FINISH(journal);
+ INFO("Found " << count << " entries");
+
TRACE_FINISH(entry_text, 1);
TRACE_FINISH(entry_date, 1);
TRACE_FINISH(entry_details, 1);
diff --git a/report.cc b/report.cc
index 8d2776e4..cca3a4b2 100644
--- a/report.cc
+++ b/report.cc
@@ -73,7 +73,7 @@ value_t report_t::abbrev(expr::call_scope_t& args)
return value_t(abbreviate(str, wid, style, true,
static_cast<int>(abbrev_len)), true);
#else
- return value_t();
+ return NULL_VALUE;
#endif
}
diff --git a/valexpr.cc b/valexpr.cc
index 2c087aa3..645cfec3 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -87,7 +87,7 @@ value_t scope_t::resolve(const string& name) {
if (definition)
return definition->calc(*this);
else
- return value_t();
+ return NULL_VALUE;
}
void symbol_scope_t::define(const string& name, ptr_op_t def)
@@ -135,7 +135,7 @@ namespace {
temp.reset(expr);
} else {
temp.reset(new op_t(op_t::VALUE));
- temp->set_value(value_t());
+ temp->set_value(NULL_VALUE);
expr->compute(temp->as_value_lval(), details, context);
}
} else {
diff --git a/value.cc b/value.cc
index 85f9f49a..8051a794 100644
--- a/value.cc
+++ b/value.cc
@@ -298,7 +298,9 @@ void value_t::in_place_simplify()
if (is_balance() && as_balance().amounts.size() == 1) {
DEBUG_("Reducing balance to amount");
+ DEBUG("ledger.value.reduce", "as a balance it looks like: " << *this);
in_place_cast(AMOUNT);
+ DEBUG("ledger.value.reduce", "as an amount it looks like: " << *this);
}
#if 0
@@ -445,7 +447,7 @@ value_t& value_t::operator+=(const value_t& val)
break;
}
- throw_(value_error, "Cannot add " << label() << " to " << val.label());
+ throw_(value_error, "Cannot add " << val.label() << " to " << label());
return *this;
}
@@ -605,7 +607,7 @@ value_t& value_t::operator-=(const value_t& val)
break;
}
- throw_(value_error, "Cannot subtract " << label() << " from " << val.label());
+ throw_(value_error, "Cannot subtract " << val.label() << " from " << label());
return *this;
}
@@ -952,8 +954,7 @@ bool value_t::operator>(const value_t& val) const
break;
}
- throw_(value_error,
- "Cannot compare " << label() << " to " << val.label());
+ throw_(value_error, "Cannot compare " << label() << " to " << val.label());
return *this;
}
@@ -1019,13 +1020,13 @@ void value_t::in_place_cast(type_t cast_type)
if (amt.is_null())
set_balance(balance_t());
else
- set_balance(as_amount());
+ set_balance(as_amount()); // creates temporary
return;
case BALANCE_PAIR:
if (amt.is_null())
set_balance_pair(balance_pair_t());
else
- set_balance_pair(as_amount());
+ set_balance_pair(as_amount()); // creates temporary
return;
case STRING:
if (amt.is_null())
@@ -1044,7 +1045,10 @@ void value_t::in_place_cast(type_t cast_type)
case AMOUNT: {
const balance_t& temp(as_balance());
if (temp.amounts.size() == 1) {
- set_amount((*temp.amounts.begin()).second);
+ // Because we are changing the current balance value to an amount
+ // value, and because set_amount takes a reference (and that memory is
+ // about to be repurposed), we must pass in a copy.
+ set_amount(amount_t((*temp.amounts.begin()).second));
return;
}
else if (temp.amounts.size() == 0) {
@@ -1070,7 +1074,7 @@ void value_t::in_place_cast(type_t cast_type)
case AMOUNT: {
const balance_t& temp(as_balance_pair().quantity());
if (temp.amounts.size() == 1) {
- set_amount((*temp.amounts.begin()).second);
+ set_amount(amount_t((*temp.amounts.begin()).second));
return;
}
else if (temp.amounts.size() == 0) {
@@ -1084,7 +1088,9 @@ void value_t::in_place_cast(type_t cast_type)
break;
}
case BALANCE:
- set_balance(as_balance_pair().quantity());
+ // A temporary is required, becaues set_balance is going to wipe us out
+ // before assigned the value passed in.
+ set_balance(balance_t(as_balance_pair().quantity()));
return;
default:
break;
@@ -1176,6 +1182,37 @@ bool value_t::is_realzero() const
return true;
}
+bool value_t::is_zero() const
+{
+ switch (type()) {
+ case BOOLEAN:
+ return ! as_boolean();
+ case INTEGER:
+ return as_long() == 0;
+ case DATETIME:
+ return ! is_valid(as_datetime());
+ case AMOUNT:
+ return as_amount().is_zero();
+ case BALANCE:
+ return as_balance().is_zero();
+ case BALANCE_PAIR:
+ return as_balance_pair().is_zero();
+ case STRING:
+ return as_string().empty();
+ case SEQUENCE:
+ return as_sequence().empty();
+
+ case POINTER:
+ return as_any_pointer().empty();
+
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return true;
+}
+
value_t value_t::value(const optional<datetime_t>& moment) const
{
switch (type()) {
@@ -1204,7 +1241,7 @@ value_t value_t::value(const optional<datetime_t>& moment) const
}
throw_(value_error, "Cannot find the value of " << label());
- return value_t();
+ return NULL_VALUE;
}
void value_t::in_place_reduce()
@@ -1248,7 +1285,7 @@ value_t value_t::abs() const
}
throw_(value_error, "Cannot abs " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::round() const
@@ -1267,7 +1304,7 @@ value_t value_t::round() const
}
throw_(value_error, "Cannot round " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::unround() const
@@ -1282,7 +1319,7 @@ value_t value_t::unround() const
}
throw_(value_error, "Cannot unround " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::annotated_price() const
@@ -1300,7 +1337,7 @@ value_t value_t::annotated_price() const
}
throw_(value_error, "Cannot find the annotated price of " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::annotated_date() const
@@ -1321,7 +1358,7 @@ value_t value_t::annotated_date() const
}
throw_(value_error, "Cannot find the annotated date of " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::annotated_tag() const
@@ -1342,7 +1379,7 @@ value_t value_t::annotated_tag() const
}
throw_(value_error, "Cannot find the annotated tag of " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::strip_annotations(const bool keep_price,
@@ -1378,7 +1415,7 @@ value_t value_t::strip_annotations(const bool keep_price,
break;
}
assert(false);
- return value_t();
+ return NULL_VALUE;
}
value_t value_t::cost() const
@@ -1401,7 +1438,7 @@ value_t value_t::cost() const
}
throw_(value_error, "Cannot find the cost of " << label());
- return value_t();
+ return NULL_VALUE;
}
value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
diff --git a/value.h b/value.h
index 54f8a502..86b07041 100644
--- a/value.h
+++ b/value.h
@@ -400,6 +400,7 @@ public:
operator bool() const;
bool is_realzero() const;
+ bool is_zero() const;
bool is_null() const {
if (! storage) {
return true;