summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.cc13
-rw-r--r--src/account.h1
-rw-r--r--src/amount.cc46
-rw-r--r--src/amount.h11
-rw-r--r--src/annotate.cc158
-rw-r--r--src/annotate.h62
-rw-r--r--src/balance.cc14
-rw-r--r--src/balance.h6
-rw-r--r--src/chain.cc16
-rw-r--r--src/chain.h3
-rw-r--r--src/commodity.cc123
-rw-r--r--src/commodity.h49
-rw-r--r--src/csv.cc4
-rw-r--r--src/draft.cc13
-rw-r--r--src/expr.cc112
-rw-r--r--src/expr.h126
-rw-r--r--src/exprbase.h6
-rw-r--r--src/filters.cc15
-rw-r--r--src/filters.h20
-rw-r--r--src/format.cc4
-rw-r--r--src/global.cc3
-rw-r--r--src/global.h3
-rw-r--r--src/history.cc218
-rw-r--r--src/history.h132
-rw-r--r--src/item.cc26
-rw-r--r--src/iterators.h2
-rw-r--r--src/journal.cc15
-rw-r--r--src/journal.h7
-rw-r--r--src/main.cc17
-rw-r--r--src/op.cc398
-rw-r--r--src/op.h27
-rw-r--r--src/option.h78
-rw-r--r--src/parser.cc48
-rw-r--r--src/parser.h2
-rw-r--r--src/pool.cc174
-rw-r--r--src/pool.h53
-rw-r--r--src/post.cc49
-rw-r--r--src/post.h6
-rw-r--r--src/predicate.cc40
-rw-r--r--src/print.cc17
-rw-r--r--src/py_amount.cc6
-rw-r--r--src/py_balance.cc8
-rw-r--r--src/py_commodity.cc18
-rw-r--r--src/py_value.cc9
-rw-r--r--src/pyinterp.cc3
-rw-r--r--src/pyinterp.h4
-rw-r--r--src/query.h3
-rw-r--r--src/quotes.cc2
-rw-r--r--src/quotes.h2
-rw-r--r--src/report.cc435
-rw-r--r--src/report.h746
-rw-r--r--src/scope.cc7
-rw-r--r--src/scope.h41
-rw-r--r--src/series.h135
-rw-r--r--src/session.cc24
-rw-r--r--src/session.h27
-rw-r--r--src/system.hh.in7
-rw-r--r--src/textual.cc134
-rw-r--r--src/timelog.cc2
-rw-r--r--src/times.h6
-rw-r--r--src/token.cc2
-rw-r--r--src/utils.cc3
-rw-r--r--src/utils.h16
-rw-r--r--src/value.cc140
-rw-r--r--src/value.h12
-rw-r--r--src/xact.cc57
-rw-r--r--src/xact.h14
67 files changed, 2330 insertions, 1650 deletions
diff --git a/src/account.cc b/src/account.cc
index 40ddf70b..d772368c 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -89,9 +89,13 @@ account_t * account_t::find_account(const string& acct_name,
if (has_flags(ACCOUNT_GENERATED))
account->add_flags(ACCOUNT_GENERATED);
- std::pair<accounts_map::iterator, bool> result
- = accounts.insert(accounts_map::value_type(first, account));
+#if defined(DEBUG_ON)
+ std::pair<accounts_map::iterator, bool> result =
+#endif
+ accounts.insert(accounts_map::value_type(first, account));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
} else {
account = (*i).second;
}
@@ -137,7 +141,10 @@ void account_t::add_post(post_t * post)
bool account_t::remove_post(post_t * post)
{
- assert(! posts.empty());
+ // It's possible that 'post' wasn't yet in this account, but try to
+ // remove it anyway. This can happen if there is an error during
+ // parsing, when the posting knows what it's account is, but
+ // xact_t::finalize has not yet added that posting to the account.
posts.remove(post);
post->account = NULL;
return true;
diff --git a/src/account.h b/src/account.h
index 8f0f915f..95e04079 100644
--- a/src/account.h
+++ b/src/account.h
@@ -67,6 +67,7 @@ public:
unsigned short depth;
accounts_map accounts;
posts_list posts;
+ optional<expr_t> value_expr;
mutable string _fullname;
diff --git a/src/amount.cc b/src/amount.cc
index 9704dd21..5fa58528 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -120,11 +120,13 @@ namespace {
{
char * buf = NULL;
try {
+#if defined(DEBUG_ON)
IF_DEBUG("amount.convert") {
char * tbuf = mpq_get_str(NULL, 10, quant);
DEBUG("amount.convert", "Rational to convert = " << tbuf);
std::free(tbuf);
}
+#endif
// Convert the rational number to a floating-point, extending the
// floating-point to a large enough size to get a precise answer.
@@ -726,24 +728,24 @@ void amount_t::in_place_unreduce()
}
optional<amount_t>
-amount_t::value(const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of) const
+amount_t::value(const datetime_t& moment,
+ const commodity_t * in_terms_of) const
{
if (quantity) {
#if defined(DEBUG_ON)
- DEBUG("commodity.prices.find",
+ DEBUG("commodity.price.find",
"amount_t::value of " << commodity().symbol());
- if (moment)
- DEBUG("commodity.prices.find",
- "amount_t::value: moment = " << *moment);
+ if (! moment.is_not_a_date_time())
+ DEBUG("commodity.price.find",
+ "amount_t::value: moment = " << moment);
if (in_terms_of)
- DEBUG("commodity.prices.find",
+ DEBUG("commodity.price.find",
"amount_t::value: in_terms_of = " << in_terms_of->symbol());
#endif
if (has_commodity() &&
(in_terms_of || ! commodity().has_flags(COMMODITY_PRIMARY))) {
optional<price_point_t> point;
- optional<commodity_t&> comm(in_terms_of);
+ const commodity_t * comm(in_terms_of);
if (has_annotation() && annotation().price) {
if (annotation().has_flags(ANNOTATION_PRICE_FIXATED)) {
@@ -753,7 +755,7 @@ amount_t::value(const optional<datetime_t>& moment,
"amount_t::value: fixated price = " << point->price);
}
else if (! comm) {
- comm = annotation().price->commodity();
+ comm = annotation().price->commodity_ptr();
}
}
@@ -785,7 +787,7 @@ amount_t::value(const optional<datetime_t>& moment,
return none;
}
-amount_t amount_t::price() const
+optional<amount_t> amount_t::price() const
{
if (has_annotation() && annotation().price) {
amount_t tmp(*annotation().price);
@@ -793,7 +795,7 @@ amount_t amount_t::price() const
DEBUG("amount.price", "Returning price of " << *this << " = " << tmp);
return tmp;
}
- return *this;
+ return none;
}
@@ -865,10 +867,10 @@ bool amount_t::fits_in_long() const
return mpfr_fits_slong_p(tempf, GMP_RNDN);
}
-commodity_t& amount_t::commodity() const
+commodity_t * amount_t::commodity_ptr() const
{
- return (has_commodity() ?
- *commodity_ : *commodity_pool_t::current_pool->null_commodity);
+ return (commodity_ ?
+ commodity_ : commodity_pool_t::current_pool->null_commodity);
}
bool amount_t::has_commodity() const
@@ -1027,12 +1029,12 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
}
// Allocate memory for the amount's quantity value. We have to
- // monitor the allocation in an auto_ptr because this function gets
+ // monitor the allocation in a unique_ptr because this function gets
// called sometimes from amount_t's constructor; and if there is an
// exeception thrown by any of the function calls after this point,
// the destructor will never be called and the memory never freed.
- std::auto_ptr<bigint_t> new_quantity;
+ unique_ptr<bigint_t> new_quantity;
if (quantity) {
if (quantity->refc > 1)
@@ -1058,10 +1060,6 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
if (! commodity_)
commodity_ = commodity_pool_t::current_pool->create(symbol);
assert(commodity_);
-
- if (details)
- commodity_ =
- commodity_pool_t::current_pool->find_or_create(*commodity_, details);
}
// Quickly scan through and verify the correctness of the amount's use of
@@ -1197,6 +1195,14 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
if (! flags.has_flags(PARSE_NO_REDUCE))
in_place_reduce(); // will not throw an exception
+ if (commodity_ && details) {
+ if (details.has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) {
+ assert(details.price);
+ *details.price /= this->abs();
+ }
+ set_commodity(*commodity_pool_t::current_pool->find_or_create(*commodity_, details));
+ }
+
VERIFY(valid());
return true;
diff --git a/src/amount.h b/src/amount.h
index 1db59b7e..903a01cd 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -404,10 +404,10 @@ public:
$100.00.
*/
optional<amount_t>
- value(const optional<datetime_t>& moment = none,
- const optional<commodity_t&>& in_terms_of = none) const;
+ value(const datetime_t& moment = datetime_t(),
+ const commodity_t * in_terms_of = NULL) const;
- amount_t price() const;
+ optional<amount_t> price() const;
/*@}*/
@@ -533,7 +533,10 @@ public:
number() returns a commodity-less version of an amount. This is
useful for accessing just the numeric portion of an amount.
*/
- commodity_t& commodity() const;
+ commodity_t * commodity_ptr() const;
+ commodity_t& commodity() const {
+ return *commodity_ptr();
+ }
bool has_commodity() const;
void set_commodity(commodity_t& comm) {
diff --git a/src/annotate.cc b/src/annotate.cc
index cd1733ca..2b118e76 100644
--- a/src/annotate.cc
+++ b/src/annotate.cc
@@ -33,11 +33,47 @@
#include "amount.h"
#include "commodity.h"
+#include "expr.h"
#include "annotate.h"
#include "pool.h"
namespace ledger {
+bool annotation_t::operator<(const annotation_t& rhs) const
+{
+ if (! price && rhs.price) return true;
+ if (price && ! rhs.price) return false;
+ if (! date && rhs.date) return true;
+ if (date && ! rhs.date) return false;
+ if (! tag && rhs.tag) return true;
+ if (tag && ! rhs.tag) return false;
+
+ if (! value_expr && rhs.value_expr) return true;
+ if (value_expr && ! rhs.value_expr) return false;
+
+ if (price) {
+ if (price->commodity().symbol() < rhs.price->commodity().symbol())
+ return true;
+ if (price->commodity().symbol() > rhs.price->commodity().symbol())
+ return false;
+ if (*price < *rhs.price) return true;
+ if (*price > *rhs.price) return false;
+ }
+ if (date) {
+ if (*date < *rhs.date) return true;
+ if (*date > *rhs.date) return false;
+ }
+ if (tag) {
+ if (*tag < *rhs.tag) return true;
+ if (*tag > *rhs.tag) return false;
+ }
+ if (value_expr) {
+ if (value_expr->text() < rhs.value_expr->text()) return true;
+ if (value_expr->text() > rhs.value_expr->text()) return false;
+ }
+ return false;
+}
+
void annotation_t::parse(std::istream& in)
{
do {
@@ -52,6 +88,12 @@ void annotation_t::parse(std::istream& in)
throw_(amount_error, _("Commodity specifies more than one price"));
in.get(c);
+ c = static_cast<char>(in.peek());
+ if (c == '{') {
+ in.get(c);
+ add_flags(ANNOTATION_PRICE_NOT_PER_UNIT);
+ }
+
c = peek_next_nonws(in);
if (c == '=') {
in.get(c);
@@ -59,10 +101,18 @@ void annotation_t::parse(std::istream& in)
}
READ_INTO(in, buf, 255, c, c != '}');
- if (c == '}')
+ if (c == '}') {
in.get(c);
- else
- throw_(amount_error, _("Commodity price lacks closing brace"));
+ if (has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) {
+ c = static_cast<char>(in.peek());
+ if (c != '}')
+ throw_(amount_error, _("Commodity lot price lacks double closing brace"));
+ else
+ in.get(c);
+ }
+ } else {
+ throw_(amount_error, _("Commodity lot price lacks closing brace"));
+ }
amount_t temp;
temp.parse(buf, PARSE_NO_MIGRATE);
@@ -84,17 +134,46 @@ void annotation_t::parse(std::istream& in)
date = parse_date(buf);
}
else if (c == '(') {
- if (tag)
- throw_(amount_error, _("Commodity specifies more than one tag"));
-
in.get(c);
- READ_INTO(in, buf, 255, c, c != ')');
- if (c == ')')
- in.get(c);
- else
- throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
+ c = static_cast<char>(in.peek());
+ if (c == '@') {
+ in.clear();
+ in.seekg(pos, std::ios::beg);
+ break;
+ }
+ else if (c == '(') {
+ if (value_expr)
+ throw_(amount_error,
+ _("Commodity specifies more than one valuation expresion"));
- tag = buf;
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')') {
+ in.get(c);
+ c = static_cast<char>(in.peek());
+ if (c == ')')
+ in.get(c);
+ else
+ throw_(amount_error,
+ _("Commodity valuation expression lacks closing parentheses"));
+ } else {
+ throw_(amount_error,
+ _("Commodity valuation expression lacks closing parentheses"));
+ }
+
+ value_expr = expr_t(buf);
+ } else {
+ if (tag)
+ throw_(amount_error, _("Commodity specifies more than one tag"));
+
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')')
+ in.get(c);
+ else
+ throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
+
+ tag = buf;
+ }
}
else {
in.clear();
@@ -128,6 +207,10 @@ void annotation_t::print(std::ostream& out, bool keep_base,
if (tag &&
(! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED)))
out << " (" << *tag << ')';
+
+ if (value_expr && (! no_computed_annotations ||
+ ! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED)))
+ out << " ((" << *value_expr << "))";
}
bool keep_details_t::keep_all(const commodity_t& comm) const
@@ -157,6 +240,54 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const
return true;
}
+optional<price_point_t>
+annotated_commodity_t::find_price(const commodity_t * commodity,
+ const datetime_t& moment,
+ const datetime_t& oldest) const
+{
+ DEBUG("commodity.price.find",
+ "annotated_commodity_t::find_price(" << symbol() << ")");
+
+ datetime_t when;
+ if (! moment.is_not_a_date_time())
+ when = moment;
+ else if (epoch)
+ when = *epoch;
+ else
+ when = CURRENT_TIME();
+
+ DEBUG("commodity.price.find", "reference time: " << when);
+
+ const commodity_t * target = NULL;
+ if (commodity)
+ target = commodity;
+
+ if (details.price) {
+ DEBUG("commodity.price.find", "price annotation: " << *details.price);
+
+ if (details.has_flags(ANNOTATION_PRICE_FIXATED)) {
+ DEBUG("commodity.price.find",
+ "amount_t::value: fixated price = " << *details.price);
+ return price_point_t(when, *details.price);
+ }
+ else if (! target) {
+ DEBUG("commodity.price.find", "setting target commodity from price");
+ target = details.price->commodity_ptr();
+ }
+ }
+
+#if defined(DEBUG_ON)
+ if (target)
+ DEBUG("commodity.price.find", "target commodity: " << target->symbol());
+#endif
+
+ if (details.value_expr)
+ return find_price_from_expr(const_cast<expr_t&>(*details.value_expr),
+ commodity, when);
+
+ return commodity_t::find_price(target, moment, oldest);
+}
+
commodity_t&
annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
{
@@ -192,7 +323,8 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
if ((keep_price && details.price) ||
(keep_date && details.date) ||
- (keep_tag && details.tag))
+ (keep_tag && details.tag) ||
+ details.value_expr)
{
new_comm = pool().find_or_create
(referent(), annotation_t(keep_price ? details.price : none,
diff --git a/src/annotate.h b/src/annotate.h
index 3c6db8e8..044ebc4d 100644
--- a/src/annotate.h
+++ b/src/annotate.h
@@ -46,29 +46,39 @@
#ifndef _ANNOTATE_H
#define _ANNOTATE_H
+#include "expr.h"
+
namespace ledger {
struct annotation_t : public supports_flags<>,
public equality_comparable<annotation_t>
{
-#define ANNOTATION_PRICE_CALCULATED 0x01
-#define ANNOTATION_PRICE_FIXATED 0x02
-#define ANNOTATION_DATE_CALCULATED 0x04
-#define ANNOTATION_TAG_CALCULATED 0x08
+#define ANNOTATION_PRICE_CALCULATED 0x01
+#define ANNOTATION_PRICE_FIXATED 0x02
+#define ANNOTATION_PRICE_NOT_PER_UNIT 0x04
+#define ANNOTATION_DATE_CALCULATED 0x08
+#define ANNOTATION_TAG_CALCULATED 0x10
+#define ANNOTATION_VALUE_EXPR_CALCULATED 0x20
optional<amount_t> price;
optional<date_t> date;
optional<string> tag;
-
- explicit annotation_t(const optional<amount_t>& _price = none,
- const optional<date_t>& _date = none,
- const optional<string>& _tag = none)
- : supports_flags<>(), price(_price), date(_date), tag(_tag) {
- TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string");
+ optional<expr_t> value_expr;
+
+ explicit annotation_t(const optional<amount_t>& _price = none,
+ const optional<date_t>& _date = none,
+ const optional<string>& _tag = none,
+ const optional<expr_t>& _value_expr = none)
+ : supports_flags<>(), price(_price), date(_date), tag(_tag),
+ value_expr(_value_expr) {
+ TRACE_CTOR(annotation_t,
+ "const optional<amount_t>& + date_t + string + expr_t");
}
annotation_t(const annotation_t& other)
: supports_flags<>(other.flags()),
- price(other.price), date(other.date), tag(other.tag) {
+ price(other.price), date(other.date), tag(other.tag),
+ value_expr(other.value_expr)
+ {
TRACE_CTOR(annotation_t, "copy");
}
~annotation_t() {
@@ -76,17 +86,20 @@ struct annotation_t : public supports_flags<>,
}
operator bool() const {
- return price || date || tag;
+ return price || date || tag || value_expr;
}
+ bool operator<(const annotation_t& rhs) const;
bool operator==(const annotation_t& rhs) const {
- return (price == rhs.price &&
- date == rhs.date &&
- tag == rhs.tag);
+ return (price == rhs.price &&
+ date == rhs.date &&
+ tag == rhs.tag &&
+ (value_expr && rhs.value_expr ?
+ value_expr->text() == rhs.value_expr->text() :
+ value_expr == rhs.value_expr));
}
void parse(std::istream& in);
-
void print(std::ostream& out, bool keep_base = false,
bool no_computed_annotations = false) const;
@@ -132,6 +145,12 @@ inline void to_xml(std::ostream& out, const annotation_t& details)
push_xml y(out, "tag");
out << y.guard(*details.tag);
}
+
+ if (details.value_expr)
+ {
+ push_xml y(out, "value-expr");
+ out << y.guard(details.value_expr->text());
+ }
}
struct keep_details_t
@@ -230,6 +249,17 @@ public:
return *ptr;
}
+ virtual optional<expr_t> value_expr() const {
+ if (details.value_expr)
+ return details.value_expr;
+ return commodity_t::value_expr();
+ }
+
+ optional<price_point_t>
+ virtual find_price(const commodity_t * commodity = NULL,
+ const datetime_t& moment = datetime_t(),
+ const datetime_t& oldest = datetime_t()) const;
+
virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep);
virtual void write_annotations(std::ostream& out,
bool no_computed_annotations = false) const;
diff --git a/src/balance.cc b/src/balance.cc
index 4fba7344..f87e8bbd 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -185,8 +185,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
}
optional<balance_t>
-balance_t::value(const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of) const
+balance_t::value(const datetime_t& moment,
+ const commodity_t * in_terms_of) const
{
balance_t temp;
bool resolved = false;
@@ -202,16 +202,6 @@ balance_t::value(const optional<datetime_t>& moment,
return resolved ? temp : optional<balance_t>();
}
-balance_t balance_t::price() const
-{
- balance_t temp;
-
- foreach (const amounts_map::value_type& pair, amounts)
- temp += pair.second.price();
-
- return temp;
-}
-
optional<amount_t>
balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const
{
diff --git a/src/balance.h b/src/balance.h
index 57e6ace4..5f0d52ed 100644
--- a/src/balance.h
+++ b/src/balance.h
@@ -384,10 +384,8 @@ public:
}
optional<balance_t>
- value(const optional<datetime_t>& moment = none,
- const optional<commodity_t&>& in_terms_of = none) const;
-
- balance_t price() const;
+ value(const datetime_t& moment = datetime_t(),
+ const commodity_t * in_terms_of = NULL) const;
/**
* Truth tests. An balance may be truth test in two ways:
diff --git a/src/chain.cc b/src/chain.cc
index fc1be5bd..44b3db82 100644
--- a/src/chain.cc
+++ b/src/chain.cc
@@ -88,10 +88,9 @@ post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler,
predicate_t(report.HANDLER(forecast_while_).str(),
report.what_to_keep()),
report,
- report.HANDLED(forecast_years_) ?
- static_cast<std::size_t>
- (report.HANDLER(forecast_years_).value.to_long()) :
- 5UL);
+ (report.HANDLED(forecast_years_) ?
+ lexical_cast<std::size_t>
+ (report.HANDLER(forecast_years_).value) : 5UL));
forecast_handler->add_period_xacts(report.session.journal->period_xacts);
handler.reset(forecast_handler);
@@ -115,10 +114,13 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler,
predicate_t only_predicate;
display_filter_posts * display_filter = NULL;
- assert(report.HANDLED(amount_));
expr_t& expr(report.HANDLER(amount_).expr);
expr.set_context(&report);
+ report.HANDLER(total_).expr.set_context(&report);
+ report.HANDLER(display_amount_).expr.set_context(&report);
+ report.HANDLER(display_total_).expr.set_context(&report);
+
if (! for_accounts_report) {
// Make sure only forecast postings which match are allowed through
if (report.HANDLED(forecast_while_)) {
@@ -134,9 +136,9 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler,
handler.reset
(new truncate_xacts(handler,
report.HANDLED(head_) ?
- report.HANDLER(head_).value.to_int() : 0,
+ lexical_cast<int>(report.HANDLER(head_).value) : 0,
report.HANDLED(tail_) ?
- report.HANDLER(tail_).value.to_int() : 0));
+ lexical_cast<int>(report.HANDLER(tail_).value) : 0));
// display_filter_posts adds virtual posts to the list to account
// for changes in value of commodities, which otherwise would affect
diff --git a/src/chain.h b/src/chain.h
index 080c4231..15ae12ba 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -50,8 +50,9 @@ class post_t;
class account_t;
template <typename T>
-struct item_handler : public noncopyable
+class item_handler : public noncopyable
{
+protected:
shared_ptr<item_handler> handler;
public:
diff --git a/src/commodity.cc b/src/commodity.cc
index 7d473d74..8f0dc100 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -35,6 +35,7 @@
#include "commodity.h"
#include "annotate.h"
#include "pool.h"
+#include "scope.h"
namespace ledger {
@@ -70,12 +71,12 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity)
}
void commodity_t::map_prices(function<void(datetime_t, const amount_t&)> fn,
- const optional<datetime_t>& moment,
- const optional<datetime_t>& _oldest)
+ const datetime_t& moment,
+ const datetime_t& _oldest)
{
datetime_t when;
- if (moment)
- when = *moment;
+ if (! moment.is_not_a_date_time())
+ when = moment;
else if (epoch)
when = *epoch;
else
@@ -85,78 +86,109 @@ void commodity_t::map_prices(function<void(datetime_t, const amount_t&)> fn,
}
optional<price_point_t>
-commodity_t::find_price(const optional<commodity_t&>& commodity,
- const optional<datetime_t>& moment,
- const optional<datetime_t>& oldest) const
+commodity_t::find_price_from_expr(expr_t& expr, const commodity_t * commodity,
+ const datetime_t& moment) const
{
- optional<commodity_t&> target;
+#if defined(DEBUG_ON)
+ if (SHOW_DEBUG("commodity.price.find")) {
+ ledger::_log_buffer << "valuation expr: ";
+ expr.dump(ledger::_log_buffer);
+ DEBUG("commodity.price.find", "");
+ }
+#endif
+ value_t result(expr.calc(*scope_t::default_scope));
+
+ if (is_expr(result)) {
+ value_t call_args;
+
+ call_args.push_back(string_value(base_symbol()));
+ call_args.push_back(moment);
+ if (commodity)
+ call_args.push_back(string_value(commodity->symbol()));
+
+ result = as_expr(result)->call(call_args, *scope_t::default_scope);
+ }
+
+ return price_point_t(moment, result.to_amount());
+}
+
+optional<price_point_t>
+commodity_t::find_price(const commodity_t * commodity,
+ const datetime_t& moment,
+ const datetime_t& oldest) const
+{
+ DEBUG("commodity.price.find", "commodity_t::find_price(" << symbol() << ")");
+
+ const commodity_t * target = NULL;
if (commodity)
target = commodity;
else if (pool().default_commodity)
- target = *pool().default_commodity;
+ target = &*pool().default_commodity;
- if (target && *this == *target)
+ if (target && this == target)
return none;
- optional<base_t::time_and_commodity_t> pair =
- base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest),
- commodity ? &(*commodity) : NULL);
+ base_t::memoized_price_entry entry(moment, oldest,
+ commodity ? commodity : NULL);
- DEBUG("history.find", "looking for memoized args: "
- << (moment ? format_datetime(*moment) : "NONE") << ", "
- << (oldest ? format_datetime(*oldest) : "NONE") << ", "
+ DEBUG("commodity.price.find", "looking for memoized args: "
+ << (! moment.is_not_a_date_time() ? format_datetime(moment) : "NONE") << ", "
+ << (! oldest.is_not_a_date_time() ? format_datetime(oldest) : "NONE") << ", "
<< (commodity ? commodity->symbol() : "NONE"));
{
- base_t::memoized_price_map::iterator i = base->price_map.find(*pair);
+ base_t::memoized_price_map::iterator i = base->price_map.find(entry);
if (i != base->price_map.end()) {
- DEBUG("history.find", "found! returning: "
+ DEBUG("commodity.price.find", "found! returning: "
<< ((*i).second ? (*i).second->price : amount_t(0L)));
return (*i).second;
}
}
datetime_t when;
- if (moment)
- when = *moment;
+ if (! moment.is_not_a_date_time())
+ when = moment;
else if (epoch)
when = *epoch;
else
when = CURRENT_TIME();
- optional<price_point_t> point =
- target ?
- pool().commodity_price_history.find_price(*this, *target, when, oldest) :
- pool().commodity_price_history.find_price(*this, when, oldest);
-
- if (pair) {
- if (base->price_map.size() > base_t::max_price_map_size) {
- DEBUG("history.find",
- "price map has grown too large, clearing it by half");
- for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++)
- base->price_map.erase(base->price_map.begin());
- }
+ if (base->value_expr)
+ return find_price_from_expr(*base->value_expr, commodity, when);
+ optional<price_point_t>
+ point(target ?
+ pool().commodity_price_history.find_price(*this, *target,
+ when, oldest) :
+ pool().commodity_price_history.find_price(*this, when, oldest));
+
+ // Record this price point in the memoization map
+ if (base->price_map.size() > base_t::max_price_map_size) {
DEBUG("history.find",
- "remembered: " << (point ? point->price : amount_t(0L)));
- base->price_map.insert
- (base_t::memoized_price_map::value_type(*pair, point));
+ "price map has grown too large, clearing it by half");
+ for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++)
+ base->price_map.erase(base->price_map.begin());
}
+
+ DEBUG("history.find",
+ "remembered: " << (point ? point->price : amount_t(0L)));
+ base->price_map.insert(base_t::memoized_price_map::value_type(entry, point));
+
return point;
}
optional<price_point_t>
commodity_t::check_for_updated_price(const optional<price_point_t>& point,
- const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of)
+ const datetime_t& moment,
+ const commodity_t* in_terms_of)
{
if (pool().get_quotes && ! has_flags(COMMODITY_NOMARKET)) {
bool exceeds_leeway = true;
if (point) {
time_duration_t::sec_type seconds_diff;
- if (moment) {
- seconds_diff = (*moment - point->when).total_seconds();
- DEBUG("commodity.download", "moment = " << *moment);
+ if (! moment.is_not_a_date_time()) {
+ seconds_diff = (moment - point->when).total_seconds();
+ DEBUG("commodity.download", "moment = " << moment);
DEBUG("commodity.download", "slip.moment = " << seconds_diff);
} else {
seconds_diff = (TRUE_CURRENT_TIME() - point->when).total_seconds();
@@ -175,7 +207,7 @@ commodity_t::check_for_updated_price(const optional<price_point_t>& point,
pool().get_commodity_quote(*this, in_terms_of)) {
if (! in_terms_of ||
(quote->price.has_commodity() &&
- quote->price.commodity() == *in_terms_of))
+ quote->price.commodity_ptr() == in_terms_of))
return quote;
}
}
@@ -183,6 +215,15 @@ commodity_t::check_for_updated_price(const optional<price_point_t>& point,
return point;
}
+commodity_t& commodity_t::nail_down(const expr_t& expr)
+{
+ annotation_t new_details;
+ new_details.value_expr = expr;
+ commodity_t * new_comm =
+ commodity_pool_t::current_pool->find_or_create(symbol(), new_details);
+ return *new_comm;
+}
+
commodity_t::operator bool() const
{
return this != pool().null_commodity;
diff --git a/src/commodity.h b/src/commodity.h
index 524daaab..bd1aedb9 100644
--- a/src/commodity.h
+++ b/src/commodity.h
@@ -47,6 +47,8 @@
#ifndef _COMMODITY_H
#define _COMMODITY_H
+#include "expr.h"
+
namespace ledger {
struct keep_details_t;
@@ -113,15 +115,14 @@ protected:
optional<string> note;
optional<amount_t> smaller;
optional<amount_t> larger;
+ optional<expr_t> value_expr;
- typedef std::pair<optional<datetime_t>,
- optional<datetime_t> > optional_time_pair_t;
- typedef std::pair<optional_time_pair_t,
- commodity_t *> time_and_commodity_t;
- typedef std::map<time_and_commodity_t,
+ typedef tuple<datetime_t, datetime_t,
+ const commodity_t *> memoized_price_entry;
+ typedef std::map<memoized_price_entry,
optional<price_point_t> > memoized_price_map;
- static const std::size_t max_price_map_size = 16;
+ static const std::size_t max_price_map_size = 8;
mutable memoized_price_map price_map;
public:
@@ -164,7 +165,6 @@ protected:
commodity_pool_t * parent_;
optional<string> qualified_symbol;
- optional<string> mapping_key_;
bool annotated;
explicit commodity_t(commodity_pool_t * _parent,
@@ -218,13 +218,6 @@ public:
return qualified_symbol ? *qualified_symbol : base_symbol();
}
- string mapping_key() const {
- if (mapping_key_)
- return *mapping_key_;
- else
- return base_symbol();
- }
-
optional<std::size_t> graph_index() const {;
return base->graph_index;
}
@@ -267,23 +260,36 @@ public:
base->larger = arg;
}
+ virtual optional<expr_t> value_expr() const {
+ return base->value_expr;
+ }
+ void set_value_expr(const optional<expr_t>& expr = none) {
+ base->value_expr = expr;
+ }
+
void add_price(const datetime_t& date, const amount_t& price,
const bool reflexive = true);
void remove_price(const datetime_t& date, commodity_t& commodity);
void map_prices(function<void(datetime_t, const amount_t&)> fn,
- const optional<datetime_t>& moment = none,
- const optional<datetime_t>& _oldest = none);
+ const datetime_t& moment = datetime_t(),
+ const datetime_t& _oldest = datetime_t());
+
+ optional<price_point_t>
+ find_price_from_expr(expr_t& expr, const commodity_t * commodity,
+ const datetime_t& moment) const;
optional<price_point_t>
- find_price(const optional<commodity_t&>& commodity = none,
- const optional<datetime_t>& moment = none,
- const optional<datetime_t>& oldest = none) const;
+ virtual find_price(const commodity_t * commodity = NULL,
+ const datetime_t& moment = datetime_t(),
+ const datetime_t& oldest = datetime_t()) const;
optional<price_point_t>
check_for_updated_price(const optional<price_point_t>& point,
- const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of);
+ const datetime_t& moment,
+ const commodity_t * in_terms_of);
+
+ commodity_t& nail_down(const expr_t& expr);
// Methods related to parsing, reading, writing, etc., the commodity
// itself.
@@ -325,7 +331,6 @@ private:
ar & base;
ar & parent_;
ar & qualified_symbol;
- ar & mapping_key_;
ar & annotated;
}
#endif // HAVE_BOOST_SERIALIZATION
diff --git a/src/csv.cc b/src/csv.cc
index 305db992..1e55129e 100644
--- a/src/csv.cc
+++ b/src/csv.cc
@@ -139,8 +139,8 @@ xact_t * csv_reader::read_xact(bool rich_data)
std::istringstream instr(line);
- std::auto_ptr<xact_t> xact(new xact_t);
- std::auto_ptr<post_t> post(new post_t);
+ unique_ptr<xact_t> xact(new xact_t);
+ unique_ptr<post_t> post(new post_t);
xact->set_state(item_t::CLEARED);
diff --git a/src/draft.cc b/src/draft.cc
index 7c95caf7..74a6f4d2 100644
--- a/src/draft.cc
+++ b/src/draft.cc
@@ -109,7 +109,14 @@ void draft_t::parse_args(const value_t& args)
}
else if (check_for_date &&
bool(weekday = string_to_day_of_week(what[0]))) {
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
short dow = static_cast<short>(*weekday);
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
+#pragma GCC diagnostic pop
+#endif
date_t date = CURRENT_DATE() - date_duration(1);
while (date.day_of_week() != dow)
date -= date_duration(1);
@@ -233,7 +240,7 @@ xact_t * draft_t::insert(journal_t& journal)
throw std::runtime_error(_("'xact' command requires at least a payee"));
xact_t * matching = NULL;
- std::auto_ptr<xact_t> added(new xact_t);
+ unique_ptr<xact_t> added(new xact_t);
if (xact_t * xact =
lookup_probable_account(tmpl->payee_mask.str(), journal.xacts.rbegin(),
@@ -316,7 +323,7 @@ xact_t * draft_t::insert(journal_t& journal)
}
foreach (xact_template_t::post_template_t& post, tmpl->posts) {
- std::auto_ptr<post_t> new_post;
+ unique_ptr<post_t> new_post;
commodity_t * found_commodity = NULL;
@@ -515,7 +522,7 @@ value_t xact_command(call_scope_t& args)
xact_t * new_xact = draft.insert(*report.session.journal.get());
// Only consider actual postings for the "xact" command
- report.HANDLER(limit_).on(string("#xact"), "actual");
+ report.HANDLER(limit_).on("#xact", "actual");
if (new_xact)
report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact);
diff --git a/src/expr.cc b/src/expr.cc
index 74d16ecc..b22572f8 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -37,6 +37,59 @@
namespace ledger {
+expr_t::expr_t() : base_type()
+{
+ TRACE_CTOR(expr_t, "");
+}
+
+expr_t::expr_t(const expr_t& other) : base_type(other), ptr(other.ptr)
+{
+ TRACE_CTOR(expr_t, "copy");
+}
+expr_t::expr_t(ptr_op_t _ptr, scope_t * _context)
+ : base_type(_context), ptr(_ptr)
+{
+ TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *");
+}
+
+expr_t::expr_t(const string& _str, const parse_flags_t& flags)
+ : base_type()
+{
+ TRACE_CTOR(expr_t, "string, parse_flags_t");
+ if (! _str.empty())
+ parse(_str, flags);
+}
+
+expr_t::expr_t(std::istream& in, const parse_flags_t& flags)
+ : base_type()
+{
+ TRACE_CTOR(expr_t, "std::istream&, parse_flags_t");
+ parse(in, flags);
+}
+
+expr_t::~expr_t() {
+ TRACE_DTOR(expr_t);
+}
+
+expr_t& expr_t::operator=(const expr_t& _expr)
+{
+ if (this != &_expr) {
+ base_type::operator=(_expr);
+ ptr = _expr.ptr;
+ }
+ return *this;
+}
+
+expr_t::operator bool() const throw()
+{
+ return ptr.get() != NULL;
+}
+
+expr_t::ptr_op_t expr_t::get_op() throw()
+{
+ return ptr;
+}
+
void expr_t::parse(std::istream& in, const parse_flags_t& flags,
const optional<string>& original_string)
{
@@ -163,6 +216,65 @@ void expr_t::dump(std::ostream& out) const
if (ptr) ptr->dump(out, 0);
}
+bool merged_expr_t::check_for_single_identifier(const string& expr)
+{
+ bool single_identifier = true;
+ for (const char * p = expr.c_str(); *p; ++p)
+ if (! std::isalnum(*p) || *p == '_') {
+ single_identifier = false;
+ break;
+ }
+
+ if (single_identifier) {
+ set_base_expr(expr);
+ exprs.clear();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void merged_expr_t::compile(scope_t& scope)
+{
+ if (exprs.empty()) {
+ parse(base_expr);
+ } else {
+ std::ostringstream buf;
+
+ buf << "__tmp_" << term << "=(" << term << "=(" << base_expr << ")";
+ foreach (const string& expr, exprs) {
+ if (merge_operator == ";")
+ buf << merge_operator << term << "=" << expr;
+ else
+ buf << merge_operator << "(" << expr << ")";
+ }
+ buf << ";" << term << ");__tmp_" << term;
+
+ DEBUG("expr.merged.compile", "Compiled expr: " << buf.str());
+ parse(buf.str());
+ }
+
+ expr_t::compile(scope);
+}
+
+expr_t::ptr_op_t as_expr(const value_t& val)
+{
+ VERIFY(val.is_any());
+ return val.as_any<expr_t::ptr_op_t>();
+}
+
+void set_expr(value_t& val, expr_t::ptr_op_t op)
+{
+ val.set_any(op);
+}
+
+value_t expr_value(expr_t::ptr_op_t op)
+{
+ value_t temp;
+ temp.set_any(op);
+ return temp;
+}
+
value_t source_command(call_scope_t& args)
{
std::istream * in = NULL;
diff --git a/src/expr.h b/src/expr.h
index e082efa5..590bdc15 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -71,49 +71,20 @@ protected:
ptr_op_t ptr;
public:
- expr_t() : base_type() {
- TRACE_CTOR(expr_t, "");
- }
- expr_t(const expr_t& other)
- : base_type(other), ptr(other.ptr) {
- TRACE_CTOR(expr_t, "copy");
- }
- expr_t(ptr_op_t _ptr, scope_t * _context = NULL)
- : base_type(_context), ptr(_ptr) {
- TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *");
- }
+ expr_t();
+ expr_t(const expr_t& other);
+ expr_t(ptr_op_t _ptr, scope_t * _context = NULL);
- expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT)
- : base_type() {
- TRACE_CTOR(expr_t, "string, parse_flags_t");
- if (! _str.empty())
- parse(_str, flags);
- }
- expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT)
- : base_type() {
- TRACE_CTOR(expr_t, "std::istream&, parse_flags_t");
- parse(in, flags);
- }
+ expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT);
+ expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT);
- virtual ~expr_t() {
- TRACE_DTOR(expr_t);
- }
+ virtual ~expr_t();
- expr_t& operator=(const expr_t& _expr) {
- if (this != &_expr) {
- base_type::operator=(_expr);
- ptr = _expr.ptr;
- }
- return *this;
- }
+ expr_t& operator=(const expr_t& _expr);
- virtual operator bool() const throw() {
- return ptr.get() != NULL;
- }
+ virtual operator bool() const throw();
- ptr_op_t get_op() throw() {
- return ptr;
- }
+ ptr_op_t get_op() throw();
void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) {
std::istringstream stream(str);
@@ -156,21 +127,74 @@ private:
inline bool is_expr(const value_t& val) {
return val.is_any() && val.as_any().type() == typeid(expr_t::ptr_op_t);
}
-inline expr_t::ptr_op_t as_expr(const value_t& val) {
- VERIFY(val.is_any());
- return val.as_any<expr_t::ptr_op_t>();
-}
-inline void set_expr(value_t& val, expr_t::ptr_op_t op) {
- val.set_any(op);
-}
-inline value_t expr_value(expr_t::ptr_op_t op) {
- value_t temp;
- temp.set_any(op);
- return temp;
-}
-class call_scope_t;
+expr_t::ptr_op_t as_expr(const value_t& val);
+void set_expr(value_t& val, expr_t::ptr_op_t op);
+value_t expr_value(expr_t::ptr_op_t op);
+
+// A merged expression allows one to set an expression term, "foo", and
+// a base expression, "bar", and then merge in later expressions that
+// utilize foo. For example:
+//
+// foo: bar
+// merge: foo * 10
+// merge: foo + 20
+//
+// When this expression is finally compiled, the base and merged
+// elements are written into this:
+//
+// __tmp=(foo=bar; foo=foo*10; foo=foo+20);__tmp
+//
+// This allows users to select flags like -O, -B or -I at any time, and
+// also combine flags such as -V and -A.
+
+class merged_expr_t : public expr_t
+{
+public:
+ string term;
+ string base_expr;
+ string merge_operator;
+
+ std::list<string> exprs;
+ merged_expr_t(const string& _term, const string& expr,
+ const string& merge_op = ";")
+ : expr_t(), term(_term), base_expr(expr), merge_operator(merge_op) {
+ TRACE_CTOR(merged_expr_t, "string, string, string");
+ }
+
+ virtual ~merged_expr_t() {
+ TRACE_DTOR(merged_expr_t);
+ }
+
+ void set_term(const string& _term) {
+ term = _term;
+ }
+ void set_base_expr(const string& expr) {
+ base_expr = expr;
+ }
+ void set_merge_operator(const string& merge_op) {
+ merge_operator = merge_op;
+ }
+
+ bool check_for_single_identifier(const string& expr);
+
+ void prepend(const string& expr) {
+ if (! check_for_single_identifier(expr))
+ exprs.push_front(expr);
+ }
+ void append(const string& expr) {
+ if (! check_for_single_identifier(expr))
+ exprs.push_back(expr);
+ }
+ void remove(const string& expr) {
+ exprs.remove(expr);
+ }
+
+ virtual void compile(scope_t& scope);
+};
+
+class call_scope_t;
value_t source_command(call_scope_t& scope);
} // namespace ledger
diff --git a/src/exprbase.h b/src/exprbase.h
index 0b1ef243..bea25320 100644
--- a/src/exprbase.h
+++ b/src/exprbase.h
@@ -113,7 +113,7 @@ public:
return ! str.empty();
}
- virtual string text() {
+ virtual string text() const throw() {
return str;
}
void set_text(const string& txt) {
@@ -163,7 +163,7 @@ public:
}
#endif // defined(DEBUG_ON)
- DEBUG("expr.calc.when", "Compiling: " << str);
+ DEBUG("expr.compile", "Compiling: " << str);
compile(scope);
#if defined(DEBUG_ON)
@@ -174,7 +174,7 @@ public:
#endif // defined(DEBUG_ON)
}
- DEBUG("expr.calc.when", "Calculating: " << str);
+ DEBUG("expr.calc", "Calculating: " << str);
return real_calc(scope);
}
diff --git a/src/filters.cc b/src/filters.cc
index 8543cddb..3a975920 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -833,10 +833,17 @@ void subtotal_posts::report_subtotal(const char * spec_fmt,
foreach (post_t * post, component_posts) {
date_t date = post->date();
date_t value_date = post->value_date();
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
if (! range_start || date < *range_start)
range_start = date;
if (! range_finish || value_date > *range_finish)
range_finish = value_date;
+#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
+#pragma GCC diagnostic pop
+#endif
}
}
component_posts.clear();
@@ -880,9 +887,13 @@ void subtotal_posts::operator()(post_t& post)
if (i == values.end()) {
value_t temp;
post.add_to_value(temp, amount_expr);
- std::pair<values_map::iterator, bool> result
- = values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp)));
+#if defined(DEBUG_ON)
+ std::pair<values_map::iterator, bool> result =
+#endif
+ values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp)));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
} else {
post.add_to_value((*i).second.value, amount_expr);
}
diff --git a/src/filters.h b/src/filters.h
index 22f2d2cb..af68cd7c 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -65,14 +65,14 @@ protected:
value_to_posts_map posts_map;
post_handler_ptr post_chain;
report_t& report;
- expr_t group_by_expr;
+ expr_t& group_by_expr;
custom_flusher_t preflush_func;
optional<custom_flusher_t> postflush_func;
public:
post_splitter(post_handler_ptr _post_chain,
report_t& _report,
- expr_t _group_by_expr)
+ expr_t& _group_by_expr)
: post_chain(_post_chain), report(_report),
group_by_expr(_group_by_expr) {
TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t");
@@ -372,6 +372,7 @@ public:
}
virtual ~anonymize_posts() {
TRACE_DTOR(anonymize_posts);
+ handler.reset();
}
void render_commodity(amount_t& amt);
@@ -451,6 +452,7 @@ public:
}
virtual ~collapse_posts() {
TRACE_DTOR(collapse_posts);
+ handler.reset();
}
void create_accounts() {
@@ -521,8 +523,8 @@ class display_filter_posts : public item_handler<post_t>
// later in the chain.
report_t& report;
- expr_t display_amount_expr;
- expr_t display_total_expr;
+ expr_t& display_amount_expr;
+ expr_t& display_total_expr;
bool show_rounding;
value_t last_display_total;
temporaries_t temps;
@@ -539,6 +541,7 @@ public:
virtual ~display_filter_posts() {
TRACE_DTOR(display_filter_posts);
+ handler.reset();
}
void create_accounts() {
@@ -569,8 +572,8 @@ class changed_value_posts : public item_handler<post_t>
// later in the chain.
report_t& report;
- expr_t total_expr;
- expr_t display_total_expr;
+ expr_t& total_expr;
+ expr_t& display_total_expr;
bool changed_values_only;
bool for_accounts_report;
bool show_unrealized;
@@ -595,6 +598,7 @@ public:
virtual ~changed_value_posts() {
TRACE_DTOR(changed_value_posts);
+ handler.reset();
}
void create_accounts() {
@@ -671,6 +675,7 @@ public:
}
virtual ~subtotal_posts() {
TRACE_DTOR(subtotal_posts);
+ handler.reset();
}
void report_subtotal(const char * spec_fmt = NULL,
@@ -849,6 +854,7 @@ public:
}
virtual ~transfer_details() {
TRACE_DTOR(transfer_details);
+ handler.reset();
}
virtual void operator()(post_t& post);
@@ -908,6 +914,7 @@ public:
virtual ~generate_posts() {
TRACE_DTOR(generate_posts);
+ handler.reset();
}
void add_period_xacts(period_xacts_list& period_xacts);
@@ -995,6 +1002,7 @@ class inject_posts : public item_handler<post_t>
virtual ~inject_posts() throw() {
TRACE_DTOR(inject_posts);
+ handler.reset();
}
virtual void operator()(post_t& post);
diff --git a/src/format.cc b/src/format.cc
index 9824d5f7..79f94869 100644
--- a/src/format.cc
+++ b/src/format.cc
@@ -125,7 +125,7 @@ namespace {
format_t::element_t * format_t::parse_elements(const string& fmt,
const optional<format_t&>& tmpl)
{
- std::auto_ptr<element_t> result;
+ unique_ptr<element_t> result;
element_t * current = NULL;
@@ -221,8 +221,10 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
static_cast<int>(current->max_width) : -1);
else if (keyword == "left")
expr << (current->has_flags(ELEMENT_ALIGN_LEFT) ? "false" : "true");
+#if defined(DEBUG_ON)
else
assert("Unrecognized format substitution keyword" == NULL);
+#endif
} else {
expr << *ptr++;
}
diff --git a/src/global.cc b/src/global.cc
index 5b7bb1c1..d7742161 100644
--- a/src/global.cc
+++ b/src/global.cc
@@ -70,6 +70,7 @@ global_scope_t::global_scope_t(char ** envp)
// generated.
report_stack.push_front(new report_t(*session_ptr));
scope_t::default_scope = &report();
+ scope_t::empty_scope = &empty_scope;
// Read the user's options, in the following order:
//
@@ -192,7 +193,7 @@ void global_scope_t::execute_command(strings_list args, bool at_repl)
is_precommand = true;
// If it is not a pre-command, then parse the user's ledger data at this
- // time if not done alreday (i.e., if not at a REPL). Then patch up the
+ // time if not done already (i.e., if not at a REPL). Then patch up the
// report options based on the command verb.
if (! is_precommand) {
diff --git a/src/global.h b/src/global.h
index 2cb7842e..392b03a9 100644
--- a/src/global.h
+++ b/src/global.h
@@ -50,6 +50,7 @@ class global_scope_t : public noncopyable, public scope_t
{
shared_ptr<session_t> session_ptr;
ptr_list<report_t> report_stack;
+ empty_scope_t empty_scope;
public:
global_scope_t(char ** envp);
@@ -82,6 +83,7 @@ public:
void pop_report() {
assert(! report_stack.empty());
report_stack.pop_front();
+
// There should always be the "default report" waiting on the stack.
assert(! report_stack.empty());
scope_t::default_scope = &report();
@@ -139,7 +141,6 @@ See LICENSE file included with the distribution for details and disclaimer.");
OPTION__
(global_scope_t, init_file_, // -i
-
CTOR(global_scope_t, init_file_) {
if (const char * home_var = std::getenv("HOME"))
on(none, (path(home_var) / ".ledgerrc").string());
diff --git a/src/history.cc b/src/history.cc
index c4f6b3fc..22ac4494 100644
--- a/src/history.cc
+++ b/src/history.cc
@@ -42,13 +42,89 @@ struct f_max : public std::binary_function<T, T, bool> {
namespace ledger {
+template <typename EdgeWeightMap,
+ typename PricePointMap,
+ typename PriceRatioMap>
+class recent_edge_weight
+{
+public:
+ EdgeWeightMap weight;
+ PricePointMap price_point;
+ PriceRatioMap ratios;
+
+ datetime_t reftime;
+ datetime_t oldest;
+
+ recent_edge_weight() { }
+ recent_edge_weight(EdgeWeightMap _weight,
+ PricePointMap _price_point,
+ PriceRatioMap _ratios,
+ const datetime_t& _reftime,
+ const datetime_t& _oldest = datetime_t())
+ : weight(_weight), price_point(_price_point), ratios(_ratios),
+ reftime(_reftime), oldest(_oldest) { }
+
+ template <typename Edge>
+ bool operator()(const Edge& e) const
+ {
+#if defined(DEBUG_ON)
+ DEBUG("history.find", " reftime = " << reftime);
+ if (! oldest.is_not_a_date_time()) {
+ DEBUG("history.find", " oldest = " << oldest);
+ }
+#endif
+
+ const price_map_t& prices(get(ratios, e));
+ if (prices.empty()) {
+ DEBUG("history.find", " prices map is empty for this edge");
+ return false;
+ }
+
+ price_map_t::const_iterator low = prices.upper_bound(reftime);
+ if (low != prices.end() && low == prices.begin()) {
+ DEBUG("history.find", " don't use this edge");
+ return false;
+ } else {
+ --low;
+ assert(((*low).first <= reftime));
+
+ if (! oldest.is_not_a_date_time() && (*low).first < oldest) {
+ DEBUG("history.find", " edge is out of range");
+ return false;
+ }
+
+ long secs = (reftime - (*low).first).total_seconds();
+ assert(secs >= 0);
+
+ put(weight, e, secs);
+ put(price_point, e, price_point_t((*low).first, (*low).second));
+
+ DEBUG("history.find", " using edge at price point "
+ << (*low).first << " " << (*low).second);
+ return true;
+ }
+ }
+};
+
+typedef filtered_graph
+ <commodity_history_t::Graph,
+ recent_edge_weight<commodity_history_t::EdgeWeightMap,
+ commodity_history_t::PricePointMap,
+ commodity_history_t::PriceRatioMap> > FGraph;
+
+typedef property_map<FGraph, vertex_name_t>::type FNameMap;
+typedef property_map<FGraph, vertex_index_t>::type FIndexMap;
+typedef iterator_property_map
+ <commodity_history_t::vertex_descriptor*, FIndexMap,
+ commodity_history_t::vertex_descriptor,
+ commodity_history_t::vertex_descriptor&> FPredecessorMap;
+typedef iterator_property_map<long*, FIndexMap, long, long&> FDistanceMap;
+
void commodity_history_t::add_commodity(commodity_t& comm)
{
if (! comm.graph_index()) {
- std::size_t index = num_vertices(price_graph);
- comm.set_graph_index(index);
- const vertex_descriptor vert = add_vertex(&comm, price_graph);
- put(indexmap, vert, index);
+ comm.set_graph_index(num_vertices(price_graph));
+ add_vertex(/* vertex_name= */ &comm, price_graph);
}
}
@@ -59,7 +135,10 @@ void commodity_history_t::add_price(const commodity_t& source,
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph);
- std::pair<edge_descriptor, bool> e1 = add_edge(sv, tv, 0, price_graph);
+ std::pair<edge_descriptor, bool> e1 = edge(sv, tv, price_graph);
+ if (! e1.second)
+ e1 = add_edge(sv, tv, price_graph);
+
price_map_t& prices(get(ratiomap, e1.first));
std::pair<price_map_t::iterator, bool> result =
@@ -67,9 +146,6 @@ void commodity_history_t::add_price(const commodity_t& source,
if (! result.second) {
// There is already an entry for this moment, so update it
(*result.first).second = price;
- } else {
- last_reftime = none; // invalidate the FGraph cache
- last_oldest = none;
}
}
@@ -80,31 +156,30 @@ void commodity_history_t::remove_price(const commodity_t& source,
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
vertex_descriptor tv = vertex(*target.graph_index(), price_graph);
- std::pair<edge_descriptor, bool> e1 = add_edge(sv, tv, 0, price_graph);
- price_map_t& prices(get(ratiomap, e1.first));
+ std::pair<Graph::edge_descriptor, bool> e1 = edge(sv, tv, price_graph);
+ if (e1.second) {
+ price_map_t& prices(get(ratiomap, e1.first));
- // jww (2012-03-04): If it fails, should we give a warning?
- prices.erase(date);
+ // jww (2012-03-04): If it fails, should we give a warning?
+ prices.erase(date);
- last_reftime = none; // invalidate the FGraph cache
- last_oldest = none;
+ if (prices.empty())
+ remove_edge(e1.first, price_graph);
+ }
}
void commodity_history_t::map_prices(function<void(datetime_t,
const amount_t&)> fn,
- const commodity_t& source,
- const datetime_t& moment,
- const optional<datetime_t>& _oldest)
+ const commodity_t& source,
+ const datetime_t& moment,
+ const datetime_t& oldest)
{
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
- reftime = moment;
- oldest = _oldest;
-
FGraph fg(price_graph,
recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap>
(get(edge_weight, price_graph), pricemap, ratiomap,
- &reftime, &last_reftime, &oldest, &last_oldest));
+ moment, oldest));
FNameMap namemap(get(vertex_name, fg));
@@ -118,7 +193,7 @@ void commodity_history_t::map_prices(function<void(datetime_t,
foreach (const price_map_t::value_type& pair, prices) {
const datetime_t& when(pair.first);
- if ((! _oldest || when >= *_oldest) && when <= moment) {
+ if ((oldest.is_not_a_date_time() || when >= oldest) && when <= moment) {
if (pair.second.commodity() == source) {
amount_t price(pair.second);
price.in_place_invert();
@@ -134,19 +209,16 @@ void commodity_history_t::map_prices(function<void(datetime_t,
}
optional<price_point_t>
-commodity_history_t::find_price(const commodity_t& source,
- const datetime_t& moment,
- const optional<datetime_t>& _oldest)
+commodity_history_t::find_price(const commodity_t& source,
+ const datetime_t& moment,
+ const datetime_t& oldest)
{
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
- reftime = moment;
- oldest = _oldest;
-
FGraph fg(price_graph,
recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap>
(get(edge_weight, price_graph), pricemap, ratiomap,
- &reftime, &last_reftime, &oldest, &last_oldest));
+ moment, oldest));
FNameMap namemap(get(vertex_name, fg));
@@ -188,9 +260,6 @@ commodity_history_t::find_price(const commodity_t& source,
DEBUG("history.find", "price is = " << price.unrounded());
}
- last_reftime = reftime; // invalidate the FGraph cache
- last_oldest = oldest;
-
if (price.is_null()) {
DEBUG("history.find", "there is no final price");
return none;
@@ -201,32 +270,30 @@ commodity_history_t::find_price(const commodity_t& source,
}
optional<price_point_t>
-commodity_history_t::find_price(const commodity_t& source,
- const commodity_t& target,
- const datetime_t& moment,
- const optional<datetime_t>& _oldest)
+commodity_history_t::find_price(const commodity_t& source,
+ const commodity_t& target,
+ const datetime_t& moment,
+ const datetime_t& oldest)
{
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
vertex_descriptor tv = vertex(*target.graph_index(), price_graph);
- reftime = moment;
- oldest = _oldest;
-
FGraph fg(price_graph,
recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap>
(get(edge_weight, price_graph), pricemap, ratiomap,
- &reftime, &last_reftime, &oldest, &last_oldest));
+ moment, oldest));
FNameMap namemap(get(vertex_name, fg));
DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol());
DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol());
- std::vector<vertex_descriptor> predecessors(num_vertices(fg));
- std::vector<long> distances(num_vertices(fg));
-
- PredecessorMap predecessorMap(&predecessors[0]);
- DistanceMap distanceMap(&distances[0]);
+ std::size_t vector_len(num_vertices(fg));
+ std::vector<vertex_descriptor> predecessors(vector_len);
+ std::vector<long> distances(vector_len);
+
+ FPredecessorMap predecessorMap(&predecessors[0]);
+ FDistanceMap distanceMap(&distances[0]);
dijkstra_shortest_paths(fg, /* start= */ sv,
predecessor_map(predecessorMap)
@@ -235,10 +302,15 @@ commodity_history_t::find_price(const commodity_t& source,
// Extract the shortest path and performance the calculations
datetime_t least_recent = moment;
- amount_t price;
+ amount_t price;
const commodity_t * last_target = &target;
+ typedef tuple<const commodity_t *, const commodity_t *,
+ const price_point_t *> results_tuple;
+ std::vector<results_tuple> results;
+ bool results_reversed = false;
+
vertex_descriptor v = tv;
for (vertex_descriptor u = predecessorMap[v];
u != v;
@@ -247,8 +319,24 @@ commodity_history_t::find_price(const commodity_t& source,
std::pair<Graph::edge_descriptor, bool> edgePair = edge(u, v, fg);
Graph::edge_descriptor edge = edgePair.first;
+ const commodity_t * u_comm = get(namemap, u);
+ const commodity_t * v_comm = get(namemap, v);
const price_point_t& point(get(pricemap, edge));
+ if (v == tv && u_comm != last_target && v_comm != last_target)
+ results_reversed = true;
+
+ results.push_back(results_tuple(u_comm, v_comm, &point));
+ }
+
+ if (results_reversed)
+ std::reverse(results.begin(), results.end());
+
+ foreach (const results_tuple& edge, results) {
+ const commodity_t * u_comm = edge.get<0>();
+ const commodity_t * v_comm = edge.get<1>();
+ const price_point_t& point(*edge.get<2>());
+
bool first_run = false;
if (price.is_null()) {
least_recent = point.when;
@@ -258,8 +346,8 @@ commodity_history_t::find_price(const commodity_t& source,
least_recent = point.when;
}
- DEBUG("history.find", "u commodity = " << get(namemap, u)->symbol());
- DEBUG("history.find", "v commodity = " << get(namemap, v)->symbol());
+ DEBUG("history.find", "u commodity = " << u_comm->symbol());
+ DEBUG("history.find", "v commodity = " << v_comm->symbol());
DEBUG("history.find", "last target = " << last_target->symbol());
// Determine which direction we are converting in
@@ -268,12 +356,12 @@ commodity_history_t::find_price(const commodity_t& source,
if (! first_run) {
DEBUG("history.find", "price was = " << price.unrounded());
- if (pprice.commodity() != *last_target)
+ if (pprice.commodity_ptr() != last_target)
price *= pprice.inverted();
else
price *= pprice;
}
- else if (pprice.commodity() != *last_target) {
+ else if (pprice.commodity_ptr() != last_target) {
price = pprice.inverted();
}
else {
@@ -281,17 +369,14 @@ commodity_history_t::find_price(const commodity_t& source,
}
DEBUG("history.find", "price is = " << price.unrounded());
- if (*last_target == *get(namemap, v))
- last_target = get(namemap, u);
+ if (last_target == v_comm)
+ last_target = u_comm;
else
- last_target = get(namemap, v);
+ last_target = v_comm;
DEBUG("history.find", "last target now = " << last_target->symbol());
}
- last_reftime = reftime; // invalidate the FGraph cache
- last_oldest = oldest;
-
if (price.is_null()) {
DEBUG("history.find", "there is no final price");
return none;
@@ -317,25 +402,16 @@ private:
Name name;
};
-void commodity_history_t::print_map(std::ostream& out,
- const optional<datetime_t>& moment)
+void commodity_history_t::print_map(std::ostream& out, const datetime_t& moment)
{
- if (moment) {
- reftime = *moment;
- oldest = none;
-
+ if (moment.is_not_a_date_time()) {
+ write_graphviz(out, price_graph,
+ label_writer<NameMap>(get(vertex_name, price_graph)));
+ } else {
FGraph fg(price_graph,
recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap>
- (get(edge_weight, price_graph), pricemap, ratiomap,
- &reftime, &last_reftime, &oldest, &last_oldest));
-
+ (get(edge_weight, price_graph), pricemap, ratiomap, moment));
write_graphviz(out, fg, label_writer<FNameMap>(get(vertex_name, fg)));
-
- last_reftime = reftime;
- last_oldest = none;
- } else {
- write_graphviz(out, price_graph,
- label_writer<NameMap>(get(vertex_name, price_graph)));
}
}
diff --git a/src/history.h b/src/history.h
index fc984c1b..71cbad0c 100644
--- a/src/history.h
+++ b/src/history.h
@@ -60,93 +60,12 @@ namespace ledger {
typedef std::map<datetime_t, amount_t> price_map_t;
-template <typename EdgeWeightMap,
- typename PricePointMap,
- typename PriceRatioMap>
-class recent_edge_weight
-{
-public:
- EdgeWeightMap weight;
- PricePointMap price_point;
- PriceRatioMap ratios;
-
- datetime_t * reftime;
- optional<datetime_t> * last_reftime;
- optional<datetime_t> * oldest;
- optional<datetime_t> * last_oldest;
-
- recent_edge_weight() { }
- recent_edge_weight(EdgeWeightMap _weight,
- PricePointMap _price_point,
- PriceRatioMap _ratios,
- datetime_t * _reftime,
- optional<datetime_t> * _last_reftime,
- optional<datetime_t> * _oldest,
- optional<datetime_t> * _last_oldest)
- : weight(_weight), price_point(_price_point), ratios(_ratios),
- reftime(_reftime), last_reftime(_last_reftime),
- oldest(_oldest), last_oldest(_last_oldest) { }
-
- template <typename Edge>
- bool operator()(const Edge& e) const
- {
- DEBUG("history.find", " reftime = " << *reftime);
- if (*last_reftime)
- DEBUG("history.find", " last_reftime = " << **last_reftime);
- if (*oldest)
- DEBUG("history.find", " oldest = " << **oldest);
- if (*last_oldest)
- DEBUG("history.find", " last_oldest = " << **last_oldest);
-
-#if 0
- if (*last_reftime && *reftime == **last_reftime &&
- *oldest == *last_oldest) {
- DEBUG("history.find", " using previous reftime");
- return get(weight, e) != std::numeric_limits<std::size_t>::max();
- }
-#endif
-
- const price_map_t& prices(get(ratios, e));
- if (prices.empty()) {
- DEBUG("history.find", " prices map is empty for this edge");
- put(weight, e, std::numeric_limits<std::size_t>::max());
- return false;
- }
-
- price_map_t::const_iterator low = prices.upper_bound(*reftime);
- if (low != prices.end() && low == prices.begin()) {
- DEBUG("history.find", " don't use this edge");
- put(weight, e, std::numeric_limits<std::size_t>::max());
- return false;
- } else {
- --low;
- assert(((*low).first <= *reftime));
-
- if (*oldest && (*low).first < **oldest) {
- DEBUG("history.find", " edge is out of range");
- put(weight, e, std::numeric_limits<std::size_t>::max());
- return false;
- }
-
- long secs = (*reftime - (*low).first).total_seconds();
- assert(secs >= 0);
-
- put(weight, e, secs);
- put(price_point, e, price_point_t((*low).first, (*low).second));
-
- DEBUG("history.find", " using edge at price point "
- << (*low).first << " " << (*low).second);
- return true;
- }
- }
-};
-
class commodity_history_t : public noncopyable
{
public:
typedef adjacency_list
- <setS, // Store all edges as a set
- setS, // Store all vertices in a set
+ <vecS, // Store all edges in a vector
+ vecS, // Store all vertices in a vector
undirectedS, // Relations are both ways
// All vertices are commodities
@@ -170,36 +89,16 @@ public:
typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef graph_traits<Graph>::edge_descriptor edge_descriptor;
- typedef property_map<Graph, vertex_index_t>::type IndexMap;
- typedef property_map<Graph, vertex_name_t>::type NameMap;
-
- typedef iterator_property_map<vertex_descriptor*, IndexMap,
- vertex_descriptor,
- vertex_descriptor&> PredecessorMap;
- typedef iterator_property_map<long*, IndexMap, long, long&> DistanceMap;
-
+ typedef property_map<Graph, vertex_name_t>::type NameMap;
typedef property_map<Graph, edge_weight_t>::type EdgeWeightMap;
typedef property_map<Graph, edge_price_point_t>::type PricePointMap;
typedef property_map<Graph, edge_price_ratio_t>::type PriceRatioMap;
- IndexMap indexmap;
PricePointMap pricemap;
PriceRatioMap ratiomap;
- typedef filtered_graph<Graph, recent_edge_weight<EdgeWeightMap,
- PricePointMap,
- PriceRatioMap> > FGraph;
- typedef property_map<FGraph, vertex_name_t>::type FNameMap;
-
- // jww (2012-03-05): Prevents threading
- mutable datetime_t reftime;
- mutable optional<datetime_t> last_reftime;
- mutable optional<datetime_t> oldest;
- mutable optional<datetime_t> last_oldest;
-
commodity_history_t()
- : indexmap(get(vertex_index, price_graph)),
- pricemap(get(edge_price_point, price_graph)),
+ : pricemap(get(edge_price_point, price_graph)),
ratiomap(get(edge_price_ratio, price_graph)) {}
void add_commodity(commodity_t& comm);
@@ -212,23 +111,22 @@ public:
const datetime_t& date);
void map_prices(function<void(datetime_t, const amount_t&)> fn,
- const commodity_t& source,
- const datetime_t& moment,
- const optional<datetime_t>& _oldest = none);
+ const commodity_t& source,
+ const datetime_t& moment,
+ const datetime_t& _oldest = datetime_t());
optional<price_point_t>
- find_price(const commodity_t& source,
- const datetime_t& moment,
- const optional<datetime_t>& oldest = none);
+ find_price(const commodity_t& source,
+ const datetime_t& moment,
+ const datetime_t& oldest = datetime_t());
optional<price_point_t>
- find_price(const commodity_t& source,
- const commodity_t& target,
- const datetime_t& moment,
- const optional<datetime_t>& oldest = none);
+ find_price(const commodity_t& source,
+ const commodity_t& target,
+ const datetime_t& moment,
+ const datetime_t& oldest = datetime_t());
- void print_map(std::ostream& out,
- const optional<datetime_t>& moment = none);
+ void print_map(std::ostream& out, const datetime_t& moment = datetime_t());
};
} // namespace ledger
diff --git a/src/item.cc b/src/item.cc
index 3a2b0b60..4ddbe913 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -172,19 +172,7 @@ void item_t::parse_tags(const char * p,
q = std::strtok(NULL, " \t")) {
const string::size_type len = std::strlen(q);
if (len < 2) continue;
- if (! tag.empty()) {
- string_map::iterator i;
- string field(p + (q - buf.get()));
- if (by_value) {
- bind_scope_t bound_scope(scope, *this);
- i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing);
- } else {
- i = set_tag(tag, string_value(field), overwrite_existing);
- }
- (*i).second.second = true;
- break;
- }
- else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags
+ if (q[0] == ':' && q[len - 1] == ':') { // a series of tags
for (char * r = std::strtok(q + 1, ":");
r;
r = std::strtok(NULL, ":")) {
@@ -199,6 +187,18 @@ void item_t::parse_tags(const char * p,
index = 2;
}
tag = string(q, len - index);
+
+ string_map::iterator i;
+ string field(p + len + index);
+ trim(field);
+ if (by_value) {
+ bind_scope_t bound_scope(scope, *this);
+ i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing);
+ } else {
+ i = set_tag(tag, string_value(field), overwrite_existing);
+ }
+ (*i).second.second = true;
+ break;
}
first = false;
}
diff --git a/src/iterators.h b/src/iterators.h
index 6d490259..5bb9de6f 100644
--- a/src/iterators.h
+++ b/src/iterators.h
@@ -169,8 +169,8 @@ protected:
journal_posts_iterator journal_posts;
xacts_iterator xacts;
xact_posts_iterator posts;
- temporaries_t temps;
xacts_list xact_temps;
+ temporaries_t temps;
public:
posts_commodities_iterator() {}
diff --git a/src/journal.cc b/src/journal.cc
index 55c89dd9..37eacdaf 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -122,14 +122,21 @@ account_t * journal_t::register_account(const string& name, post_t * post,
{
account_t * result = NULL;
+ // If there any account aliases, substitute before creating an account
+ // object.
if (account_aliases.size() > 0) {
accounts_map::const_iterator i = account_aliases.find(name);
if (i != account_aliases.end())
result = (*i).second;
}
+
+ // Create the account object and associate it with the journal; this
+ // is registering the account.
if (! result)
result = master_account->find_account(name);
+ // If the account name being registered is "Unknown", check whether
+ // the payee indicates an account that should be used.
if (result->name == _("Unknown")) {
foreach (account_mapping_t& value, payees_for_unknown_accounts) {
if (value.first.match(post->xact->payee)) {
@@ -139,6 +146,8 @@ account_t * journal_t::register_account(const string& name, post_t * post,
}
}
+ // Now that we have an account, make certain that the account is
+ // "known", if the user has requested validation of that fact.
if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) {
if (! result->has_flags(ACCOUNT_KNOWN)) {
if (! post) {
@@ -313,10 +322,12 @@ bool journal_t::add_xact(xact_t * xact)
}
extend_xact(xact);
-
check_all_metadata(*this, xact);
- foreach (post_t * post, xact->posts)
+
+ foreach (post_t * post, xact->posts) {
+ extend_post(*post, *this);
check_all_metadata(*this, post);
+ }
// If a transaction with this UUID has already been seen, simply do
// not add this one to the journal. However, all automated checks
diff --git a/src/journal.h b/src/journal.h
index 8b750993..ca73c415 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -127,16 +127,17 @@ public:
bool fixed_payees;
bool fixed_commodities;
bool fixed_metadata;
+ bool was_loaded;
+ bool force_checking;
+ bool check_payees;
payee_mappings_t payee_mappings;
account_mappings_t account_mappings;
accounts_map account_aliases;
account_mappings_t payees_for_unknown_accounts;
checksum_map_t checksum_map;
tag_check_exprs_map tag_check_exprs;
+ optional<expr_t> value_expr;
parse_context_t * current_context;
- bool was_loaded;
- bool force_checking;
- bool check_payees;
enum checking_style_t {
CHECK_PERMISSIVE,
diff --git a/src/main.cc b/src/main.cc
index aafbdbcb..dc0798e3 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -80,12 +80,12 @@ int main(int argc, char * argv[], char * envp[])
::textdomain("ledger");
#endif
- std::auto_ptr<global_scope_t> global_scope;
+ global_scope_t * global_scope = NULL;
try {
// Create the session object, which maintains nearly all state relating to
// this invocation of Ledger; and register all known journal parsers.
- global_scope.reset(new global_scope_t(envp));
+ global_scope = new global_scope_t(envp);
global_scope->session().set_flush_on_next_data_file(true);
// Construct an STL-style argument list from the process command arguments
@@ -94,7 +94,7 @@ int main(int argc, char * argv[], char * envp[])
args.push_back(argv[i]);
// Look for options and a command verb in the command-line arguments
- bind_scope_t bound_scope(*global_scope.get(), global_scope->report());
+ bind_scope_t bound_scope(*global_scope, global_scope->report());
args = global_scope->read_command_arguments(bound_scope, args);
if (global_scope->HANDLED(script_)) {
@@ -185,7 +185,7 @@ int main(int argc, char * argv[], char * envp[])
}
}
catch (const std::exception& err) {
- if (global_scope.get())
+ if (global_scope)
global_scope->report_error(err);
else
std::cerr << "Exception during initialization: " << err.what()
@@ -201,15 +201,18 @@ int main(int argc, char * argv[], char * envp[])
// up everything by closing the session and deleting the session object, and
// then shutting down the memory tracing subsystem. Otherwise, let it all
// leak because we're about to exit anyway.
+#if defined(VERIFY_ON)
IF_VERIFY() {
- global_scope.reset();
+ checked_delete(global_scope);
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
#if defined(VERIFY_ON)
shutdown_memory_tracing();
#endif
- } else {
- INFO("Ledger ended");
+ } else
+#endif
+ {
+ INFO("Ledger ended"); // let global_scope leak!
}
// Return the final status to the operating system, either 1 for error or 0
diff --git a/src/op.cc b/src/op.cc
index c8e099e7..23f47f73 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -38,6 +38,16 @@
namespace ledger {
+void intrusive_ptr_add_ref(const expr_t::op_t * op)
+{
+ op->acquire();
+}
+
+void intrusive_ptr_release(const expr_t::op_t * op)
+{
+ op->release();
+}
+
namespace {
value_t split_cons_expr(expr_t::ptr_op_t op)
{
@@ -76,10 +86,12 @@ namespace {
}
}
-expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
+expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth,
+ scope_t * param_scope)
{
- scope_t * scope_ptr = &scope;
- expr_t::ptr_op_t result;
+ scope_t * scope_ptr = &scope;
+ unique_ptr<scope_t> bound_scope;
+ expr_t::ptr_op_t result;
#if defined(DEBUG_ON)
if (SHOW_DEBUG("expr.compile")) {
@@ -93,7 +105,12 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
if (is_ident()) {
DEBUG("expr.compile", "Lookup: " << as_ident() << " in " << scope_ptr);
- if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) {
+ ptr_op_t def;
+ if (param_scope)
+ def = param_scope->lookup(symbol_t::FUNCTION, as_ident());
+ if (! def)
+ def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident());
+ if (def) {
// Identifier references are first looked up at the point of
// definition, and then at the point of every use if they could
// not be found there.
@@ -113,9 +130,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
}
}
else if (is_scope()) {
- shared_ptr<scope_t> subscope(new symbol_scope_t(scope));
+ shared_ptr<scope_t> subscope(new symbol_scope_t(*scope_t::empty_scope));
set_scope(subscope);
- scope_ptr = subscope.get();
+ bound_scope.reset(new bind_scope_t(*scope_ptr, *subscope.get()));
+ scope_ptr = bound_scope.get();
}
else if (kind < TERMINALS) {
result = this;
@@ -123,7 +141,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
else if (kind == O_DEFINE) {
switch (left()->kind) {
case IDENT: {
- ptr_op_t node(right()->compile(*scope_ptr, depth + 1));
+ ptr_op_t node(right()->compile(*scope_ptr, depth + 1, param_scope));
DEBUG("expr.compile",
"Defining " << left()->as_ident() << " in " << scope_ptr);
@@ -136,12 +154,12 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
ptr_op_t node(new op_t(op_t::O_LAMBDA));
node->set_left(left()->right());
node->set_right(right());
- node = node->compile(*scope_ptr, depth + 1);
+
+ node = node->compile(*scope_ptr, depth + 1, param_scope);
DEBUG("expr.compile",
"Defining " << left()->left()->as_ident() << " in " << scope_ptr);
- scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(),
- node);
+ scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), node);
break;
}
// fall through...
@@ -151,12 +169,39 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
}
result = wrap_value(NULL_VALUE);
}
+ else if (kind == O_LAMBDA) {
+ symbol_scope_t params(param_scope ? *param_scope : *scope_t::empty_scope);
+
+ for (ptr_op_t sym = left();
+ sym;
+ sym = sym->has_right() ? sym->right() : NULL) {
+ ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym;
+
+ if (! varname->is_ident()) {
+ std::ostringstream buf;
+ varname->dump(buf, 0);
+ throw_(calc_error,
+ _("Invalid function or lambda parameter: %1") << buf.str());
+ } else {
+ DEBUG("expr.compile",
+ "Defining function parameter " << varname->as_ident());
+ params.define(symbol_t::FUNCTION, varname->as_ident(),
+ new op_t(PLUG));
+ }
+ }
+
+ ptr_op_t rhs(right()->compile(*scope_ptr, depth + 1, &params));
+ if (rhs == right())
+ result = this;
+ else
+ result = copy(left(), rhs);
+ }
if (! result) {
- ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1));
+ ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1, param_scope));
ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ?
(kind == O_LOOKUP ? right() :
- right()->compile(*scope_ptr, depth + 1)) : NULL);
+ right()->compile(*scope_ptr, depth + 1, param_scope)) : NULL);
if (lhs == left() && (! rhs || rhs == right())) {
result = this;
@@ -182,6 +227,23 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
return result;
}
+namespace {
+ expr_t::ptr_op_t lookup_ident(expr_t::ptr_op_t op, scope_t& scope)
+ {
+ expr_t::ptr_op_t def = op->left();
+
+ // If no definition was pre-compiled for this identifier, look it up
+ // in the current scope.
+ if (! def || def->kind == expr_t::op_t::PLUG) {
+ DEBUG("scope.symbols", "Looking for IDENT '" << op->as_ident() << "'");
+ def = scope.lookup(symbol_t::FUNCTION, op->as_ident());
+ }
+ if (! def)
+ throw_(calc_error, _("Unknown identifier '%1'") << op->as_ident());
+ return def;
+ }
+}
+
value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
{
try {
@@ -206,23 +268,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
result = NULL_VALUE;
break;
- case IDENT: {
- ptr_op_t definition = left();
- // If no definition was pre-compiled for this identifier, look it up
- // in the current scope.
- if (! definition) {
- DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'");
- definition = scope.lookup(symbol_t::FUNCTION, as_ident());
+ case IDENT:
+ if (ptr_op_t definition = lookup_ident(this, scope)) {
+ // Evaluating an identifier is the same as calling its definition
+ // directly
+ result = definition->calc(scope, locus, depth + 1);
+ check_type_context(scope, result);
}
- if (! definition)
- throw_(calc_error, _("Unknown identifier '%1'") << as_ident());
-
- // Evaluating an identifier is the same as calling its definition
- // directly
- result = definition->calc(scope, locus, depth + 1);
- check_type_context(scope, result);
break;
- }
case FUNCTION: {
// Evaluating a FUNCTION is the same as calling it directly; this
@@ -260,68 +313,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
break;
}
- case O_CALL: {
- ptr_op_t func = left();
- const string& name(func->as_ident());
-
- func = func->left();
- if (! func)
- func = scope.lookup(symbol_t::FUNCTION, name);
- if (! func)
- throw_(calc_error, _("Calling unknown function '%1'") << name);
-
- call_scope_t call_args(scope, locus, depth + 1);
- if (has_right())
- call_args.set_args(split_cons_expr(right()));
-
- try {
- if (func->is_function())
- result = func->as_function()(call_args);
- else
- result = func->calc(call_args, locus, depth + 1);
- }
- catch (const std::exception&) {
- add_error_context(_("While calling function '%1':" << name));
- throw;
- }
-
+ case O_CALL:
+ result = calc_call(scope, locus, depth);
check_type_context(scope, result);
break;
- }
-
- case O_LAMBDA: {
- call_scope_t& call_args(find_scope<call_scope_t>(scope, true));
- std::size_t args_count(call_args.size());
- std::size_t args_index(0);
- symbol_scope_t call_scope(call_args);
-
- for (ptr_op_t sym = left();
- sym;
- sym = sym->has_right() ? sym->right() : NULL) {
- ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym;
- if (! varname->is_ident()) {
- throw_(calc_error, _("Invalid function definition"));
- }
- else if (args_index == args_count) {
- call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
- wrap_value(NULL_VALUE));
- }
- else {
- DEBUG("expr.compile",
- "Defining function parameter " << varname->as_ident());
- call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
- wrap_value(call_args[args_index++]));
- }
- }
-
- if (args_index < args_count)
- throw_(calc_error,
- _("Too few arguments in function call (saw %1, wanted %2)")
- << args_count << args_index);
- result = right()->calc(call_scope, locus, depth + 1);
+ case O_LAMBDA:
+ result = expr_value(this);
break;
- }
case O_MATCH:
result = (right()->calc(scope, locus, depth + 1).as_mask()
@@ -402,51 +401,12 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
break;
case O_CONS:
- result = left()->calc(scope, locus, depth + 1);
- if (has_right()) {
- value_t temp;
- temp.push_back(result);
-
- ptr_op_t next = right();
- while (next) {
- ptr_op_t value_op;
- if (next->kind == O_CONS) {
- value_op = next->left();
- next = next->has_right() ? next->right() : NULL;
- } else {
- value_op = next;
- next = NULL;
- }
- temp.push_back(value_op->calc(scope, locus, depth + 1));
- }
- result = temp;
- }
+ result = calc_cons(scope, locus, depth);
break;
- case O_SEQ: {
- // An O_SEQ is very similar to an O_CONS except that only the last
- // result value in the series is kept. O_CONS builds up a list.
- //
- // Another feature of O_SEQ is that it pushes a new symbol scope
- // onto the stack. We evaluate the left side here to catch any
- // side-effects, such as definitions in the case of 'x = 1; x'.
- result = left()->calc(scope, locus, depth + 1);
- if (has_right()) {
- ptr_op_t next = right();
- while (next) {
- ptr_op_t value_op;
- if (next->kind == O_SEQ) {
- value_op = next->left();
- next = next->right();
- } else {
- value_op = next;
- next = NULL;
- }
- result = value_op->calc(scope, locus, depth + 1);
- }
- }
+ case O_SEQ:
+ result = calc_seq(scope, locus, depth);
break;
- }
default:
throw_(calc_error, _("Unexpected expr node '%1'") << op_context(this));
@@ -473,6 +433,184 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
}
namespace {
+ expr_t::ptr_op_t find_definition(expr_t::ptr_op_t op, scope_t& scope,
+ expr_t::ptr_op_t * locus, const int depth,
+ int recursion_depth = 0)
+ {
+ // If the object we are apply call notation to is a FUNCTION value
+ // or a O_LAMBDA expression, then this is the object we want to
+ // call.
+ if (op->is_function() || op->kind == expr_t::op_t::O_LAMBDA)
+ return op;
+
+ if (recursion_depth > 256)
+ throw_(value_error, _("Function recursion_depth too deep (> 256)"));
+
+ // If it's an identifier, look up its definition and see if it's a
+ // function.
+ if (op->is_ident())
+ return find_definition(lookup_ident(op, scope), scope,
+ locus, depth, recursion_depth + 1);
+
+ // Value objects might be callable if they contain an expression.
+ if (op->is_value()) {
+ value_t def(op->as_value());
+ if (is_expr(def))
+ return find_definition(as_expr(def), scope, locus, depth,
+ recursion_depth + 1);
+ else
+ throw_(value_error, _("Cannot call %1 as a function") << def.label());
+ }
+
+ // Resolve ordinary expressions.
+ return find_definition(expr_t::op_t::wrap_value(op->calc(scope, locus,
+ depth + 1)),
+ scope, locus, depth + 1, recursion_depth + 1);
+ }
+
+ value_t call_lambda(expr_t::ptr_op_t func, scope_t& scope,
+ call_scope_t& call_args, expr_t::ptr_op_t * locus,
+ const int depth)
+ {
+ std::size_t args_index(0);
+ std::size_t args_count(call_args.size());
+
+ symbol_scope_t args_scope(*scope_t::empty_scope);
+
+ for (expr_t::ptr_op_t sym = func->left();
+ sym;
+ sym = sym->has_right() ? sym->right() : NULL) {
+ expr_t::ptr_op_t varname =
+ sym->kind == expr_t::op_t::O_CONS ? sym->left() : sym;
+ if (! varname->is_ident()) {
+ throw_(calc_error, _("Invalid function definition"));
+ }
+ else if (args_index == args_count) {
+ DEBUG("expr.calc", "Defining function argument as null: "
+ << varname->as_ident());
+ args_scope.define(symbol_t::FUNCTION, varname->as_ident(),
+ expr_t::op_t::wrap_value(NULL_VALUE));
+ }
+ else {
+ DEBUG("expr.calc", "Defining function argument from call_args: "
+ << varname->as_ident());
+ args_scope.define(symbol_t::FUNCTION, varname->as_ident(),
+ expr_t::op_t::wrap_value(call_args[args_index++]));
+ }
+ }
+
+ if (args_index < args_count)
+ throw_(calc_error,
+ _("Too few arguments in function call (saw %1, wanted %2)")
+ << args_count << args_index);
+
+ if (func->right()->is_scope()) {
+ bind_scope_t outer_scope(scope, *func->right()->as_scope());
+ bind_scope_t bound_scope(outer_scope, args_scope);
+
+ return func->right()->left()->calc(bound_scope, locus, depth + 1);
+ } else {
+ return func->right()->calc(args_scope, locus, depth + 1);
+ }
+ }
+}
+
+
+value_t expr_t::op_t::call(const value_t& args, scope_t& scope,
+ ptr_op_t * locus, const int depth)
+{
+ call_scope_t call_args(scope, locus, depth + 1);
+ call_args.set_args(args);
+
+ if (is_function())
+ return as_function()(call_args);
+ else if (kind == O_LAMBDA)
+ return call_lambda(this, scope, call_args, locus, depth);
+ else
+ return find_definition(this, scope, locus, depth)
+ ->calc(call_args, locus, depth);
+}
+
+value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus,
+ const int depth)
+{
+ ptr_op_t func = left();
+ string name = func->is_ident() ? func->as_ident() : "<value expr>";
+
+ func = find_definition(func, scope, locus, depth);
+
+ call_scope_t call_args(scope, locus, depth + 1);
+ if (has_right())
+ call_args.set_args(split_cons_expr(right()));
+
+ try {
+ if (func->is_function()) {
+ return func->as_function()(call_args);
+ } else {
+ assert(func->kind == O_LAMBDA);
+ return call_lambda(func, scope, call_args, locus, depth);
+ }
+ }
+ catch (const std::exception&) {
+ add_error_context(_("While calling function '%1 %2':" << name
+ << call_args.args));
+ throw;
+ }
+}
+
+value_t expr_t::op_t::calc_cons(scope_t& scope, ptr_op_t * locus,
+ const int depth)
+{
+ value_t result = left()->calc(scope, locus, depth + 1);
+ if (has_right()) {
+ value_t temp;
+ temp.push_back(result);
+
+ ptr_op_t next = right();
+ while (next) {
+ ptr_op_t value_op;
+ if (next->kind == O_CONS) {
+ value_op = next->left();
+ next = next->has_right() ? next->right() : NULL;
+ } else {
+ value_op = next;
+ next = NULL;
+ }
+ temp.push_back(value_op->calc(scope, locus, depth + 1));
+ }
+ result = temp;
+ }
+ return result;
+}
+
+value_t expr_t::op_t::calc_seq(scope_t& scope, ptr_op_t * locus,
+ const int depth)
+{
+ // An O_SEQ is very similar to an O_CONS except that only the last
+ // result value in the series is kept. O_CONS builds up a list.
+ //
+ // Another feature of O_SEQ is that it pushes a new symbol scope onto
+ // the stack. We evaluate the left side here to catch any
+ // side-effects, such as definitions in the case of 'x = 1; x'.
+ value_t result = left()->calc(scope, locus, depth + 1);
+ if (has_right()) {
+ ptr_op_t next = right();
+ while (next) {
+ ptr_op_t value_op;
+ if (next->kind == O_SEQ) {
+ value_op = next->left();
+ next = next->right();
+ } else {
+ value_op = next;
+ next = NULL;
+ }
+ result = value_op->calc(scope, locus, depth + 1);
+ }
+ }
+ return result;
+}
+
+namespace {
bool print_cons(std::ostream& out, const expr_t::const_ptr_op_t op,
const expr_t::op_t::context_t& context)
{
@@ -743,6 +881,10 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
out << " ";
switch (kind) {
+ case PLUG:
+ out << "PLUG";
+ break;
+
case VALUE:
out << "VALUE: ";
as_value().dump(out);
@@ -803,7 +945,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
// An identifier is a special non-terminal, in that its left() can
// hold the compiled definition of the identifier.
- if (kind > TERMINALS || is_scope()) {
+ if (kind > TERMINALS || is_scope() || is_ident()) {
if (left()) {
left()->dump(out, depth + 1);
if (kind > UNARY_OPERATORS && has_right())
diff --git a/src/op.h b/src/op.h
index 192c1f5e..03fcf816 100644
--- a/src/op.h
+++ b/src/op.h
@@ -69,6 +69,7 @@ private:
public:
enum kind_t {
// Constants
+ PLUG,
VALUE,
IDENT,
@@ -260,12 +261,8 @@ private:
checked_delete(this);
}
- friend inline void intrusive_ptr_add_ref(const op_t * op) {
- op->acquire();
- }
- friend inline void intrusive_ptr_release(const op_t * op) {
- op->release();
- }
+ friend void intrusive_ptr_add_ref(const op_t * op);
+ friend void intrusive_ptr_release(const op_t * op);
ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
ptr_op_t node(new_node(kind, _left, _right));
@@ -278,10 +275,14 @@ public:
static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
ptr_op_t _right = NULL);
- ptr_op_t compile(scope_t& scope, const int depth = 0);
+ ptr_op_t compile(scope_t& scope, const int depth = 0,
+ scope_t * param_scope = NULL);
value_t calc(scope_t& scope, ptr_op_t * locus = NULL,
const int depth = 0);
+ value_t call(const value_t& args, scope_t& scope,
+ ptr_op_t * locus = NULL, const int depth = 0);
+
struct context_t
{
ptr_op_t expr_op;
@@ -309,6 +310,11 @@ public:
static ptr_op_t wrap_functor(expr_t::func_t fobj);
static ptr_op_t wrap_scope(shared_ptr<scope_t> sobj);
+private:
+ value_t calc_call(scope_t& scope, ptr_op_t * locus, const int depth);
+ value_t calc_cons(scope_t& scope, ptr_op_t * locus, const int depth);
+ value_t calc_seq(scope_t& scope, ptr_op_t * locus, const int depth);
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
@@ -359,13 +365,6 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) {
return temp;
}
-inline expr_t::ptr_op_t
-expr_t::op_t::wrap_scope(shared_ptr<scope_t> sobj) {
- ptr_op_t temp(new op_t(op_t::SCOPE));
- temp->set_scope(sobj);
- return temp;
-}
-
#define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1))
#define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x)
diff --git a/src/option.h b/src/option.h
index dc1099db..36dba3a4 100644
--- a/src/option.h
+++ b/src/option.h
@@ -61,9 +61,9 @@ protected:
option_t& operator=(const option_t&);
public:
- T * parent;
- value_t value;
- bool wants_arg;
+ T * parent;
+ string value;
+ bool wants_arg;
option_t(const char * _name, const char _ch = '\0')
: name(_name), name_len(std::strlen(name)), ch(_ch),
@@ -94,7 +94,8 @@ public:
out << std::right << desc();
if (wants_arg) {
out << " = ";
- value.print(out, 42);
+ out.width(42);
+ out << value;
} else {
out.width(45);
out << ' ';
@@ -123,43 +124,48 @@ public:
return handled;
}
- string& str() {
+ string str() const {
assert(handled);
- if (! value)
+ if (value.empty())
throw_(std::runtime_error, _("No argument provided for %1") << desc());
- return value.as_string_lval();
+ return value;
}
- string str() const {
- assert(handled);
- if (! value)
- throw_(std::runtime_error, _("No argument provided for %1") << desc());
- return value.as_string();
+ void on(const char * whence) {
+ on(string(whence));
}
+ void on(const optional<string>& whence) {
+ handler_thunk(whence);
- void on_only(const optional<string>& whence) {
handled = true;
source = whence;
}
- void on(const optional<string>& whence, const string& str) {
- on_with(whence, string_value(str));
+
+ void on(const char * whence, const string& str) {
+ on(string(whence), str);
}
- virtual void on_with(const optional<string>& whence,
- const value_t& val) {
+ void on(const optional<string>& whence, const string& str) {
+ string before = value;
+
+ handler_thunk(whence, str);
+
+ if (value == before)
+ value = str;
+
handled = true;
- value = val;
source = whence;
}
void off() {
handled = false;
- value = value_t();
+ value = "";
source = none;
}
- virtual void handler_thunk(call_scope_t&) {}
+ virtual void handler_thunk(const optional<string>&) {}
+ virtual void handler_thunk(const optional<string>&, const string&) {}
- virtual void handler(call_scope_t& args) {
+ value_t handler(call_scope_t& args) {
if (wants_arg) {
if (args.size() < 2)
throw_(std::runtime_error, _("No argument provided for %1") << desc());
@@ -167,7 +173,7 @@ public:
throw_(std::runtime_error, _("To many arguments provided for %1") << desc());
else if (! args[0].is_string())
throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
- on_with(args.get<string>(0), args[1]);
+ on(args.get<string>(0), args.get<string>(1));
}
else if (args.size() < 1) {
throw_(std::runtime_error, _("No argument provided for %1") << desc());
@@ -176,27 +182,18 @@ public:
throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
}
else {
- on_only(args.get<string>(0));
+ on(args.get<string>(0));
}
-
- handler_thunk(args);
- }
-
- virtual value_t handler_wrapper(call_scope_t& args) {
- handler(args);
return true;
}
virtual value_t operator()(call_scope_t& args) {
if (! args.empty()) {
args.push_front(string_value("?expr"));
- return handler_wrapper(args);
+ return handler(args);
}
else if (wants_arg) {
- if (handled)
- return value;
- else
- return NULL_VALUE;
+ return string_value(value);
}
else {
return handled;
@@ -213,17 +210,18 @@ public:
name ## option_t() : option_t<type>(#name), base
#define DECL1(type, name, vartype, var, value) \
vartype var ; \
- name ## option_t() : option_t<type>(#name), var(value)
+ name ## option_t() : option_t<type>(#name), var value
-#define DO() virtual void handler_thunk(call_scope_t&)
-#define DO_(var) virtual void handler_thunk(call_scope_t& var)
+#define DO() virtual void handler_thunk(const optional<string>& whence)
+#define DO_(var) virtual void handler_thunk(const optional<string>& whence, \
+ const string& var)
#define END(name) name ## handler
#define COPY_OPT(name, other) name ## handler(other.name ## handler)
#define MAKE_OPT_HANDLER(type, x) \
- expr_t::op_t::wrap_functor(bind(&option_t<type>::handler_wrapper, x, _1))
+ expr_t::op_t::wrap_functor(bind(&option_t<type>::handler, x, _1))
#define MAKE_OPT_FUNCTOR(type, x) \
expr_t::op_t::wrap_functor(bind(&option_t<type>::operator(), x, _1))
@@ -284,6 +282,10 @@ inline bool is_eq(const char * p, const char * n) {
} \
END(name)
+#define OTHER(name) \
+ parent->HANDLER(name).parent = parent; \
+ parent->HANDLER(name)
+
bool process_option(const string& whence, const string& name, scope_t& scope,
const char * arg, const string& varname);
diff --git a/src/parser.cc b/src/parser.cc
index 2c9069d7..360ac93d 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -54,20 +54,6 @@ expr_t::parser_t::parse_value_term(std::istream& in,
node = new op_t(op_t::IDENT);
node->set_ident(ident);
-
- // An identifier followed by ( represents a function call
- tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT));
- if (tok.kind == token_t::LPAREN) {
- op_t::kind_t kind = op_t::O_CALL;
- ptr_op_t call_node(new op_t(kind));
- call_node->set_left(node);
- node = call_node;
-
- push_token(tok); // let the parser see it again
- node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE)));
- } else {
- push_token(tok);
- }
break;
}
@@ -85,8 +71,9 @@ expr_t::parser_t::parse_value_term(std::istream& in,
return node;
}
+
expr_t::ptr_op_t
-expr_t::parser_t::parse_dot_expr(std::istream& in,
+expr_t::parser_t::parse_call_expr(std::istream& in,
const parse_flags_t& tflags) const
{
ptr_op_t node(parse_value_term(in, tflags));
@@ -94,11 +81,36 @@ expr_t::parser_t::parse_dot_expr(std::istream& in,
if (node && ! tflags.has_flags(PARSE_SINGLE)) {
while (true) {
token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT));
+ if (tok.kind == token_t::LPAREN) {
+ ptr_op_t prev(node);
+ node = new op_t(op_t::O_CALL);
+ node->set_left(prev);
+ push_token(tok); // let the parser see the '(' again
+ node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE)));
+ } else {
+ push_token(tok);
+ break;
+ }
+ }
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_dot_expr(std::istream& in,
+ const parse_flags_t& tflags) const
+{
+ ptr_op_t node(parse_call_expr(in, tflags));
+
+ if (node && ! tflags.has_flags(PARSE_SINGLE)) {
+ while (true) {
+ token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT));
if (tok.kind == token_t::DOT) {
ptr_op_t prev(node);
node = new op_t(op_t::O_LOOKUP);
node->set_left(prev);
- node->set_right(parse_value_term(in, tflags));
+ node->set_right(parse_call_expr(in, tflags));
if (! node->right())
throw_(parse_error,
_("%1 operator not followed by argument") << tok.symbol);
@@ -470,7 +482,9 @@ expr_t::parser_t::parse_lambda_expr(std::istream& in,
ptr_op_t prev(node);
node = new op_t(op_t::O_LAMBDA);
node->set_left(prev);
- node->set_right(parse_querycolon_expr(in, tflags));
+ ptr_op_t scope(new op_t(op_t::SCOPE));
+ scope->set_left(parse_querycolon_expr(in, tflags));
+ node->set_right(scope);
} else {
push_token(tok);
}
diff --git a/src/parser.h b/src/parser.h
index 75fd9a41..db16a919 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -81,6 +81,8 @@ class expr_t::parser_t : public noncopyable
ptr_op_t parse_value_term(std::istream& in,
const parse_flags_t& flags) const;
+ ptr_op_t parse_call_expr(std::istream& in,
+ const parse_flags_t& flags) const;
ptr_op_t parse_dot_expr(std::istream& in,
const parse_flags_t& flags) const;
ptr_op_t parse_unary_expr(std::istream& in,
diff --git a/src/pool.cc b/src/pool.cc
index 2c094d47..0118a97d 100644
--- a/src/pool.cc
+++ b/src/pool.cc
@@ -56,7 +56,7 @@ commodity_t * commodity_pool_t::create(const string& symbol)
{
shared_ptr<commodity_t::base_t>
base_commodity(new commodity_t::base_t(symbol));
- std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
+ shared_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
DEBUG("pool.commodities", "Creating base commodity " << symbol);
@@ -67,17 +67,29 @@ commodity_t * commodity_pool_t::create(const string& symbol)
*commodity->qualified_symbol += "\"";
}
- DEBUG("pool.commodities",
- "Creating commodity '" << commodity->symbol() << "'");
+ DEBUG("pool.commodities", "Creating commodity '" << symbol << "'");
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_map::value_type(commodity->mapping_key(),
- commodity.get()));
+#if defined(DEBUG_ON)
+ std::pair<commodities_map::iterator, bool> result =
+#endif
+ commodities.insert(commodities_map::value_type(symbol, commodity));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
commodity_price_history.add_commodity(*commodity.get());
- return commodity.release();
+ return commodity.get();
+}
+
+commodity_t * commodity_pool_t::find(const string& symbol)
+{
+ DEBUG("pool.commodities", "Find commodity " << symbol);
+
+ commodities_map::const_iterator i = commodities.find(symbol);
+ if (i != commodities.end())
+ return (*i).second.get();
+ return NULL;
}
commodity_t * commodity_pool_t::find_or_create(const string& symbol)
@@ -88,97 +100,103 @@ commodity_t * commodity_pool_t::find_or_create(const string& symbol)
return create(symbol);
}
-commodity_t * commodity_pool_t::find(const string& symbol)
+commodity_t * commodity_pool_t::alias(const string& name, commodity_t& referent)
{
- DEBUG("pool.commodities", "Find commodity " << symbol);
+ commodities_map::const_iterator i = commodities.find(referent.symbol());
+ assert(i != commodities.end());
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
- return NULL;
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_map::value_type(name, (*i).second));
+ assert(result.second);
+
+ return (*result.first).second.get();
}
commodity_t *
commodity_pool_t::create(const string& symbol, const annotation_t& details)
{
- commodity_t * new_comm = create(symbol);
- if (! new_comm)
- return NULL;
+ DEBUG("pool.commodities", "commodity_pool_t::create[ann] "
+ << "symbol " << symbol << std::endl << details);
if (details)
- return find_or_create(*new_comm, details);
+ return create(*find_or_create(symbol), details);
else
- return new_comm;
+ return create(symbol);
}
-string commodity_pool_t::make_qualified_name(const commodity_t& comm,
- const annotation_t& details)
+commodity_t *
+commodity_pool_t::find(const string& symbol, const annotation_t& details)
{
- assert(details);
-
- if (details.price && details.price->sign() < 0)
- throw_(amount_error, _("A commodity's price may not be negative"));
-
- std::ostringstream name;
- comm.print(name);
- details.print(name, comm.pool().keep_base);
-
-#if defined(DEBUG_ON)
- if (comm.qualified_symbol)
- DEBUG("pool.commodities", "make_qualified_name for "
- << *comm.qualified_symbol << std::endl << details);
-#endif
- DEBUG("pool.commodities", "qualified_name is " << name.str());
+ DEBUG("pool.commodities", "commodity_pool_t::find[ann] "
+ << "symbol " << symbol << std::endl << details);
- return name.str();
+ if (details) {
+ annotated_commodities_map::const_iterator i =
+ annotated_commodities.find
+ (annotated_commodities_map::key_type(symbol, details));
+ if (i != annotated_commodities.end()) {
+ DEBUG("pool.commodities", "commodity_pool_t::find[ann] found "
+ << "symbol " << (*i).second->symbol() << std::endl
+ << as_annotated_commodity(*(*i).second.get()).details);
+ return (*i).second.get();
+ } else {
+ return NULL;
+ }
+ } else {
+ return find(symbol);
+ }
}
commodity_t *
-commodity_pool_t::find(const string& symbol, const annotation_t& details)
+commodity_pool_t::find_or_create(const string& symbol,
+ const annotation_t& details)
{
- commodity_t * comm = find(symbol);
- if (! comm)
- return NULL;
+ DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann] "
+ << "symbol " << symbol << std::endl << details);
if (details) {
- string name = make_qualified_name(*comm, details);
-
- if (commodity_t * ann_comm = find(name)) {
+ if (commodity_t * ann_comm = find(symbol, details)) {
assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
return ann_comm;
+ } else {
+ return create(symbol, details);
}
- return NULL;
} else {
- return comm;
+ return find_or_create(symbol);
}
}
commodity_t *
-commodity_pool_t::find_or_create(const string& symbol,
- const annotation_t& details)
+commodity_pool_t::find_or_create(commodity_t& comm, const annotation_t& details)
{
- commodity_t * comm = find_or_create(symbol);
- if (! comm)
- return NULL;
+ DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann:comm] "
+ << "symbol " << comm.symbol() << std::endl << details);
- if (details)
- return find_or_create(*comm, details);
- else
- return comm;
+ if (details) {
+ if (commodity_t * ann_comm = find(comm.symbol(), details)) {
+ assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
+ return ann_comm;
+ } else {
+ return create(comm, details);
+ }
+ } else {
+ return &comm;
+ }
}
-commodity_t *
+annotated_commodity_t *
commodity_pool_t::create(commodity_t& comm,
- const annotation_t& details,
- const string& mapping_key)
+ const annotation_t& details)
{
+ DEBUG("pool.commodities", "commodity_pool_t::create[ann:comm] "
+ << "symbol " << comm.symbol() << std::endl << details);
+
assert(comm);
assert(! comm.has_annotation());
assert(details);
- assert(! mapping_key.empty());
- unique_ptr<commodity_t> commodity
- (new annotated_commodity_t(&comm, details));
+ shared_ptr<annotated_commodity_t>
+ commodity(new annotated_commodity_t(&comm, details));
comm.add_flags(COMMODITY_SAW_ANNOTATED);
if (details.price) {
@@ -193,34 +211,19 @@ commodity_pool_t::create(commodity_t& comm,
DEBUG("pool.commodities", "Creating annotated commodity "
<< "symbol " << commodity->symbol()
- << " key " << mapping_key << std::endl << details);
+ << std::endl << details);
- // Add the fully annotated name to the map, so that this symbol may
- // quickly be found again.
- commodity->mapping_key_ = mapping_key;
-
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_map::value_type(mapping_key,
- commodity.get()));
+#if defined(DEBUG_ON)
+ std::pair<annotated_commodities_map::iterator, bool> result =
+#endif
+ annotated_commodities.insert(annotated_commodities_map::value_type
+ (annotated_commodities_map::key_type
+ (comm.symbol(), details), commodity));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
- return commodity.release();
-}
-
-commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
- const annotation_t& details)
-{
- assert(comm);
- assert(details);
-
- string name = make_qualified_name(comm, details);
- assert(! name.empty());
-
- if (commodity_t * ann_comm = find(name)) {
- assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
- return ann_comm;
- }
- return create(comm, details, name);
+ return commodity.get();
}
void commodity_pool_t::exchange(commodity_t& commodity,
@@ -241,6 +244,7 @@ cost_breakdown_t
commodity_pool_t::exchange(const amount_t& amount,
const amount_t& cost,
const bool is_per_unit,
+ const bool add_price,
const optional<datetime_t>& moment,
const optional<string>& tag)
{
diff --git a/src/pool.h b/src/pool.h
index 709f5c71..eb630781 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -47,6 +47,7 @@
#define _POOL_H
#include "history.h"
+#include "annotate.h"
namespace ledger {
@@ -66,51 +67,47 @@ public:
* explicitly by calling the create methods of commodity_pool_t, or
* implicitly by parsing a commoditized amount.
*/
- typedef std::map<string, commodity_t *> commodities_map;
+ typedef std::map<string, shared_ptr<commodity_t> > commodities_map;
+ typedef std::map<std::pair<string, annotation_t>,
+ shared_ptr<annotated_commodity_t> > annotated_commodities_map;
- commodities_map commodities;
- commodity_history_t commodity_price_history;
- commodity_t * null_commodity;
- commodity_t * default_commodity;
+ commodities_map commodities;
+ annotated_commodities_map annotated_commodities;
+ commodity_history_t commodity_price_history;
+ commodity_t * null_commodity;
+ commodity_t * default_commodity;
- bool keep_base; // --base
-
- optional<path> price_db; // --price-db=
- long quote_leeway; // --leeway=
- bool get_quotes; // --download
-
- static shared_ptr<commodity_pool_t> current_pool;
+ bool keep_base; // --base
+ optional<path> price_db; // --price-db=
+ long quote_leeway; // --leeway=
+ bool get_quotes; // --download
function<optional<price_point_t>
- (commodity_t& commodity, const optional<commodity_t&>& in_terms_of)>
+ (commodity_t& commodity, const commodity_t * in_terms_of)>
get_commodity_quote;
- explicit commodity_pool_t();
+ static shared_ptr<commodity_pool_t> current_pool;
+ explicit commodity_pool_t();
virtual ~commodity_pool_t() {
TRACE_DTOR(commodity_pool_t);
- foreach (commodities_map::value_type& pair, commodities)
- checked_delete(pair.second);
}
- string make_qualified_name(const commodity_t& comm,
- const annotation_t& details);
-
commodity_t * create(const string& symbol);
commodity_t * find(const string& name);
commodity_t * find_or_create(const string& symbol);
+ commodity_t * alias(const string& name, commodity_t& referent);
- commodity_t * create(const string& symbol, const annotation_t& details);
- commodity_t * find(const string& symbol, const annotation_t& details);
+ commodity_t * create(const string& symbol,
+ const annotation_t& details);
+ commodity_t * find(const string& symbol,
+ const annotation_t& details);
commodity_t * find_or_create(const string& symbol,
const annotation_t& details);
+ commodity_t * find_or_create(commodity_t& comm, const annotation_t& details);
- commodity_t * create(commodity_t& comm,
- const annotation_t& details,
- const string& mapping_key);
-
- commodity_t * find_or_create(commodity_t& comm,
- const annotation_t& details);
+ annotated_commodity_t * create(commodity_t& comm,
+ const annotation_t& details);
// Exchange one commodity for another, while recording the factored price.
@@ -121,6 +118,7 @@ public:
cost_breakdown_t exchange(const amount_t& amount,
const amount_t& cost,
const bool is_per_unit = false,
+ const bool add_price = true,
const optional<datetime_t>& moment = none,
const optional<string>& tag = none);
@@ -144,6 +142,7 @@ private:
void serialize(Archive& ar, const unsigned int /* version */) {
ar & current_pool;
ar & commodities;
+ ar & annotated_commodities;
ar & null_commodity;
ar & default_commodity;
ar & keep_base;
diff --git a/src/post.cc b/src/post.cc
index 191a9142..babb1292 100644
--- a/src/post.cc
+++ b/src/post.cc
@@ -36,6 +36,7 @@
#include "account.h"
#include "journal.h"
#include "format.h"
+#include "pool.h"
namespace ledger {
@@ -239,6 +240,15 @@ namespace {
return post.amount;
}
+ value_t get_price(post_t& post) {
+ if (post.amount.is_null())
+ return 0L;
+ if (post.amount.has_annotation() && post.amount.annotation().price)
+ return *post.amount.price();
+ else
+ return get_cost(post);
+ }
+
value_t get_total(post_t& post) {
if (post.xdata_ && ! post.xdata_->total.is_null())
return post.xdata_->total;
@@ -474,6 +484,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind,
return WRAP_FUNCTOR(get_wrapper<&get_payee>);
else if (name == "primary")
return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>);
+ else if (name == "price")
+ return WRAP_FUNCTOR(get_wrapper<&get_price>);
else if (name == "parent")
return WRAP_FUNCTOR(get_wrapper<&get_xact>);
break;
@@ -638,6 +650,43 @@ void post_t::set_reported_account(account_t * acct)
acct->xdata().reported_posts.push_back(this);
}
+void extend_post(post_t& post, journal_t& journal)
+{
+ commodity_t& comm(post.amount.commodity());
+
+ annotation_t * details =
+ (comm.has_annotation() ?
+ &as_annotated_commodity(comm).details : NULL);
+
+ if (! details || ! details->value_expr) {
+ optional<expr_t> value_expr;
+
+ if (optional<value_t> data = post.get_tag(_("Value")))
+ value_expr = expr_t(data->to_string());
+
+ if (! value_expr)
+ value_expr = post.account->value_expr;
+
+ if (! value_expr)
+ value_expr = post.amount.commodity().value_expr();
+
+ if (! value_expr)
+ value_expr = journal.value_expr;
+
+ if (value_expr) {
+ if (! details) {
+ annotation_t new_details;
+ new_details.value_expr = value_expr;
+ commodity_t * new_comm =
+ commodity_pool_t::current_pool->find_or_create(comm, new_details);
+ post.amount.set_commodity(*new_comm);
+ } else {
+ details->value_expr = value_expr;
+ }
+ }
+ }
+}
+
void to_xml(std::ostream& out, const post_t& post)
{
push_xml x(out, "posting", true);
diff --git a/src/post.h b/src/post.h
index ce33fefc..7d93b1cc 100644
--- a/src/post.h
+++ b/src/post.h
@@ -58,7 +58,8 @@ public:
#define POST_COST_CALCULATED 0x0080 // posting's cost was calculated
#define POST_COST_IN_FULL 0x0100 // cost specified using @@
#define POST_COST_FIXATED 0x0200 // cost is fixed using = indicator
-#define POST_ANONYMIZED 0x0400 // a temporary, anonymous posting
+#define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@)
+#define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting
xact_t * xact; // only set for posts of regular xacts
account_t * account;
@@ -259,6 +260,9 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class journal_t;
+void extend_post(post_t& post, journal_t& journal);
+
void to_xml(std::ostream& out, const post_t& post);
} // namespace ledger
diff --git a/src/predicate.cc b/src/predicate.cc
deleted file mode 100644
index 58d6c752..00000000
--- a/src/predicate.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2003-2012, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <system.hh>
-
-#include "predicate.h"
-#include "query.h"
-#include "op.h"
-
-namespace ledger {
-
-} // namespace ledger
diff --git a/src/print.cc b/src/print.cc
index c544c4e0..9e52ce95 100644
--- a/src/print.cc
+++ b/src/print.cc
@@ -133,7 +133,7 @@ namespace {
std::size_t columns =
(report.HANDLED(columns_) ?
- static_cast<std::size_t>(report.HANDLER(columns_).value.to_long()) : 80);
+ lexical_cast<std::size_t>(report.HANDLER(columns_).str()) : 80);
if (xact.note)
print_note(out, *xact.note, xact.has_flags(ITEM_NOTE_ON_NEXT_LINE),
@@ -191,8 +191,8 @@ namespace {
unistring name(pbuf.str());
std::size_t account_width =
- (report.HANDLER(account_width_).specified ?
- static_cast<std::size_t>(report.HANDLER(account_width_).value.to_long()) : 36);
+ (report.HANDLED(account_width_) ?
+ lexical_cast<std::size_t>(report.HANDLER(account_width_).str()) : 36);
if (account_width < name.length())
account_width = name.length();
@@ -218,13 +218,14 @@ namespace {
// first.
}
else {
- int amount_width =
- (report.HANDLER(amount_width_).specified ?
- report.HANDLER(amount_width_).value.to_int() : 12);
+ std::size_t amount_width =
+ (report.HANDLED(amount_width_) ?
+ lexical_cast<std::size_t>(report.HANDLER(amount_width_).str()) :
+ 12);
std::ostringstream amt_str;
- value_t(post->amount).print(amt_str, amount_width, -1,
- AMOUNT_PRINT_RIGHT_JUSTIFY |
+ value_t(post->amount).print(amt_str, static_cast<int>(amount_width),
+ -1, AMOUNT_PRINT_RIGHT_JUSTIFY |
AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS);
amt = amt_str.str();
}
diff --git a/src/py_amount.cc b/src/py_amount.cc
index 25ec8e26..ea69dec5 100644
--- a/src/py_amount.cc
+++ b/src/py_amount.cc
@@ -48,12 +48,12 @@ namespace {
return amount.value(CURRENT_TIME());
}
boost::optional<amount_t> py_value_1(const amount_t& amount,
- commodity_t& in_terms_of) {
+ const commodity_t * in_terms_of) {
return amount.value(CURRENT_TIME(), in_terms_of);
}
boost::optional<amount_t> py_value_2(const amount_t& amount,
- commodity_t& in_terms_of,
- datetime_t& moment) {
+ const commodity_t * in_terms_of,
+ const datetime_t& moment) {
return amount.value(moment, in_terms_of);
}
diff --git a/src/py_balance.cc b/src/py_balance.cc
index 6c9ccb24..2ae546f1 100644
--- a/src/py_balance.cc
+++ b/src/py_balance.cc
@@ -48,12 +48,12 @@ namespace {
return balance.value(CURRENT_TIME());
}
boost::optional<balance_t> py_value_1(const balance_t& balance,
- commodity_t& in_terms_of) {
+ const commodity_t * in_terms_of) {
return balance.value(CURRENT_TIME(), in_terms_of);
}
boost::optional<balance_t> py_value_2(const balance_t& balance,
- commodity_t& in_terms_of,
- datetime_t& moment) {
+ const commodity_t * in_terms_of,
+ const datetime_t& moment) {
return balance.value(moment, in_terms_of);
}
@@ -201,8 +201,6 @@ void export_balance()
.def("value", py_value_1, args("in_terms_of"))
.def("value", py_value_2, args("in_terms_of", "moment"))
- .def("price", &balance_t::price)
-
.def("__nonzero__", &balance_t::is_nonzero)
.def("is_nonzero", &balance_t::is_nonzero)
.def("is_zero", &balance_t::is_zero)
diff --git a/src/py_commodity.cc b/src/py_commodity.cc
index b5230850..c75b5e64 100644
--- a/src/py_commodity.cc
+++ b/src/py_commodity.cc
@@ -96,14 +96,15 @@ namespace {
pool.exchange(commodity, per_unit_cost, moment);
}
- cost_breakdown_t py_exchange_5(commodity_pool_t& pool,
+ cost_breakdown_t py_exchange_7(commodity_pool_t& pool,
const amount_t& amount,
const amount_t& cost,
const bool is_per_unit,
+ const bool add_prices,
const boost::optional<datetime_t>& moment,
const boost::optional<string>& tag)
{
- return pool.exchange(amount, cost, is_per_unit, moment, tag);
+ return pool.exchange(amount, cost, is_per_unit, add_prices, moment, tag);
}
commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol)
@@ -115,7 +116,7 @@ namespace {
(string("Could not find commodity ") + symbol).c_str());
throw_error_already_set();
}
- return (*i).second;
+ return (*i).second.get();
}
python::list py_pool_keys(commodity_pool_t& pool) {
@@ -168,13 +169,15 @@ namespace {
py_pool_commodities_values_begin(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.begin(),
- bind(&commodity_pool_t::commodities_map::value_type::second, _1));
+ bind(&shared_ptr<commodity_t>::get,
+ bind(&commodity_pool_t::commodities_map::value_type::second, _1)));
}
commodities_map_seconds_iterator
py_pool_commodities_values_end(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.end(),
- bind(&commodity_pool_t::commodities_map::value_type::second, _1));
+ bind(&shared_ptr<commodity_t>::get,
+ bind(&commodity_pool_t::commodities_map::value_type::second, _1)));
}
void py_add_price_2(commodity_t& commodity,
@@ -267,8 +270,6 @@ void export_commodity()
make_getter(&commodity_pool_t::get_commodity_quote),
make_setter(&commodity_pool_t::get_commodity_quote))
- .def("make_qualified_name", &commodity_pool_t::make_qualified_name)
-
.def("create", py_create_1, return_internal_reference<>())
.def("create", py_create_2, return_internal_reference<>())
@@ -280,7 +281,7 @@ void export_commodity()
.def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>())
.def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>())
- .def("exchange", py_exchange_5)
+ .def("exchange", py_exchange_7)
.def("parse_price_directive", &commodity_pool_t::parse_price_directive)
.def("parse_price_expression", &commodity_pool_t::parse_price_expression,
@@ -359,7 +360,6 @@ void export_commodity()
.add_property("base_symbol", &commodity_t::base_symbol)
.add_property("symbol", &commodity_t::symbol)
- .add_property("mapping_key", &commodity_t::mapping_key)
.add_property("name", &commodity_t::name, &commodity_t::set_name)
.add_property("note", &commodity_t::note, &commodity_t::set_note)
diff --git a/src/py_value.cc b/src/py_value.cc
index 949f2a49..efeb4340 100644
--- a/src/py_value.cc
+++ b/src/py_value.cc
@@ -51,12 +51,12 @@ namespace {
return value.value(CURRENT_TIME());
}
boost::optional<value_t> py_value_1(const value_t& value,
- commodity_t& in_terms_of) {
+ const commodity_t * in_terms_of) {
return value.value(CURRENT_TIME(), in_terms_of);
}
boost::optional<value_t> py_value_2(const value_t& value,
- commodity_t& in_terms_of,
- datetime_t& moment) {
+ const commodity_t * in_terms_of,
+ const datetime_t& moment) {
return value.value(moment, in_terms_of);
}
@@ -266,8 +266,7 @@ void export_value()
.def("value", py_value_1, args("in_terms_of"))
.def("value", py_value_2, args("in_terms_of", "moment"))
- .def("value", &value_t::value, value_overloads())
- .def("price", &value_t::price)
+ //.def("value", &value_t::value, value_overloads())
.def("exchange_commodities", &value_t::exchange_commodities,
exchange_commodities_overloads())
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index d733c40d..8d9c8c84 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -535,6 +535,9 @@ namespace {
case value_t::ANY: // a pointer to an arbitrary object
return object(val);
}
+#if !defined(__clang__)
+ return object();
+#endif
}
}
diff --git a/src/pyinterp.h b/src/pyinterp.h
index 8699f69d..556b1563 100644
--- a/src/pyinterp.h
+++ b/src/pyinterp.h
@@ -136,8 +136,8 @@ public:
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
- OPTION_(python_interpreter_t, import_, DO_(args) {
- parent->import_option(args.get<string>(1));
+ OPTION_(python_interpreter_t, import_, DO_(str) {
+ parent->import_option(str);
});
};
diff --git a/src/query.h b/src/query.h
index f95988a7..c694d099 100644
--- a/src/query.h
+++ b/src/query.h
@@ -186,6 +186,9 @@ public:
assert(false);
return "<UNKNOWN>";
}
+#if !defined(__clang__)
+ return "<ERROR>";
+#endif
}
void unexpected();
diff --git a/src/quotes.cc b/src/quotes.cc
index b29eb8bd..c33e0826 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -40,7 +40,7 @@ namespace ledger {
optional<price_point_t>
commodity_quote_from_script(commodity_t& commodity,
- const optional<commodity_t&>& exchange_commodity)
+ const commodity_t * exchange_commodity)
{
DEBUG("commodity.download", "downloading quote for symbol " << commodity.symbol());
#if defined(DEBUG_ON)
diff --git a/src/quotes.h b/src/quotes.h
index 52092fbc..56740e47 100644
--- a/src/quotes.h
+++ b/src/quotes.h
@@ -46,7 +46,7 @@ namespace ledger {
optional<price_point_t>
commodity_quote_from_script(commodity_t& commodity,
- const optional<commodity_t&>& exchange_commodity);
+ const commodity_t * exchange_commodity);
} // namespace ledger
diff --git a/src/report.cc b/src/report.cc
index 7789ff80..1adbe9d0 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -59,7 +59,7 @@ void report_t::normalize_options(const string& verb)
#ifdef HAVE_ISATTY
if (! HANDLED(force_color)) {
if (! HANDLED(no_color) && isatty(STDOUT_FILENO))
- HANDLER(color).on_only(string("?normalize"));
+ HANDLER(color).on("?normalize");
if (HANDLED(color) && ! isatty(STDOUT_FILENO))
HANDLER(color).off();
}
@@ -83,7 +83,7 @@ void report_t::normalize_options(const string& verb)
if (session.HANDLED(price_exp_))
commodity_pool_t::current_pool->quote_leeway =
- session.HANDLER(price_exp_).value.as_long();
+ lexical_cast<long>(session.HANDLER(price_exp_).value) * 3600L;
if (session.HANDLED(price_db_))
commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str();
@@ -106,39 +106,35 @@ void report_t::normalize_options(const string& verb)
if (! HANDLED(meta_width_)) {
string::size_type i = HANDLER(meta_).str().find(':');
if (i != string::npos) {
- HANDLED(meta_width_).on_with
- (string("?normalize"),
- lexical_cast<long>(string(HANDLER(meta_).str(), i + 1)));
- HANDLED(meta_).on(string("?normalize"),
+ HANDLED(meta_width_).on("?normalize",
+ string(HANDLER(meta_).str(), i + 1));
+ HANDLED(meta_).on("?normalize",
string(HANDLER(meta_).str(), 0, i));
}
}
if (HANDLED(meta_width_)) {
- HANDLER(prepend_format_).on
- (string("?normalize"),
- string("%(justify(truncated(tag(\"") +
- HANDLER(meta_).str() + "\"), " +
- HANDLED(meta_width_).value.to_string() + " - 1), " +
- HANDLED(meta_width_).value.to_string() + "))");
- meta_width = HANDLED(meta_width_).value.to_long();
+ HANDLER(prepend_format_)
+ .on("?normalize", string("%(justify(truncated(tag(\"") +
+ HANDLER(meta_).str() + "\"), " +
+ HANDLED(meta_width_).value + " - 1), " +
+ HANDLED(meta_width_).value + "))");
+ meta_width = lexical_cast<long>(HANDLED(meta_width_).value);
} else {
- HANDLER(prepend_format_).on(string("?normalize"), string("%(tag(\"") +
- HANDLER(meta_).str() + "\"))");
+ HANDLER(prepend_format_)
+ .on("?normalize", string("%(tag(\"") + HANDLER(meta_).str() + "\"))");
}
}
- if (! HANDLED(prepend_width_))
- HANDLER(prepend_width_).on_with(string("?normalize"), static_cast<long>(0));
if (verb == "print" || verb == "xact" || verb == "dump") {
- HANDLER(related).on_only(string("?normalize"));
- HANDLER(related_all).on_only(string("?normalize"));
+ HANDLER(related_all).parent = this;
+ HANDLER(related_all).on("?normalize");
}
else if (verb == "equity") {
- HANDLER(equity).on_only(string("?normalize"));
+ HANDLER(equity).on("?normalize");
}
if (verb[0] != 'b' && verb[0] != 'r')
- HANDLER(base).on_only(string("?normalize"));
+ HANDLER(base).on("?normalize");
// If a time period was specified with -p, check whether it also gave a
// begin and/or end to the report period (though these can be overridden
@@ -152,12 +148,10 @@ void report_t::normalize_options(const string& verb)
// to avoid option ordering issues were we to have done it during the
// initial parsing of the options.
if (HANDLED(amount_data)) {
- HANDLER(format_)
- .on_with(string("?normalize"), HANDLER(plot_amount_format_).value);
+ HANDLER(format_).on("?normalize", HANDLER(plot_amount_format_).value);
}
else if (HANDLED(total_data)) {
- HANDLER(format_)
- .on_with(string("?normalize"), HANDLER(plot_total_format_).value);
+ HANDLER(format_).on("?normalize", HANDLER(plot_total_format_).value);
}
// If the --exchange (-X) option was used, parse out any final price
@@ -168,9 +162,26 @@ void report_t::normalize_options(const string& verb)
terminus);
}
+ if (HANDLED(percent)) {
+ commodity_t::decimal_comma_by_default = false;
+ if (HANDLED(market)) {
+ HANDLER(total_)
+ .on("?normalize",
+ "(__tmp = market(parent.total, value_date, exchange);"
+ " ((is_account & parent & __tmp) ?"
+ " percent(scrub(market(total, value_date, exchange)), "
+ " scrub(__tmp)) : 0))");
+ }
+ }
+
+ if (HANDLED(immediate) && HANDLED(market)) {
+ HANDLER(amount_)
+ .on("?normalize", "market(amount_expr, value_date, exchange)");
+ }
+
long cols = 0;
if (HANDLED(columns_))
- cols = HANDLER(columns_).value.to_long();
+ cols = lexical_cast<long>(HANDLER(columns_).value);
else if (const char * columns = std::getenv("COLUMNS"))
cols = lexical_cast<long>(columns);
else
@@ -182,23 +193,21 @@ void report_t::normalize_options(const string& verb)
if (cols > 0) {
DEBUG("auto.columns", "cols = " << cols);
- if (! HANDLER(date_width_).specified)
- HANDLER(date_width_)
- .on_with(none, static_cast<long>(format_date(CURRENT_DATE(),
- FMT_PRINTED).length()));
-
- long date_width = HANDLER(date_width_).value.to_long();
- long payee_width = (HANDLER(payee_width_).specified ?
- HANDLER(payee_width_).value.to_long() :
- int(double(cols) * 0.263157));
- long account_width = (HANDLER(account_width_).specified ?
- HANDLER(account_width_).value.to_long() :
- int(double(cols) * 0.302631));
- long amount_width = (HANDLER(amount_width_).specified ?
- HANDLER(amount_width_).value.to_long() :
- int(double(cols) * 0.157894));
- long total_width = (HANDLER(total_width_).specified ?
- HANDLER(total_width_).value.to_long() :
+ long date_width = (HANDLED(date_width_) ?
+ lexical_cast<long>(HANDLER(date_width_).str()) :
+ static_cast<long>
+ (format_date(CURRENT_DATE(),FMT_PRINTED).length()));
+ long payee_width = (HANDLED(payee_width_) ?
+ lexical_cast<long>(HANDLER(payee_width_).str()) :
+ long(double(cols) * 0.263157));
+ long account_width = (HANDLED(account_width_) ?
+ lexical_cast<long>(HANDLER(account_width_).str()) :
+ long(double(cols) * 0.302631));
+ long amount_width = (HANDLED(amount_width_) ?
+ lexical_cast<long>(HANDLER(amount_width_).str()) :
+ long(double(cols) * 0.157894));
+ long total_width = (HANDLED(total_width_) ?
+ lexical_cast<long>(HANDLER(total_width_).str()) :
amount_width);
DEBUG("auto.columns", "date_width = " << date_width);
@@ -207,32 +216,46 @@ void report_t::normalize_options(const string& verb)
DEBUG("auto.columns", "amount_width = " << amount_width);
DEBUG("auto.columns", "total_width = " << total_width);
- if (! HANDLER(date_width_).specified &&
- ! HANDLER(payee_width_).specified &&
- ! HANDLER(account_width_).specified &&
- ! HANDLER(amount_width_).specified &&
- ! HANDLER(total_width_).specified) {
+ if (! HANDLED(date_width_) &&
+ ! HANDLED(payee_width_) &&
+ ! HANDLED(account_width_) &&
+ ! HANDLED(amount_width_) &&
+ ! HANDLED(total_width_)) {
long total = (4 /* the spaces between */ + date_width + payee_width +
- account_width + amount_width + total_width);
- if (total > cols) {
+ account_width + amount_width + total_width +
+ (HANDLED(dc) ? 1 + amount_width : 0));
+ while (total > cols && account_width > 5 && payee_width > 5) {
DEBUG("auto.columns", "adjusting account down");
- account_width -= total - cols;
+ if (total > cols) {
+ --account_width;
+ --total;
+ if (total > cols) {
+ --account_width;
+ --total;
+ }
+ }
+ if (total > cols) {
+ --payee_width;
+ --total;
+ }
DEBUG("auto.columns", "account_width now = " << account_width);
}
}
if (! HANDLED(meta_width_))
- HANDLER(meta_width_).on_with(string("?normalize"), 0L);
- if (! HANDLER(date_width_).specified)
- HANDLER(date_width_).on_with(string("?normalize"), date_width);
- if (! HANDLER(payee_width_).specified)
- HANDLER(payee_width_).on_with(string("?normalize"), payee_width);
- if (! HANDLER(account_width_).specified)
- HANDLER(account_width_).on_with(string("?normalize"), account_width);
- if (! HANDLER(amount_width_).specified)
- HANDLER(amount_width_).on_with(string("?normalize"), amount_width);
- if (! HANDLER(total_width_).specified)
- HANDLER(total_width_).on_with(string("?normalize"), total_width);
+ HANDLER(meta_width_).value = "0";
+ if (! HANDLED(prepend_width_))
+ HANDLER(prepend_width_).value = "0";
+ if (! HANDLED(date_width_))
+ HANDLER(date_width_).value = to_string(date_width);
+ if (! HANDLED(payee_width_))
+ HANDLER(payee_width_).value = to_string(payee_width);
+ if (! HANDLED(account_width_))
+ HANDLER(account_width_).value = to_string(account_width);
+ if (! HANDLED(amount_width_))
+ HANDLER(amount_width_).value = to_string(amount_width);
+ if (! HANDLED(total_width_))
+ HANDLER(total_width_).value = to_string(total_width);
}
}
@@ -255,7 +278,7 @@ void report_t::normalize_period()
if (! interval.duration)
HANDLER(period_).off();
else if (! HANDLED(sort_all_))
- HANDLER(sort_xacts_).on_only(string("?normalize"));
+ HANDLER(sort_xacts_).on("?normalize");
}
void report_t::parse_query_args(const value_t& args, const string& whence)
@@ -278,7 +301,7 @@ void report_t::parse_query_args(const value_t& args, const string& whence)
}
if (query.has_query(query_t::QUERY_BOLD)) {
- HANDLER(bold_if_).set_expr(whence, query.get_query(query_t::QUERY_BOLD));
+ HANDLER(bold_if_).on(whence, query.get_query(query_t::QUERY_BOLD));
DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str());
}
@@ -309,7 +332,7 @@ void report_t::posts_report(post_handler_ptr handler)
{
handler = chain_post_handlers(handler, *this);
if (HANDLED(group_by_)) {
- std::auto_ptr<post_splitter>
+ unique_ptr<post_splitter>
splitter(new post_splitter(handler, *this, HANDLER(group_by_).expr));
splitter->set_postflush_func(posts_flusher(handler, *this));
handler = post_handler_ptr(splitter.release());
@@ -329,9 +352,9 @@ void report_t::generate_report(post_handler_ptr handler)
generate_posts_iterator walker
(session, HANDLED(seed_) ?
- static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0,
+ lexical_cast<unsigned int>(HANDLER(seed_).str()) : 0,
HANDLED(head_) ?
- static_cast<unsigned int>(HANDLER(head_).value.to_long()) : 50);
+ lexical_cast<unsigned int>(HANDLER(head_).str()) : 50);
pass_down_posts<generate_posts_iterator>(handler, walker);
}
@@ -513,20 +536,32 @@ value_t report_t::fn_should_bold(call_scope_t& scope)
value_t report_t::fn_market(call_scope_t& args)
{
- optional<datetime_t> moment = (args.has<datetime_t>(1) ?
- args.get<datetime_t>(1) :
- optional<datetime_t>());
value_t result;
+ value_t arg0 = args[0];
+
+ datetime_t moment;
+ if (args.has<datetime_t>(1))
+ moment = args.get<datetime_t>(1);
+
+ if (arg0.is_string()) {
+ amount_t tmp(1L);
+ commodity_t * commodity =
+ commodity_pool_t::current_pool->find_or_create(arg0.as_string());
+ tmp.set_commodity(*commodity);
+ arg0 = tmp;
+ }
+
+ string target_commodity;
if (args.has<string>(2))
- result = args[0].exchange_commodities(args.get<string>(2),
- /* add_prices= */ false, moment);
- else
- result = args[0].value(moment);
+ target_commodity = args.get<string>(2);
- if (! result.is_null())
- return result;
+ if (! target_commodity.empty())
+ result = arg0.exchange_commodities(target_commodity,
+ /* add_prices= */ false, moment);
+ else
+ result = arg0.value(moment);
- return args[0];
+ return ! result.is_null() ? result : arg0;
}
value_t report_t::fn_get_at(call_scope_t& args)
@@ -535,13 +570,20 @@ value_t report_t::fn_get_at(call_scope_t& args)
if (index == 0) {
if (! args[0].is_sequence())
return args[0];
- } else {
- if (! args[0].is_sequence())
- throw_(std::runtime_error,
- _("Attempting to get argument at index %1 from %2")
- << index << args[0].label());
}
- return args[0].as_sequence()[index];
+ else if (! args[0].is_sequence()) {
+ throw_(std::runtime_error,
+ _("Attempting to get argument at index %1 from %2")
+ << index << args[0].label());
+ }
+
+ value_t::sequence_t& seq(args[0].as_sequence_lval());
+ if (index >= seq.size())
+ throw_(std::runtime_error,
+ _("Attempting to get index %1 from %2 with %3 elements")
+ << index << args[0].label() << seq.size());
+
+ return seq[index];
}
value_t report_t::fn_is_seq(call_scope_t& scope)
@@ -731,14 +773,60 @@ value_t report_t::fn_percent(call_scope_t& args)
(args.get<amount_t>(0) / args.get<amount_t>(1)).number());
}
-value_t report_t::fn_price(call_scope_t& args)
+value_t report_t::fn_commodity(call_scope_t& args)
{
- return args[0].price();
+ return string_value(args.get<amount_t>(0).commodity().symbol());
}
-value_t report_t::fn_commodity(call_scope_t& args)
+value_t report_t::fn_nail_down(call_scope_t& args)
{
- return string_value(args.get<amount_t>(0).commodity().symbol());
+ value_t arg0(args[0]);
+ value_t arg1(args[1]);
+
+ switch (arg0.type()) {
+ case value_t::AMOUNT: {
+ amount_t tmp(arg0.as_amount());
+ if (tmp.has_commodity() && ! arg1.is_null()) {
+ expr_t value_expr(is_expr(arg1) ?
+ as_expr(arg1) :
+ expr_t::op_t::wrap_value(arg1.unrounded() / arg0));
+ std::ostringstream buf;
+ value_expr.print(buf);
+ value_expr.set_text(buf.str());
+
+ tmp.set_commodity(tmp.commodity().nail_down(value_expr));
+ }
+ return tmp;
+ }
+
+ case value_t::BALANCE: {
+ balance_t tmp;
+ foreach (const balance_t::amounts_map::value_type& pair,
+ arg0.as_balance_lval().amounts) {
+ call_scope_t inner_args(*args.parent);
+ inner_args.push_back(pair.second);
+ inner_args.push_back(arg1);
+ tmp += fn_nail_down(inner_args).as_amount();
+ }
+ return tmp;
+ }
+
+ case value_t::SEQUENCE: {
+ value_t tmp;
+ foreach (value_t& value, arg0.as_sequence_lval()) {
+ call_scope_t inner_args(*args.parent);
+ inner_args.push_back(value);
+ inner_args.push_back(arg1);
+ tmp.push_back(fn_nail_down(inner_args));
+ }
+ return tmp;
+ }
+
+ default:
+ throw_(std::runtime_error, _("Attempting to nail down %1")
+ << args[0].label());
+ }
+ return arg0;
}
value_t report_t::fn_lot_date(call_scope_t& args)
@@ -880,7 +968,7 @@ value_t report_t::pricemap_command(call_scope_t& args)
std::ostream& out(output_stream);
commodity_pool_t::current_pool->commodity_price_history.print_map
(out, args.has<string>(0) ?
- optional<datetime_t>(datetime_t(parse_date(args.get<string>(0)))) : none);
+ datetime_t(parse_date(args.get<string>(0))) : datetime_t());
return true;
}
@@ -911,11 +999,9 @@ option_t<report_t> * report_t::lookup_option(const char * p)
case 'G':
OPT_CH(gain);
break;
-#if 0
case 'H':
OPT_CH(historical);
break;
-#endif
case 'I':
OPT_CH(price);
break;
@@ -980,6 +1066,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT_(begin_);
else OPT(bold_if_);
else OPT(budget);
+ else OPT(budget_format_);
else OPT(by_payee);
break;
case 'c':
@@ -998,6 +1085,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT(date_);
else OPT(date_format_);
else OPT(datetime_format_);
+ else OPT(dc);
else OPT(depth_);
else OPT(deviation);
else OPT_(display_);
@@ -1031,10 +1119,12 @@ option_t<report_t> * report_t::lookup_option(const char * p)
break;
case 'h':
OPT(head_);
+ else OPT(historical);
break;
case 'i':
OPT(invert);
else OPT(inject_);
+ else OPT(immediate);
break;
case 'j':
OPT_CH(amount_data);
@@ -1043,7 +1133,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
OPT_(limit_);
else OPT(lot_dates);
else OPT(lot_prices);
- else OPT(lot_tags);
+ else OPT_ALT(lot_notes, lot_tags);
else OPT(lots);
else OPT(lots_actual);
else OPT_ALT(tail_, last_);
@@ -1223,7 +1313,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
else if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_total);
else if (is_eq(p, "date"))
- return MAKE_FUNCTOR(report_t::fn_now);
+ return MAKE_FUNCTOR(report_t::fn_today);
break;
case 'f':
@@ -1266,6 +1356,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return WRAP_FUNCTOR(fn_null);
else if (is_eq(p, "now"))
return MAKE_FUNCTOR(report_t::fn_now);
+ else if (is_eq(p, "nail_down"))
+ return MAKE_FUNCTOR(report_t::fn_nail_down);
break;
case 'o':
@@ -1278,8 +1370,6 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return WRAP_FUNCTOR(fn_false);
else if (is_eq(p, "percent"))
return MAKE_FUNCTOR(report_t::fn_percent);
- else if (is_eq(p, "price"))
- return MAKE_FUNCTOR(report_t::fn_price);
else if (is_eq(p, "print"))
return MAKE_FUNCTOR(report_t::fn_print);
break;
@@ -1382,98 +1472,98 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return MAKE_OPT_HANDLER(report_t, handler);
break;
+#define POSTS_REPORTER(formatter) \
+ WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, \
+ string("#") + p))
+
+ // Can't use WRAP_FUNCTOR here because the template arguments
+ // confuse the parser
+#define POSTS_REPORTER_(method, formatter) \
+ expr_t::op_t::wrap_functor \
+ (reporter<post_t, post_handler_ptr, method> \
+ (post_handler_ptr(formatter), *this, string("#") + p))
+
+#define FORMATTED_POSTS_REPORTER(format) \
+ POSTS_REPORTER \
+ (new format_posts \
+ (*this, report_format(HANDLER(format)), \
+ maybe_format(HANDLER(prepend_format_)), \
+ HANDLED(prepend_width_) ? \
+ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0))
+
+#define FORMATTED_COMMODITIES_REPORTER(format) \
+ POSTS_REPORTER_ \
+ (&report_t::commodities_report, \
+ new format_posts \
+ (*this, report_format(HANDLER(format)), \
+ maybe_format(HANDLER(prepend_format_)), \
+ HANDLED(prepend_width_) ? \
+ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0))
+
+#define ACCOUNTS_REPORTER(formatter) \
+ expr_t::op_t::wrap_functor(reporter<account_t, acct_handler_ptr, \
+ &report_t::accounts_report> \
+ (acct_handler_ptr(formatter), *this, \
+ string("#") + p))
+
+#define FORMATTED_ACCOUNTS_REPORTER(format) \
+ ACCOUNTS_REPORTER \
+ (new format_accounts \
+ (*this, report_format(HANDLER(format)), \
+ maybe_format(HANDLER(prepend_format_)), \
+ HANDLED(prepend_width_) ? \
+ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0))
+
case symbol_t::COMMAND:
switch (*p) {
case 'a':
if (is_eq(p, "accounts")) {
- return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_accounts(*this)),
- *this, "#accounts"));
+ return POSTS_REPORTER(new report_accounts(*this));
}
break;
case 'b':
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) {
- return expr_t::op_t::wrap_functor
- (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
- (acct_handler_ptr(new format_accounts
- (*this, report_format(HANDLER(balance_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#balance"));
+ return FORMATTED_ACCOUNTS_REPORTER(balance_format_);
}
else if (is_eq(p, "budget")) {
- HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)");
+ HANDLER(amount_).on(string("#budget"), "(amount, 0)");
budget_flags |= BUDGET_WRAP_VALUES;
if (! (budget_flags & ~BUDGET_WRAP_VALUES))
budget_flags |= BUDGET_BUDGETED;
-#if 0
-#define POSTS_REPORT(formatter)
- return WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this,
- string("#") + p));
-
-#define ACCOUNTS_REPORT(formatter)
- return WRAP_FUNCTOR(reporter<account_t, acct_handler_ptr,
- &report_t::accounts_report>
- (acct_handler_ptr(formatter), *this,
- string("#") + p));
-#endif
-
- return expr_t::op_t::wrap_functor
- (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
- (acct_handler_ptr(new format_accounts
- (*this, report_format(HANDLER(budget_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#budget"));
+ return FORMATTED_ACCOUNTS_REPORTER(budget_format_);
}
break;
case 'c':
if (is_eq(p, "csv")) {
- return WRAP_FUNCTOR
- (reporter<>
- (post_handler_ptr(new format_posts
- (*this, report_format(HANDLER(csv_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#csv"));
+ return FORMATTED_POSTS_REPORTER(csv_format_);
}
else if (is_eq(p, "cleared")) {
- HANDLER(amount_).set_expr(string("#cleared"),
- "(amount, cleared ? amount : 0)");
- return expr_t::op_t::wrap_functor
- (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
- (acct_handler_ptr(new format_accounts
- (*this, report_format(HANDLER(cleared_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#cleared"));
+ HANDLER(amount_).on(string("#cleared"),
+ "(amount, cleared ? amount : 0)");
+ return FORMATTED_ACCOUNTS_REPORTER(cleared_format_);
}
else if (is_eq(p, "convert")) {
return WRAP_FUNCTOR(convert_command);
}
else if (is_eq(p, "commodities")) {
- return WRAP_FUNCTOR(reporter<>
- (post_handler_ptr(new report_commodities(*this)),
- *this, "#commodities"));
+ return POSTS_REPORTER(new report_commodities(*this));
}
break;
case 'e':
if (is_eq(p, "equity")) {
- HANDLER(generated).on_only(string("#equity"));
- return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new print_xacts(*this)),
- *this, "#equity"));
+ HANDLER(generated).on("#equity");
+ return POSTS_REPORTER(new print_xacts(*this));
}
else if (is_eq(p, "entry")) {
return WRAP_FUNCTOR(xact_command);
}
else if (is_eq(p, "emacs")) {
- return WRAP_FUNCTOR
- (reporter<>(post_handler_ptr(new format_emacs_posts(output_stream)),
- *this, "#emacs"));
+ return POSTS_REPORTER(new format_emacs_posts(output_stream));
}
else if (is_eq(p, "echo")) {
return MAKE_FUNCTOR(report_t::echo_command);
@@ -1482,56 +1572,32 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
case 'o':
if (is_eq(p, "org")) {
- return WRAP_FUNCTOR
- (reporter<>
- (post_handler_ptr(new posts_to_org_table
- (*this, maybe_format(HANDLER(prepend_format_)))),
- *this, "#org"));
+ return POSTS_REPORTER(new posts_to_org_table
+ (*this, maybe_format(HANDLER(prepend_format_))));
}
break;
case 'p':
if (*(p + 1) == '\0' || is_eq(p, "print")) {
- return WRAP_FUNCTOR
- (reporter<>(post_handler_ptr(new print_xacts(*this, HANDLED(raw))),
- *this, "#print"));
+ return POSTS_REPORTER(new print_xacts(*this, HANDLED(raw)));
}
else if (is_eq(p, "prices")) {
- return expr_t::op_t::wrap_functor
- (reporter<post_t, post_handler_ptr, &report_t::commodities_report>
- (post_handler_ptr(new format_posts
- (*this, report_format(HANDLER(prices_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#prices"));
+ return FORMATTED_COMMODITIES_REPORTER(prices_format_);
}
else if (is_eq(p, "pricedb")) {
- return expr_t::op_t::wrap_functor
- (reporter<post_t, post_handler_ptr, &report_t::commodities_report>
- (post_handler_ptr(new format_posts
- (*this, report_format(HANDLER(pricedb_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#pricedb"));
+ return FORMATTED_COMMODITIES_REPORTER(pricedb_format_);
}
else if (is_eq(p, "pricemap")) {
return MAKE_FUNCTOR(report_t::pricemap_command);
}
else if (is_eq(p, "payees")) {
- return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_payees(*this)),
- *this, "#payees"));
+ return POSTS_REPORTER(new report_payees(*this));
}
break;
case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) {
- return WRAP_FUNCTOR
- (reporter<>
- (post_handler_ptr(new format_posts
- (*this, report_format(HANDLER(register_format_)),
- maybe_format(HANDLER(prepend_format_)),
- HANDLER(prepend_width_).value.to_size_t())),
- *this, "#register"));
+ return FORMATTED_POSTS_REPORTER(register_format_);
}
else if (is_eq(p, "reload")) {
return MAKE_FUNCTOR(report_t::reload_command);
@@ -1549,8 +1615,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
if (is_eq(p, "xact"))
return WRAP_FUNCTOR(xact_command);
else if (is_eq(p, "xml"))
- return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new format_xml(*this)),
- *this, "#xml"));
+ return POSTS_REPORTER(new format_xml(*this));
break;
}
break;
@@ -1572,11 +1637,9 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return WRAP_FUNCTOR(format_command);
break;
case 'g':
- if (is_eq(p, "generate")) {
- return expr_t::op_t::wrap_functor
- (reporter<post_t, post_handler_ptr, &report_t::generate_report>
- (post_handler_ptr(new print_xacts(*this)), *this, "#generate"));
- }
+ if (is_eq(p, "generate"))
+ return POSTS_REPORTER_(&report_t::generate_report,
+ new print_xacts(*this));
break;
case 'p':
if (is_eq(p, "parse"))
diff --git a/src/report.h b/src/report.h
index 03eee78b..a3825335 100644
--- a/src/report.h
+++ b/src/report.h
@@ -169,8 +169,8 @@ public:
value_t fn_format_date(call_scope_t& scope);
value_t fn_ansify_if(call_scope_t& scope);
value_t fn_percent(call_scope_t& scope);
- value_t fn_price(call_scope_t& scope);
value_t fn_commodity(call_scope_t& scope);
+ value_t fn_nail_down(call_scope_t& scope);
value_t fn_lot_date(call_scope_t& scope);
value_t fn_lot_price(call_scope_t& scope);
value_t fn_lot_tag(call_scope_t& scope);
@@ -215,7 +215,7 @@ public:
bool lots = HANDLED(lots) || HANDLED(lots_actual);
return keep_details_t(lots || HANDLED(lot_prices),
lots || HANDLED(lot_dates),
- lots || HANDLED(lot_tags),
+ lots || HANDLED(lot_notes),
HANDLED(lots_actual));
}
@@ -250,6 +250,7 @@ public:
HANDLER(date_).report(out);
HANDLER(date_format_).report(out);
HANDLER(datetime_format_).report(out);
+ HANDLER(dc).report(out);
HANDLER(depth_).report(out);
HANDLER(deviation).report(out);
HANDLER(display_).report(out);
@@ -272,12 +273,13 @@ public:
HANDLER(group_by_).report(out);
HANDLER(group_title_format_).report(out);
HANDLER(head_).report(out);
+ HANDLER(immediate).report(out);
HANDLER(inject_).report(out);
HANDLER(invert).report(out);
HANDLER(limit_).report(out);
HANDLER(lot_dates).report(out);
HANDLER(lot_prices).report(out);
- HANDLER(lot_tags).report(out);
+ HANDLER(lot_notes).report(out);
HANDLER(lots).report(out);
HANDLER(lots_actual).report(out);
HANDLER(market).report(out);
@@ -353,12 +355,16 @@ public:
* Option handlers
*/
- OPTION__(report_t, abbrev_len_,
- CTOR(report_t, abbrev_len_) { on_with(none, 2L); });
+ OPTION__
+ (report_t, abbrev_len_,
+ CTOR(report_t, abbrev_len_) {
+ on(none, "2");
+ });
+
OPTION(report_t, account_);
OPTION_(report_t, actual, DO() { // -L
- parent->HANDLER(limit_).on(string("--actual"), "actual");
+ OTHER(limit_).on(whence, "actual");
});
OPTION_(report_t, add_budget, DO() {
@@ -367,16 +373,9 @@ public:
OPTION__
(report_t, amount_, // -t
- expr_t expr;
- CTOR(report_t, amount_) {
- set_expr(none, "amount");
- }
- void set_expr(const optional<string>& whence, const string& str) {
- expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
+ DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {}
+ DO_(str) {
+ expr.append(str);
});
OPTION(report_t, amount_data); // -j
@@ -384,220 +383,271 @@ public:
OPTION(report_t, auto_match);
OPTION_(report_t, average, DO() { // -A
- parent->HANDLER(display_total_)
- .set_expr(string("--average"), "count>0?(total_expr/count):0");
+ OTHER(display_total_)
+ .on(whence, "count>0?(display_total/count):0");
});
- OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) {
- on(none,
- "%(ansify_if("
- " justify(scrub(display_total), 20, 20 + prepend_width, true, color),"
- " bold if should_bold))"
- " %(!options.flat ? depth_spacer : \"\")"
- "%-(ansify_if("
- " ansify_if(partial_account(options.flat), blue if color),"
- " bold if should_bold))\n%/"
- "%$1\n%/"
- "%(prepend_width ? \" \" * prepend_width : \"\")"
- "--------------------\n");
- });
+ OPTION__
+ (report_t, balance_format_,
+ CTOR(report_t, balance_format_) {
+ on(none,
+ "%(ansify_if("
+ " justify(scrub(display_total), 20,"
+ " 20 + int(prepend_width), true, color),"
+ " bold if should_bold))"
+ " %(!options.flat ? depth_spacer : \"\")"
+ "%-(ansify_if("
+ " ansify_if(partial_account(options.flat), blue if color),"
+ " bold if should_bold))\n%/"
+ "%$1\n%/"
+ "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
+ "--------------------\n");
+ });
OPTION(report_t, base);
OPTION_(report_t, basis, DO() { // -B
- parent->HANDLER(revalued).on_only(string("--basis"));
- parent->HANDLER(amount_).set_expr(string("--basis"), "rounded(cost)");
+ OTHER(revalued).on(whence);
+ OTHER(amount_).expr.set_base_expr("rounded(cost)");
});
- OPTION_(report_t, begin_, DO_(args) { // -b
- date_interval_t interval(args.get<string>(1));
- optional<date_t> begin = interval.begin();
- if (! begin)
+ OPTION_(report_t, begin_, DO_(str) { // -b
+ date_interval_t interval(str);
+ if (optional<date_t> begin = interval.begin()) {
+ string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
+ OTHER(limit_).on(whence, predicate);
+ } else {
throw_(std::invalid_argument,
- _("Could not determine beginning of period '%1'")
- << args.get<string>(1));
-
- string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
- parent->HANDLER(limit_).on(string("--begin"), predicate);
+ _("Could not determine beginning of period '%1'") << str);
+ }
});
- OPTION__
+ OPTION_
(report_t, bold_if_,
expr_t expr;
- CTOR(report_t, bold_if_) {}
- void set_expr(const optional<string>& whence, const string& str) {
+ DO_(str) {
expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
});
OPTION_(report_t, budget, DO() {
parent->budget_flags |= BUDGET_BUDGETED;
});
- OPTION__(report_t, budget_format_, CTOR(report_t, budget_format_) {
- on(none,
- "%(justify(scrub(get_at(total_expr, 0)), 12, -1, true, color))"
- " %(justify(-scrub(get_at(total_expr, 1)), 12, "
- " 12 + 1 + 12, true, color))"
- " %(justify(scrub(get_at(total_expr, 1) + "
- " get_at(total_expr, 0)), 12, "
- " 12 + 1 + 12 + 1 + 12, true, color))"
- " %(ansify_if("
- " justify((get_at(total_expr, 1) ? "
- " (100% * scrub(get_at(total_expr, 0))) / "
- " -scrub(get_at(total_expr, 1)) : 0), "
- " 5, -1, true, false),"
- " magenta if (color and get_at(total_expr, 1) and "
- " (abs(quantity(scrub(get_at(total_expr, 0))) / "
- " quantity(scrub(get_at(total_expr, 1)))) >= 1))))"
- " %(!options.flat ? depth_spacer : \"\")"
- "%-(ansify_if(partial_account(options.flat), blue if color))\n"
- "%/%$1 %$2 %$3 %$4\n%/"
- "%(prepend_width ? \" \" * prepend_width : \"\")"
- "------------ ------------ ------------ -----\n");
- });
+ OPTION__
+ (report_t, budget_format_,
+ CTOR(report_t, budget_format_) {
+ on(none,
+ "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))"
+ " %(justify(-scrub(get_at(display_total, 1)), 12, "
+ " 12 + 1 + 12, true, color))"
+ " %(justify(scrub(get_at(display_total, 1) + "
+ " get_at(display_total, 0)), 12, "
+ " 12 + 1 + 12 + 1 + 12, true, color))"
+ " %(ansify_if("
+ " justify((get_at(display_total, 1) ? "
+ " (100% * scrub(get_at(display_total, 0))) / "
+ " -scrub(get_at(display_total, 1)) : 0), "
+ " 5, -1, true, false),"
+ " magenta if (color and get_at(display_total, 1) and "
+ " (abs(quantity(scrub(get_at(display_total, 0))) / "
+ " quantity(scrub(get_at(display_total, 1)))) >= 1))))"
+ " %(!options.flat ? depth_spacer : \"\")"
+ "%-(ansify_if(partial_account(options.flat), blue if color))\n"
+ "%/%$1 %$2 %$3 %$4\n%/"
+ "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
+ "------------ ------------ ------------ -----\n");
+ });
OPTION(report_t, by_payee); // -P
OPTION_(report_t, cleared, DO() { // -C
- parent->HANDLER(limit_).on(string("--cleared"), "cleared");
+ OTHER(limit_).on(whence, "cleared");
});
- OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) {
- on(none,
- "%(justify(scrub(get_at(total_expr, 0)), 16, 16 + prepend_width, "
- " true, color)) %(justify(scrub(get_at(total_expr, 1)), 18, "
- " 36 + prepend_width, true, color))"
- " %(latest_cleared ? format_date(latest_cleared) : \" \")"
- " %(!options.flat ? depth_spacer : \"\")"
- "%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
- "%$1 %$2 %$3\n%/"
- "%(prepend_width ? \" \" * prepend_width : \"\")"
- "---------------- ---------------- ---------\n");
- });
+ OPTION__
+ (report_t, cleared_format_,
+ CTOR(report_t, cleared_format_) {
+ on(none,
+ "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), "
+ " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, "
+ " 36 + int(prepend_width), true, color))"
+ " %(latest_cleared ? format_date(latest_cleared) : \" \")"
+ " %(!options.flat ? depth_spacer : \"\")"
+ "%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
+ "%$1 %$2 %$3\n%/"
+ "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
+ "---------------- ---------------- ---------\n");
+ });
OPTION(report_t, color);
OPTION_(report_t, collapse, DO() { // -n
// Make sure that balance reports are collapsed too, but only apply it
// to account xacts
- parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1");
+ OTHER(display_).on(whence, "post|depth<=1");
});
OPTION_(report_t, collapse_if_zero, DO() {
- parent->HANDLER(collapse).on_only(string("--collapse-if-zero"));
+ OTHER(collapse).on(whence);
});
OPTION(report_t, columns_);
OPTION(report_t, count);
- OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) {
- on(none,
- "%(quoted(date)),"
- "%(quoted(code)),"
- "%(quoted(payee)),"
- "%(quoted(display_account)),"
- "%(quoted(commodity)),"
- "%(quoted(quantity(scrub(display_amount)))),"
- "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))),"
- "%(quoted(join(note | xact.note)))\n");
- });
+ OPTION__
+ (report_t, csv_format_,
+ CTOR(report_t, csv_format_) {
+ on(none,
+ "%(quoted(date)),"
+ "%(quoted(code)),"
+ "%(quoted(payee)),"
+ "%(quoted(display_account)),"
+ "%(quoted(commodity)),"
+ "%(quoted(quantity(scrub(display_amount)))),"
+ "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))),"
+ "%(quoted(join(note | xact.note)))\n");
+ });
OPTION_(report_t, current, DO() { // -c
- parent->HANDLER(limit_).on(string("--current"), "date<=today");
+ OTHER(limit_).on(whence, "date<=today");
});
OPTION_(report_t, daily, DO() { // -D
- parent->HANDLER(period_).on(string("--daily"), "daily");
+ OTHER(period_).on(whence, "daily");
});
OPTION(report_t, date_);
OPTION(report_t, date_format_);
OPTION(report_t, datetime_format_);
- OPTION_(report_t, depth_, DO_(args) {
- parent->HANDLER(display_)
- .on(string("--depth"), string("depth<=") + args.get<string>(1));
+ OPTION_(report_t, dc, DO() {
+ OTHER(amount_).expr.set_base_expr
+ ("(amount > 0 ? amount : 0, amount < 0 ? amount : 0)");
+
+ OTHER(register_format_)
+ .on(none,
+ "%(ansify_if("
+ " ansify_if(justify(format_date(date), int(date_width)),"
+ " green if color and date > today),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), "
+ " bold if color and !cleared and actual),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " ansify_if(justify(truncated(display_account, int(account_width), "
+ " abbrev_len), int(account_width)),"
+ " blue if color),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " justify(scrub(abs(get_at(display_amount, 0))), int(amount_width), "
+ " 3 + int(meta_width) + int(date_width) + int(payee_width)"
+ " + int(account_width) + int(amount_width) + int(prepend_width),"
+ " true, color),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " justify(scrub(abs(get_at(display_amount, 1))), int(amount_width), "
+ " 4 + int(meta_width) + int(date_width) + int(payee_width)"
+ " + int(account_width) + int(amount_width) + int(amount_width) + int(prepend_width),"
+ " true, color),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), int(total_width), "
+ " 5 + int(meta_width) + int(date_width) + int(payee_width)"
+ " + int(account_width) + int(amount_width) + int(amount_width) + int(total_width)"
+ " + int(prepend_width), true, color),"
+ " bold if should_bold))\n%/"
+ "%(justify(\" \", int(date_width)))"
+ " %(ansify_if("
+ " justify(truncated(has_tag(\"Payee\") ? payee : \" \", "
+ " int(payee_width)), int(payee_width)),"
+ " bold if should_bold))"
+ " %$3 %$4 %$5 %$6\n");
+
+ OTHER(balance_format_)
+ .on(none,
+ "%(ansify_if("
+ " justify(scrub(abs(get_at(display_total, 0))), 14,"
+ " 14 + int(prepend_width), true, color),"
+ " bold if should_bold)) "
+ "%(ansify_if("
+ " justify(scrub(abs(get_at(display_total, 1))), 14,"
+ " 14 + 1 + int(prepend_width) + int(total_width), true, color),"
+ " bold if should_bold)) "
+ "%(ansify_if("
+ " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), 14,"
+ " 14 + 2 + int(prepend_width) + int(total_width) + int(total_width), true, color),"
+ " bold if should_bold))"
+ " %(!options.flat ? depth_spacer : \"\")"
+ "%-(ansify_if("
+ " ansify_if(partial_account(options.flat), blue if color),"
+ " bold if should_bold))\n%/"
+ "%$1 %$2 %$3\n%/"
+ "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
+ "--------------------------------------------\n");
+ });
+
+ OPTION_(report_t, depth_, DO_(str) {
+ OTHER(display_).on(whence, string("depth<=") + str);
});
OPTION_(report_t, deviation, DO() {
- parent->HANDLER(display_total_)
- .set_expr(string("--deviation"), "amount_expr-total_expr/count");
+ OTHER(display_total_)
+ .on(whence, "display_amount-display_total");
});
- OPTION__
- (report_t, display_, // -d
- CTOR(report_t, display_) {}
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- if (! handled)
- option_t<report_t>::on_with(whence, text);
- else
- option_t<report_t>::on_with(whence,
- string_value(string("(") + str() + ")&(" +
- text.as_string() + ")"));
+ OPTION_
+ (report_t, display_,
+ DO_(str) { // -d
+ if (handled)
+ value = string("(") + value + ")&(" + str + ")";
});
OPTION__
(report_t, display_amount_,
- expr_t expr;
- CTOR(report_t, display_amount_) {
- set_expr(none, "amount_expr");
- }
- void set_expr(const optional<string>& whence, const string& str) {
- expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
+ DECL1(report_t, display_amount_, merged_expr_t, expr,
+ ("display_amount", "amount_expr")) {}
+ DO_(str) {
+ expr.append(str);
});
OPTION__
(report_t, display_total_,
- expr_t expr;
- CTOR(report_t, display_total_) {
- set_expr(none, "total_expr");
- }
- void set_expr(const optional<string>& whence, const string& str) {
- expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
+ DECL1(report_t, display_total_, merged_expr_t, expr,
+ ("display_total", "total_expr")) {}
+ DO_(str) {
+ expr.append(str);
});
OPTION(report_t, dow);
OPTION(report_t, aux_date);
OPTION(report_t, empty); // -E
- OPTION_(report_t, end_, DO_(args) { // -e
- date_interval_t interval(args.get<string>(1));
+ OPTION_(report_t, end_, DO_(str) { // -e
// Use begin() here so that if the user says --end=2008, we end on
// 2008/01/01 instead of 2009/01/01 (which is what end() would
// return).
- optional<date_t> end = interval.begin();
- if (! end)
+ date_interval_t interval(str);
+ if (optional<date_t> end = interval.begin()) {
+ string predicate = "date<[" + to_iso_extended_string(*end) + "]";
+ OTHER(limit_).on(whence, predicate);
+
+ parent->terminus = datetime_t(*end);
+ } else {
throw_(std::invalid_argument,
_("Could not determine end of period '%1'")
- << args.get<string>(1));
-
- string predicate = "date<[" + to_iso_extended_string(*end) + "]";
- parent->HANDLER(limit_).on(string("--end"), predicate);
-
- parent->terminus = datetime_t(*end);
+ << str);
+ }
});
OPTION(report_t, equity);
OPTION(report_t, exact);
- OPTION_(report_t, exchange_, DO_(args) { // -X
- on_with(args.get<string>(0), args[1]);
- call_scope_t no_args(*parent);
- no_args.push_back(args[0]);
- parent->HANDLER(market).parent = parent;
- parent->HANDLER(market).handler(no_args);
+ OPTION_(report_t, exchange_, DO_() { // -X
+ // Using -X implies -V. The main difference is that now
+ // HANDLER(exchange_) contains the name of a commodity, which
+ // is accessed via the "exchange" value expression function.
+ OTHER(market).on(whence);
});
OPTION(report_t, flat);
@@ -608,115 +658,112 @@ public:
OPTION(report_t, format_); // -F
OPTION_(report_t, gain, DO() { // -G
- parent->HANDLER(revalued).on_only(string("--gain"));
- parent->HANDLER(amount_).set_expr(string("--gain"), "(amount, cost)");
+ OTHER(revalued).on(whence);
+ OTHER(amount_).expr.set_base_expr("(amount, cost)");
+
// Since we are displaying the amounts of revalued postings, they
// will end up being composite totals, and hence a pair of pairs.
- parent->HANDLER(display_amount_)
- .set_expr(string("--gain"),
- "use_direct_amount ? amount :"
- " (is_seq(get_at(amount_expr, 0)) ?"
- " get_at(get_at(amount_expr, 0), 0) :"
- " market(get_at(amount_expr, 0), value_date, exchange)"
- " - get_at(amount_expr, 1))");
- parent->HANDLER(revalued_total_)
- .set_expr(string("--gain"),
- "(market(get_at(total_expr, 0), value_date, exchange), "
- "get_at(total_expr, 1))");
- parent->HANDLER(display_total_)
- .set_expr(string("--gain"),
- "use_direct_amount ? total_expr :"
- " market(get_at(total_expr, 0), value_date, exchange)"
- " - get_at(total_expr, 1)");
+ OTHER(display_amount_)
+ .on(whence,
+ "use_direct_amount ? amount :"
+ " (is_seq(get_at(amount_expr, 0)) ?"
+ " get_at(get_at(amount_expr, 0), 0) :"
+ " market(get_at(amount_expr, 0), value_date, exchange)"
+ " - get_at(amount_expr, 1))");
+ OTHER(revalued_total_)
+ .on(whence,
+ "(market(get_at(total_expr, 0), value_date, exchange), "
+ "get_at(total_expr, 1))");
+ OTHER(display_total_)
+ .on(whence,
+ "use_direct_amount ? total_expr :"
+ " market(get_at(total_expr, 0), value_date, exchange)"
+ " - get_at(total_expr, 1)");
});
OPTION(report_t, generated);
- OPTION__
+ OPTION_
(report_t, group_by_,
expr_t expr;
- CTOR(report_t, group_by_) {}
- void set_expr(const optional<string>& whence, const string& str) {
+ DO_(str) {
expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
});
- OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) {
- on(none, "%(value)\n");
- });
+ OPTION__
+ (report_t, group_title_format_,
+ CTOR(report_t, group_title_format_) {
+ on(none, "%(value)\n");
+ });
OPTION(report_t, head_);
+
+ OPTION_(report_t, historical, DO() { // -H
+ OTHER(market).on(whence);
+ OTHER(amount_)
+ .on(whence, "nail_down(amount_expr, "
+ "market(amount_expr, value_date, exchange))");
+ });
+
+ OPTION(report_t, immediate);
OPTION(report_t, inject_);
OPTION_(report_t, invert, DO() {
- parent->HANDLER(amount_).set_expr(string("--invert"), "-amount");
+ OTHER(amount_).on(whence, "-amount");
});
- OPTION__
- (report_t, limit_, // -l
- CTOR(report_t, limit_) {}
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- if (! handled)
- option_t<report_t>::on_with(whence, text);
- else
- option_t<report_t>::on_with(whence,
- string_value(string("(") + str() + ")&(" +
- text.as_string() + ")"));
+ OPTION_
+ (report_t, limit_,
+ DO_(str) { // -l
+ if (handled)
+ value = string("(") + value + ")&(" + str + ")";
});
OPTION(report_t, lot_dates);
OPTION(report_t, lot_prices);
- OPTION(report_t, lot_tags);
+ OPTION(report_t, lot_notes);
OPTION(report_t, lots);
OPTION(report_t, lots_actual);
OPTION_(report_t, market, DO() { // -V
- parent->HANDLER(revalued).on_only(string("--market"));
- parent->HANDLER(display_amount_)
- .set_expr(string("--market"),
- "market(amount_expr, value_date, exchange)");
- parent->HANDLER(display_total_)
- .set_expr(string("--market"),
- "market(total_expr, value_date, exchange)");
+ OTHER(revalued).on(whence);
+
+ OTHER(display_amount_)
+ .on(whence, "market(display_amount, value_date, exchange)");
+ OTHER(display_total_)
+ .on(whence, "market(display_total, value_date, exchange)");
});
OPTION(report_t, meta_);
OPTION_(report_t, monthly, DO() { // -M
- parent->HANDLER(period_).on(string("--monthly"), "monthly");
+ OTHER(period_).on(whence, "monthly");
});
OPTION_(report_t, no_color, DO() {
- parent->HANDLER(color).off();
+ OTHER(color).off();
});
OPTION(report_t, no_rounding);
OPTION(report_t, no_titles);
OPTION(report_t, no_total);
- OPTION_(report_t, now_, DO_(args) {
- date_interval_t interval(args.get<string>(1));
- optional<date_t> begin = interval.begin();
- if (! begin)
+ OPTION_(report_t, now_, DO_(str) {
+ date_interval_t interval(str);
+ if (optional<date_t> begin = interval.begin()) {
+ ledger::epoch = parent->terminus = datetime_t(*begin);
+ } else {
throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'")
- << args.get<string>(1));
- ledger::epoch = parent->terminus = datetime_t(*begin);
+ << str);
+ }
});
- OPTION__
+ OPTION_
(report_t, only_,
- CTOR(report_t, only_) {}
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- if (! handled)
- option_t<report_t>::on_with(whence, text);
- else
- option_t<report_t>::on_with(whence,
- string_value(string("(") + str() + ")&(" +
- text.as_string() + ")"));
+ DO_(str) {
+ if (handled)
+ value = string("(") + value + ")&(" + str + ")";
});
OPTION(report_t, output_); // -o
@@ -737,181 +784,162 @@ public:
setenv("LESS", "-FRSX", 0); // don't overwrite
}
}
- }
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- string cmd(text.to_string());
- if (cmd == "" || cmd == "false" || cmd == "off" ||
- cmd == "none" || cmd == "no" || cmd == "disable")
- option_t<report_t>::off();
- else
- option_t<report_t>::on_with(whence, text);
});
#else // HAVE_ISATTY
- OPTION__
- (report_t, pager_,
- CTOR(report_t, pager_) {
- }
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- string cmd(text.to_string());
- if (cmd == "" || cmd == "false" || cmd == "off" ||
- cmd == "none" || cmd == "no" || cmd == "disable")
- option_t<report_t>::off();
- else
- option_t<report_t>::on_with(whence, text);
- });
+ OPTION(report_t, pager_);
#endif // HAVE_ISATTY
+ OPTION_(report_t, no_pager, DO() {
+ OTHER(pager_).off();
+ });
+
OPTION(report_t, payee_);
OPTION_(report_t, pending, DO() { // -C
- parent->HANDLER(limit_).on(string("--pending"), "pending");
+ OTHER(limit_).on(whence, "pending");
});
OPTION_(report_t, percent, DO() { // -%
- parent->HANDLER(total_)
- .set_expr(string("--percent"),
- "((is_account&parent&parent.total)?"
- " percent(scrub(total), scrub(parent.total)):0)");
+ OTHER(total_)
+ .on(whence,
+ "((is_account&parent&parent.total)?"
+ " percent(scrub(total), scrub(parent.total)):0)");
});
- OPTION__
- (report_t, period_, // -p
- CTOR(report_t, period_) {}
- virtual void on_with(const optional<string>& whence, const value_t& text) {
- if (! handled)
- option_t<report_t>::on_with(whence, text);
- else
- option_t<report_t>::on_with(whence,
- string_value(text.as_string() + " " + str()));
+ OPTION_
+ (report_t, period_,
+ DO_(str) { // -p
+ if (handled)
+ value += string(" ") + str;
});
OPTION(report_t, pivot_);
- OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) {
- on(none,
- "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
- });
+ OPTION__
+ (report_t, plot_amount_format_,
+ CTOR(report_t, plot_amount_format_) {
+ on(none,
+ "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
+ });
- OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) {
- on(none,
- "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
- });
+ OPTION__
+ (report_t, plot_total_format_,
+ CTOR(report_t, plot_total_format_) {
+ on(none,
+ "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
+ });
OPTION(report_t, prepend_format_);
- OPTION_(report_t, prepend_width_, DO_(args) {
- value = args.get<long>(1);
- });
+ OPTION(report_t, prepend_width_);
OPTION_(report_t, price, DO() { // -I
- parent->HANDLER(display_amount_)
- .set_expr(string("--price"), "price(amount_expr)");
- parent->HANDLER(display_total_)
- .set_expr(string("--price"), "price(total_expr)");
+ OTHER(amount_).expr.set_base_expr("price");
});
- OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) {
- on(none,
- "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, "
- " 2 + 9 + 8 + 12, true, color))\n");
- });
+ OPTION__
+ (report_t, prices_format_,
+ CTOR(report_t, prices_format_) {
+ on(none,
+ "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, "
+ " 2 + 9 + 8 + 12, true, color))\n");
+ });
- OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) {
- on(none,
- "P %(datetime) %(display_account) %(scrub(display_amount))\n");
- });
+ OPTION__
+ (report_t, pricedb_format_,
+ CTOR(report_t, pricedb_format_) {
+ on(none,
+ "P %(datetime) %(display_account) %(scrub(display_amount))\n");
+ });
OPTION(report_t, primary_date);
OPTION_(report_t, quantity, DO() { // -O
- parent->HANDLER(revalued).off();
- parent->HANDLER(amount_).set_expr(string("--quantity"), "amount");
- parent->HANDLER(total_).set_expr(string("--quantity"), "total");
+ OTHER(revalued).off();
+
+ OTHER(amount_).expr.set_base_expr("amount");
+ OTHER(total_).expr.set_base_expr("total");
});
OPTION_(report_t, quarterly, DO() {
- parent->HANDLER(period_).on(string("--quarterly"), "quarterly");
+ OTHER(period_).on(whence, "quarterly");
});
OPTION(report_t, raw);
OPTION_(report_t, real, DO() { // -R
- parent->HANDLER(limit_).on(string("--real"), "real");
+ OTHER(limit_).on(whence, "real");
});
- OPTION__(report_t, register_format_, CTOR(report_t, register_format_) {
- on(none,
- "%(ansify_if("
- " ansify_if(justify(format_date(date), date_width),"
- " green if color and date > today),"
- " bold if should_bold))"
- " %(ansify_if("
- " ansify_if(justify(truncated(payee, payee_width), payee_width), "
- " bold if color and !cleared and actual),"
- " bold if should_bold))"
- " %(ansify_if("
- " ansify_if(justify(truncated(display_account, account_width, "
- " abbrev_len), account_width),"
- " blue if color),"
- " bold if should_bold))"
- " %(ansify_if("
- " justify(scrub(display_amount), amount_width, "
- " 3 + meta_width + date_width + payee_width"
- " + account_width + amount_width + prepend_width,"
- " true, color),"
- " bold if should_bold))"
- " %(ansify_if("
- " justify(scrub(display_total), total_width, "
- " 4 + meta_width + date_width + payee_width"
- " + account_width + amount_width + total_width"
- " + prepend_width, true, color),"
- " bold if should_bold))\n%/"
- "%(justify(\" \", date_width))"
- " %(ansify_if("
- " justify(truncated(has_tag(\"Payee\") ? payee : \" \", "
- " payee_width), payee_width),"
- " bold if should_bold))"
- " %$3 %$4 %$5\n");
- });
+ OPTION__
+ (report_t, register_format_,
+ CTOR(report_t, register_format_) {
+ on(none,
+ "%(ansify_if("
+ " ansify_if(justify(format_date(date), int(date_width)),"
+ " green if color and date > today),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), "
+ " bold if color and !cleared and actual),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " ansify_if(justify(truncated(display_account, int(account_width), "
+ " abbrev_len), int(account_width)),"
+ " blue if color),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " justify(scrub(display_amount), int(amount_width), "
+ " 3 + int(meta_width) + int(date_width) + int(payee_width)"
+ " + int(account_width) + int(amount_width) + int(prepend_width),"
+ " true, color),"
+ " bold if should_bold))"
+ " %(ansify_if("
+ " justify(scrub(display_total), int(total_width), "
+ " 4 + int(meta_width) + int(date_width) + int(payee_width)"
+ " + int(account_width) + int(amount_width) + int(total_width)"
+ " + int(prepend_width), true, color),"
+ " bold if should_bold))\n%/"
+ "%(justify(\" \", int(date_width)))"
+ " %(ansify_if("
+ " justify(truncated(has_tag(\"Payee\") ? payee : \" \", "
+ " int(payee_width)), int(payee_width)),"
+ " bold if should_bold))"
+ " %$3 %$4 %$5\n");
+ });
OPTION(report_t, related); // -r
OPTION_(report_t, related_all, DO() {
- parent->HANDLER(related).on_only(string("--related-all"));
+ OTHER(related).on(whence);
});
OPTION(report_t, revalued);
OPTION(report_t, revalued_only);
- OPTION__
+ OPTION_
(report_t, revalued_total_,
expr_t expr;
- CTOR(report_t, revalued_total_) {}
- void set_expr(const optional<string>& whence, const string& str) {
+ DO_(str) {
expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
});
OPTION(report_t, rich_data);
OPTION(report_t, seed_);
- OPTION_(report_t, sort_, DO_(args) { // -S
- on_with(args.get<string>(0), args[1]);
- parent->HANDLER(sort_xacts_).off();
- parent->HANDLER(sort_all_).off();
+ OPTION_(report_t, sort_, DO_(str) { // -S
+ OTHER(sort_xacts_).off();
+ OTHER(sort_all_).off();
});
- OPTION_(report_t, sort_all_, DO_(args) {
- parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]);
- parent->HANDLER(sort_xacts_).off();
+ OPTION_(report_t, sort_all_, DO_(str) {
+ OTHER(sort_).on(whence, str);
+ OTHER(sort_xacts_).off();
});
- OPTION_(report_t, sort_xacts_, DO_(args) {
- parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]);
- parent->HANDLER(sort_all_).off();
+ OPTION_(report_t, sort_xacts_, DO_(str) {
+ OTHER(sort_).on(whence, str);
+ OTHER(sort_all_).off();
});
OPTION(report_t, start_of_week_);
@@ -920,22 +948,14 @@ public:
OPTION__
(report_t, total_, // -T
- expr_t expr;
- CTOR(report_t, total_) {
- set_expr(none, "total");
- }
- void set_expr(const optional<string>& whence, const string& str) {
- expr = str;
- on(whence, str);
- }
- DO_(args) {
- set_expr(args.get<string>(0), args.get<string>(1));
+ DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {}
+ DO_(str) {
+ expr.append(str);
});
OPTION(report_t, total_data); // -J
- OPTION_(report_t, truncate_, DO_(args) {
- string style(args.get<string>(1));
+ OPTION_(report_t, truncate_, DO_(style) {
if (style == "leading")
format_t::default_style = format_t::TRUNCATE_LEADING;
else if (style == "middle")
@@ -953,7 +973,7 @@ public:
});
OPTION_(report_t, uncleared, DO() { // -U
- parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending");
+ OTHER(limit_).on(whence, "uncleared|pending");
});
OPTION(report_t, unrealized);
@@ -962,48 +982,28 @@ public:
OPTION(report_t, unrealized_losses_);
OPTION_(report_t, unround, DO() {
- parent->HANDLER(display_amount_)
- .set_expr(string("--unround"), "unrounded(amount_expr)");
- parent->HANDLER(display_total_)
- .set_expr(string("--unround"), "unrounded(total_expr)");
+ OTHER(amount_).on(whence, "unrounded(amount_expr)");
+ OTHER(total_).on(whence, "unrounded(total_expr)");
});
OPTION_(report_t, weekly, DO() { // -W
- parent->HANDLER(period_).on(string("--weekly"), "weekly");
+ OTHER(period_).on(whence, "weekly");
});
OPTION_(report_t, wide, DO() { // -w
- parent->HANDLER(columns_).on_with(string("--wide"), 132L);
+ OTHER(columns_).on(whence, "132");
});
OPTION_(report_t, yearly, DO() { // -Y
- parent->HANDLER(period_).on(string("--yearly"), "yearly");
+ OTHER(period_).on(whence, "yearly");
});
- OPTION__(report_t, meta_width_,
- bool specified;
- CTOR(report_t, meta_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
- OPTION__(report_t, date_width_,
- bool specified;
- CTOR(report_t, date_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
- OPTION__(report_t, payee_width_,
- bool specified;
- CTOR(report_t, payee_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
- OPTION__(report_t, account_width_,
- bool specified;
- CTOR(report_t, account_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
- OPTION__(report_t, amount_width_,
- bool specified;
- CTOR(report_t, amount_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
- OPTION__(report_t, total_width_,
- bool specified;
- CTOR(report_t, total_width_) { specified = false; }
- DO_(args) { value = args.get<long>(1); specified = true; });
+ OPTION(report_t, meta_width_);
+ OPTION(report_t, date_width_);
+ OPTION(report_t, payee_width_);
+ OPTION(report_t, account_width_);
+ OPTION(report_t, amount_width_);
+ OPTION(report_t, total_width_);
};
diff --git a/src/scope.cc b/src/scope.cc
index b2a7b17b..00327159 100644
--- a/src/scope.cc
+++ b/src/scope.cc
@@ -35,7 +35,8 @@
namespace ledger {
-scope_t * scope_t::default_scope = NULL;
+scope_t * scope_t::default_scope = NULL;
+empty_scope_t * scope_t::empty_scope = NULL;
void symbol_scope_t::define(const symbol_t::kind_t kind,
const string& name, expr_t::ptr_op_t def)
@@ -53,8 +54,8 @@ void symbol_scope_t::define(const symbol_t::kind_t kind,
assert(i != symbols->end());
symbols->erase(i);
- result = symbols->insert(symbol_map::value_type(symbol_t(kind, name, def),
- def));
+ result = symbols->insert(symbol_map::value_type
+ (symbol_t(kind, name, def), def));
if (! result.second)
throw_(compile_error,
_("Redefinition of '%1' in the same scope") << name);
diff --git a/src/scope.h b/src/scope.h
index 785ce284..ccfc750b 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -70,8 +70,7 @@ struct symbol_t
TRACE_CTOR(symbol_t, "symbol_t::kind_t, string");
}
symbol_t(const symbol_t& sym)
- : kind(sym.kind), name(sym.name),
- definition(sym.definition) {
+ : kind(sym.kind), name(sym.name), definition(sym.definition) {
TRACE_CTOR(symbol_t, "copy");
}
~symbol_t() throw() {
@@ -81,6 +80,9 @@ struct symbol_t
bool operator<(const symbol_t& sym) const {
return kind < sym.kind || name < sym.name;
}
+ bool operator==(const symbol_t& sym) const {
+ return kind == sym.kind || name == sym.name;
+ }
#if defined(HAVE_BOOST_SERIALIZATION)
private:
@@ -97,10 +99,13 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class empty_scope_t;
+
class scope_t
{
public:
- static scope_t * default_scope;
+ static scope_t * default_scope;
+ static empty_scope_t * empty_scope;
explicit scope_t() {
TRACE_CTOR(scope_t, "");
@@ -134,6 +139,17 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class empty_scope_t : public scope_t
+{
+public:
+ virtual string description() {
+ return _("<empty>");
+ }
+ virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t, const string&) {
+ return NULL;
+ }
+};
+
class child_scope_t : public noncopyable, public scope_t
{
public:
@@ -142,8 +158,7 @@ public:
explicit child_scope_t() : parent(NULL) {
TRACE_CTOR(child_scope_t, "");
}
- explicit child_scope_t(scope_t& _parent)
- : parent(&_parent) {
+ explicit child_scope_t(scope_t& _parent) : parent(&_parent) {
TRACE_CTOR(child_scope_t, "scope_t&");
}
virtual ~child_scope_t() {
@@ -229,6 +244,8 @@ private:
template <typename T>
T * search_scope(scope_t * ptr, bool prefer_direct_parents = false)
{
+ DEBUG("scope.search", "Searching scope " << ptr->description());
+
if (T * sought = dynamic_cast<T *>(ptr))
return sought;
@@ -274,7 +291,7 @@ class symbol_scope_t : public child_scope_t
optional<symbol_map> symbols;
public:
- explicit symbol_scope_t() {
+ explicit symbol_scope_t() : child_scope_t() {
TRACE_CTOR(symbol_scope_t, "");
}
explicit symbol_scope_t(scope_t& _parent) : child_scope_t(_parent) {
@@ -363,13 +380,8 @@ protected:
class call_scope_t : public context_scope_t
{
-#if defined(DEBUG_ON)
public:
-#endif
value_t args;
-#if defined(DEBUG_ON)
-private:
-#endif
mutable void * ptr;
value_t& resolve(const std::size_t index,
@@ -386,17 +398,12 @@ public:
: context_scope_t(_parent, _parent.type_context(),
_parent.type_required()),
ptr(NULL), locus(_locus), depth(_depth) {
- TRACE_CTOR(call_scope_t,
- "scope_t&, value_t::type_t, bool, expr_t::ptr_op_t *, int");
+ TRACE_CTOR(call_scope_t, "scope_t&, expr_t::ptr_op_t *, const int");
}
virtual ~call_scope_t() {
TRACE_DTOR(call_scope_t);
}
- virtual string description() {
- return context_scope_t::description();
- }
-
void set_args(const value_t& _args) {
args = _args;
}
diff --git a/src/series.h b/src/series.h
deleted file mode 100644
index 75b98194..00000000
--- a/src/series.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2003-2012, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @addtogroup expr
- */
-
-/**
- * @file series.h
- * @author John Wiegley
- *
- * @ingroup expr
- */
-#ifndef _SERIES_H
-#define _SERIES_H
-
-#include "scope.h"
-
-namespace ledger {
-
-class expr_series_t
-{
-protected:
- scope_t * context;
-
-public:
- optional<std::list<expr_t> > exprs;
- expr_t default_expr;
- std::string variable;
-
- expr_series_t(const std::string& _variable)
- : context(NULL), default_expr(_variable), variable(_variable) {
- TRACE_CTOR(expr_series_t, "std::string");
- }
- expr_series_t(const expr_t& expr, const std::string& _variable)
- : context(const_cast<expr_t&>(expr).get_context()),
- default_expr(expr), variable(_variable) {
- TRACE_CTOR(expr_series_t, "expr_t, std::string");
- }
- expr_series_t(const expr_series_t& other)
- : context(other.context), exprs(other.exprs),
- default_expr(other.default_expr), variable(other.variable) {
- TRACE_CTOR(expr_series_t, "copy");
- }
- virtual ~expr_series_t() {
- TRACE_DTOR(expr_series_t);
- }
-
- scope_t * get_context() {
- return context;
- }
- void set_context(scope_t * scope) {
- context = scope;
- }
-
- bool empty() const {
- return ! exprs || exprs->empty();
- }
-
- void push_back(const expr_t& expr) {
- if (! exprs)
- exprs = std::list<expr_t>();
- exprs->push_back(expr);
- }
- void pop_back() {
- assert(exprs);
- exprs->pop_back();
- }
-
- void mark_uncompiled() {
- if (exprs)
- foreach (expr_t& expr, *exprs)
- expr.mark_uncompiled();
- else
- default_expr.mark_uncompiled();
- }
-
- void compile(scope_t& scope) {
- if (exprs)
- foreach (expr_t& expr, *exprs)
- expr.compile(scope);
- else
- default_expr.compile(scope);
- }
-
- value_t calc(scope_t& scope) {
- if (exprs) {
- value_t result;
- symbol_scope_t sym_scope(scope);
- std::size_t len(exprs->size());
-
- foreach (expr_t& expr, *exprs) {
- result = expr.calc(sym_scope);
- if (--len > 0)
- sym_scope.define(symbol_t::FUNCTION, variable,
- expr_t::op_t::wrap_value(result));
- }
- return result;
- } else {
- return default_expr.calc(scope);
- }
- }
-};
-
-} // namespace ledger
-
-#endif // _SERIES_H
diff --git a/src/session.cc b/src/session.cc
index 3e7cdb3d..76061de7 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -112,6 +112,8 @@ std::size_t session_t::read_data(const string& master_account)
journal->checking_style = journal_t::CHECK_ERROR;
else if (HANDLED(strict))
journal->checking_style = journal_t::CHECK_WARNING;
+ else if (HANDLED(value_expr_))
+ journal->value_expr = HANDLER(value_expr_).str();
#if defined(HAVE_BOOST_SERIALIZATION)
optional<archive_t> cache;
@@ -268,6 +270,15 @@ value_t session_t::fn_max(call_scope_t& args)
return args[1] > args[0] ? args[1] : args[0];
}
+value_t session_t::fn_int(call_scope_t& args)
+{
+ return args[0].to_long();
+}
+value_t session_t::fn_str(call_scope_t& args)
+{
+ return string_value(args[0].to_string());
+}
+
value_t session_t::fn_lot_price(call_scope_t& args)
{
amount_t amt(args.get<amount_t>(1, false));
@@ -334,6 +345,9 @@ option_t<session_t> * session_t::lookup_option(const char * p)
case 's':
OPT(strict);
break;
+ case 'v':
+ OPT(value_expr_);
+ break;
}
return NULL;
}
@@ -360,6 +374,11 @@ expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind,
return MAKE_FUNCTOR(session_t::fn_lot_tag);
break;
+ case 'i':
+ if (is_eq(p, "int"))
+ return MAKE_FUNCTOR(session_t::fn_int);
+ break;
+
case 'm':
if (is_eq(p, "min"))
return MAKE_FUNCTOR(session_t::fn_min);
@@ -367,6 +386,11 @@ expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind,
return MAKE_FUNCTOR(session_t::fn_max);
break;
+ case 's':
+ if (is_eq(p, "str"))
+ return MAKE_FUNCTOR(session_t::fn_str);
+ break;
+
default:
break;
}
diff --git a/src/session.h b/src/session.h
index 879efeb6..962664ef 100644
--- a/src/session.h
+++ b/src/session.h
@@ -59,8 +59,9 @@ class session_t : public symbol_scope_t
public:
bool flush_on_next_data_file;
- std::auto_ptr<journal_t> journal;
- parse_context_stack_t parsing_context;
+ unique_ptr<journal_t> journal;
+ parse_context_stack_t parsing_context;
+ optional<expr_t> value_expr;
explicit session_t();
virtual ~session_t() {
@@ -86,6 +87,8 @@ public:
value_t fn_account(call_scope_t& scope);
value_t fn_min(call_scope_t& scope);
value_t fn_max(call_scope_t& scope);
+ value_t fn_int(call_scope_t& scope);
+ value_t fn_str(call_scope_t& scope);
value_t fn_lot_price(call_scope_t& scope);
value_t fn_lot_date(call_scope_t& scope);
value_t fn_lot_tag(call_scope_t& scope);
@@ -105,6 +108,7 @@ public:
HANDLER(price_db_).report(out);
HANDLER(price_exp_).report(out);
HANDLER(strict).report(out);
+ HANDLER(value_expr_).report(out);
}
option_t<session_t> * lookup_option(const char * p);
@@ -126,28 +130,24 @@ public:
OPTION__
(session_t, price_exp_, // -Z
- CTOR(session_t, price_exp_) { value = 24L * 3600L; }
- DO_(args) {
- value = args.get<long>(1) * 60L;
- });
+ CTOR(session_t, price_exp_) { value = "24"; });
OPTION__
(session_t, file_, // -f
std::list<path> data_files;
CTOR(session_t, file_) {}
- DO_(args) {
- assert(args.size() == 2);
+ DO_(str) {
if (parent->flush_on_next_data_file) {
data_files.clear();
parent->flush_on_next_data_file = false;
}
- data_files.push_back(args.get<string>(1));
+ data_files.push_back(str);
});
- OPTION_(session_t, input_date_format_, DO_(args) {
- // This changes static variables inside times.h, which affects the basic
- // date parser.
- set_input_date_format(args.get<string>(1).c_str());
+ OPTION_(session_t, input_date_format_, DO_(str) {
+ // This changes static variables inside times.h, which affects the
+ // basic date parser.
+ set_input_date_format(str.c_str());
});
OPTION(session_t, explicit);
@@ -156,6 +156,7 @@ public:
OPTION(session_t, permissive);
OPTION(session_t, price_db_);
OPTION(session_t, strict);
+ OPTION(session_t, value_expr_);
};
/**
diff --git a/src/system.hh.in b/src/system.hh.in
index 5e5a0c1d..a38deb1f 100644
--- a/src/system.hh.in
+++ b/src/system.hh.in
@@ -183,8 +183,13 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <boost/regex/icu.hpp>
#else
#include <boost/regex.hpp>
-
#endif // HAVE_BOOST_REGEX_UNICODE
+
+#include <boost/tokenizer.hpp>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
#include <boost/variant.hpp>
#include <boost/version.hpp>
diff --git a/src/textual.cc b/src/textual.cc
index 95635184..a8cb844b 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -71,22 +71,22 @@ namespace {
class instance_t : public noncopyable, public scope_t
{
-
public:
- parse_context_stack_t& context_stack;
- parse_context_t& context;
- std::istream& in;
- instance_t * parent;
-
+ parse_context_stack_t& context_stack;
+ parse_context_t& context;
+ std::istream& in;
+ instance_t * parent;
std::list<application_t> apply_stack;
#if defined(TIMELOG_SUPPORT)
- time_log_t timelog;
+ time_log_t timelog;
#endif
instance_t(parse_context_stack_t& _context_stack,
- parse_context_t& _context, instance_t * _parent = NULL)
+ parse_context_t& _context,
+ instance_t * _parent = NULL)
: context_stack(_context_stack), context(_context),
- in(*context.stream.get()), parent(_parent), timelog(context) {}
+ in(*context.stream.get()), parent(_parent),
+ timelog(context) {}
virtual string description() {
return _("textual parser");
@@ -107,10 +107,12 @@ namespace {
return (in.good() && ! in.eof() &&
(in.peek() == ' ' || in.peek() == '\t'));
}
+#if defined(HAVE_BOOST_PYTHON)
bool peek_blank_line() {
return (in.good() && ! in.eof() &&
(in.peek() == '\n' || in.peek() == '\r'));
}
+#endif
void read_next_directive();
@@ -124,6 +126,7 @@ namespace {
void account_directive(char * line);
void account_alias_directive(account_t * account, string alias);
void account_payee_directive(account_t * account, string payee);
+ void account_value_directive(account_t * account, string expr_str);
void account_default_directive(account_t * account);
void default_account_directive(char * line);
@@ -134,6 +137,7 @@ namespace {
void commodity_directive(char * line);
void commodity_alias_directive(commodity_t& comm, string alias);
+ void commodity_value_directive(commodity_t& comm, string expr_str);
void commodity_format_directive(commodity_t& comm, string format);
void commodity_nomarket_directive(commodity_t& comm);
void commodity_default_directive(commodity_t& comm);
@@ -163,10 +167,10 @@ namespace {
void eval_directive(char * line);
void assert_directive(char * line);
void check_directive(char * line);
+ void value_directive(char * line);
-#if defined(HAVE_BOOST_PYTHON)
+ void import_directive(char * line);
void python_directive(char * line);
-#endif
post_t * parse_post(char * line,
std::streamsize len,
@@ -527,7 +531,7 @@ void instance_t::automated_xact_directive(char * line)
query.parse_args(string_value(skip_ws(line + 1)).to_sequence(),
keeper, false, true);
- std::auto_ptr<auto_xact_t> ae(new auto_xact_t(predicate_t(expr, keeper)));
+ unique_ptr<auto_xact_t> ae(new auto_xact_t(predicate_t(expr, keeper)));
ae->pos = position_t();
ae->pos->pathname = context.pathname;
ae->pos->beg_pos = context.line_beg_pos;
@@ -557,11 +561,6 @@ void instance_t::automated_xact_directive(char * line)
item->add_flags(ITEM_NOTE_ON_NEXT_LINE);
item->pos->end_pos = context.curr_pos;
item->pos->end_line++;
-
- // If there was no last_post yet, then deferred notes get applied to
- // the matched posting. Other notes get applied to the auto-generated
- // posting.
- ae->deferred_notes->back().apply_to_post = last_post;
}
else if ((remlen > 7 && *p == 'a' &&
std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) ||
@@ -589,7 +588,7 @@ void instance_t::automated_xact_directive(char * line)
parse_post(p, len - (p - line), top_account(), NULL, true)) {
reveal_context = true;
ae->add_post(post);
- last_post = post;
+ ae->active_post = last_post = post;
}
reveal_context = true;
}
@@ -621,7 +620,7 @@ void instance_t::period_xact_directive(char * line)
try {
- std::auto_ptr<period_xact_t> pe(new period_xact_t(skip_ws(line + 1)));
+ unique_ptr<period_xact_t> pe(new period_xact_t(skip_ws(line + 1)));
pe->pos = position_t();
pe->pos->pathname = context.pathname;
pe->pos->beg_pos = context.line_beg_pos;
@@ -665,7 +664,7 @@ void instance_t::xact_directive(char * line, std::streamsize len)
TRACE_START(xacts, 1, "Time spent handling transactions:");
if (xact_t * xact = parse_xact(line, len, top_account())) {
- std::auto_ptr<xact_t> manager(xact);
+ unique_ptr<xact_t> manager(xact);
if (context.journal->add_xact(xact)) {
manager.release(); // it's owned by the journal now
@@ -874,7 +873,7 @@ void instance_t::account_directive(char * line)
char * p = skip_ws(line);
account_t * account =
context.journal->register_account(p, NULL, top_account());
- std::auto_ptr<auto_xact_t> ae;
+ unique_ptr<auto_xact_t> ae;
while (peek_whitespace_line()) {
read_line(line);
@@ -890,6 +889,9 @@ void instance_t::account_directive(char * line)
else if (keyword == "payee") {
account_payee_directive(account, b);
}
+ else if (keyword == "value") {
+ account_value_directive(account, b);
+ }
else if (keyword == "default") {
account_default_directive(account);
}
@@ -943,10 +945,14 @@ void instance_t::account_alias_directive(account_t * account, string alias)
// (account), add a reference to the account in the `account_aliases'
// map, which is used by the post parser to resolve alias references.
trim(alias);
- std::pair<accounts_map::iterator, bool> result
- = context.journal
- ->account_aliases.insert(accounts_map::value_type(alias, account));
+#if defined(DEBUG_ON)
+ std::pair<accounts_map::iterator, bool> result =
+#endif
+ context.journal->account_aliases.insert
+ (accounts_map::value_type(alias, account));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
}
void instance_t::alias_directive(char * line)
@@ -975,6 +981,11 @@ void instance_t::account_default_directive(account_t * account)
context.journal->bucket = account;
}
+void instance_t::account_value_directive(account_t * account, string expr_str)
+{
+ account->value_expr = expr_t(expr_str);
+}
+
void instance_t::payee_directive(char * line)
{
string payee = context.journal->register_payee(line, NULL);
@@ -1019,6 +1030,8 @@ void instance_t::commodity_directive(char * line)
string keyword(q);
if (keyword == "alias")
commodity_alias_directive(*commodity, b);
+ else if (keyword == "value")
+ commodity_value_directive(*commodity, b);
else if (keyword == "format")
commodity_format_directive(*commodity, b);
else if (keyword == "nomarket")
@@ -1031,17 +1044,15 @@ void instance_t::commodity_directive(char * line)
}
}
-void instance_t::commodity_alias_directive(commodity_t&, string)
+void instance_t::commodity_alias_directive(commodity_t& comm, string alias)
{
-#if 0
trim(alias);
- std::pair<commodity_pool_t::commodities_map::iterator, bool> result
- = commodity_pool_t::current_pool->commodities.insert
- (commodity_pool_t::commodities_map::value_type(alias, &comm));
- if (! result.second)
- throw_(parse_error,
- _("Cannot use existing commodity name as an alias: %1") << alias);
-#endif
+ commodity_pool_t::current_pool->alias(alias, comm);
+}
+
+void instance_t::commodity_value_directive(commodity_t& comm, string expr_str)
+{
+ comm.set_value_expr(expr_t(expr_str));
}
void instance_t::commodity_format_directive(commodity_t&, string format)
@@ -1109,6 +1120,11 @@ void instance_t::check_directive(char * line)
context.warning(STR(_("Check failed: %1") << line));
}
+void instance_t::value_directive(char * line)
+{
+ context.journal->value_expr = expr_t(line);
+}
+
void instance_t::comment_directive(char * line)
{
while (in.good() && ! in.eof()) {
@@ -1121,6 +1137,14 @@ void instance_t::comment_directive(char * line)
}
#if defined(HAVE_BOOST_PYTHON)
+
+void instance_t::import_directive(char * line)
+{
+ string module_name(line);
+ trim(module_name);
+ python_session->import_option(module_name);
+}
+
void instance_t::python_directive(char * line)
{
std::ostringstream script;
@@ -1160,6 +1184,21 @@ void instance_t::python_directive(char * line)
("journal", python::object(python::ptr(context.journal)));
python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI);
}
+
+#else
+
+void instance_t::import_directive(char *)
+{
+ throw_(parse_error,
+ _("'python' directive seen, but Python support is missing"));
+}
+
+void instance_t::python_directive(char *)
+{
+ throw_(parse_error,
+ _("'import' directive seen, but Python support is missing"));
+}
+
#endif // HAVE_BOOST_PYTHON
bool instance_t::general_directive(char * line)
@@ -1239,6 +1278,10 @@ bool instance_t::general_directive(char * line)
include_directive(arg);
return true;
}
+ else if (std::strcmp(p, "import") == 0) {
+ import_directive(arg);
+ return true;
+ }
break;
case 'p':
@@ -1247,12 +1290,7 @@ bool instance_t::general_directive(char * line)
return true;
}
else if (std::strcmp(p, "python") == 0) {
-#if defined(HAVE_BOOST_PYTHON)
python_directive(arg);
-#else
- throw_(parse_error,
- _("'python' directive seen, but Python support is missing"));
-#endif
return true;
}
break;
@@ -1267,6 +1305,13 @@ bool instance_t::general_directive(char * line)
return true;
}
break;
+
+ case 'v':
+ if (std::strcmp(p, "value") == 0) {
+ value_directive(arg);
+ return true;
+ }
+ break;
}
if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) {
@@ -1287,7 +1332,7 @@ post_t * instance_t::parse_post(char * line,
{
TRACE_START(post_details, 1, "Time spent parsing postings:");
- std::auto_ptr<post_t> post(new post_t);
+ unique_ptr<post_t> post(new post_t);
post->xact = xact; // this could be NULL
post->pos = position_t();
@@ -1402,12 +1447,16 @@ post_t * instance_t::parse_post(char * line,
// Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST)
- if (*next == '@') {
+ if (*next == '@' || (*next == '(' && *(next + 1) == '@')) {
DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Found a price indicator");
- bool per_unit = true;
+ if (*next == '(') {
+ post->add_flags(POST_COST_VIRTUAL);
+ ++next;
+ }
+ bool per_unit = true;
if (*++next == '@') {
per_unit = false;
post->add_flags(POST_COST_IN_FULL);
@@ -1415,6 +1464,9 @@ post_t * instance_t::parse_post(char * line,
<< "And it's for a total price");
}
+ if (post->has_flags(POST_COST_VIRTUAL) && *(next + 1) == ')')
+ ++next;
+
beg = static_cast<std::streamsize>(++next - line);
p = skip_ws(next);
diff --git a/src/timelog.cc b/src/timelog.cc
index 67ea1015..b46d3922 100644
--- a/src/timelog.cc
+++ b/src/timelog.cc
@@ -88,7 +88,7 @@ namespace {
if (! out_event.note.empty() && event.note.empty())
event.note = out_event.note;
- std::auto_ptr<xact_t> curr(new xact_t);
+ unique_ptr<xact_t> curr(new xact_t);
curr->_date = event.checkin.date();
curr->code = out_event.desc; // if it wasn't used above
curr->payee = event.desc;
diff --git a/src/times.h b/src/times.h
index 6eadcad3..e3134665 100644
--- a/src/times.h
+++ b/src/times.h
@@ -218,6 +218,9 @@ struct date_duration_t
case YEARS:
return date + gregorian::years(length);
}
+#if !defined(__clang__)
+ return date_t();
+#endif
}
date_t subtract(const date_t& date) const {
@@ -233,6 +236,9 @@ struct date_duration_t
case YEARS:
return date - gregorian::years(length);
}
+#if !defined(__clang__)
+ return date_t();
+#endif
}
string to_string() const {
diff --git a/src/token.cc b/src/token.cc
index 57b97eca..e5d6b218 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -421,7 +421,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
throw_(parse_error, _("Failed to reset input stream"));
c = static_cast<char>(in.peek());
- if (! std::isalpha(c))
+ if (! std::isalpha(c) && c != '_')
expected('\0', c);
parse_ident(in);
diff --git a/src/utils.cc b/src/utils.cc
index eb1d8009..628fb158 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -553,9 +553,6 @@ void logger_func(log_level_t level)
#if defined(VERIFY_ON)
IF_VERIFY()
*_log_stream << " TIME OBJSZ MEMSZ" << std::endl;
-#else
- IF_VERIFY()
- *_log_stream << " TIME" << std::endl;
#endif
}
diff --git a/src/utils.h b/src/utils.h
index e37f37aa..efa203c7 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -171,7 +171,7 @@ void report_memory(std::ostream& out, bool report_all = false);
#else // ! VERIFY_ON
#define VERIFY(x)
-#define DO_VERIFY() true
+#define DO_VERIFY() false
#define TRACE_CTOR(cls, args)
#define TRACE_DTOR(cls)
@@ -278,6 +278,16 @@ extern string empty_string;
strings_list split_arguments(const char * line);
+inline string to_string(long num) {
+ std::ostringstream buf;
+ buf << num;
+ return buf.str();
+}
+
+inline string operator+(const char * left, const string& right) {
+ return string(left) + right;
+}
+
} // namespace ledger
/*@}*/
@@ -353,8 +363,8 @@ inline bool category_matches(const char * cat) {
boost::make_u32regex(_log_category->c_str(),
boost::regex::perl | boost::regex::icase);
#else
- boost::make_regex(_log_category->c_str(),
- boost::regex::perl | boost::regex::icase);
+ boost::regex(_log_category->c_str(),
+ boost::regex::perl | boost::regex::icase);
#endif
}
#if defined(HAVE_BOOST_REGEX_UNICODE)
diff --git a/src/value.cc b/src/value.cc
index 2446c97a..c14a7104 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -724,7 +724,7 @@ value_t& value_t::operator/=(const value_t& val)
return *this;
case AMOUNT:
if (as_balance().single_amount()) {
- in_place_simplify();
+ in_place_cast(AMOUNT);
as_amount_lval() /= val.as_amount();
return *this;
}
@@ -1228,7 +1228,7 @@ void value_t::in_place_cast(type_t cast_type)
case STRING:
switch (cast_type) {
case INTEGER: {
- if (all(as_string(), is_digit())) {
+ if (all(as_string(), is_any_of("-0123456789"))) {
set_long(lexical_cast<long>(as_string()));
return;
}
@@ -1266,8 +1266,8 @@ void value_t::in_place_cast(type_t cast_type)
}
add_error_context(_("While converting %1:") << *this);
- throw_(value_error, _("Cannot convert %1 to %2")
- << label() << label(cast_type));
+ throw_(value_error,
+ _("Cannot convert %1 to %2") << label() << label(cast_type));
}
void value_t::in_place_negate()
@@ -1399,25 +1399,30 @@ bool value_t::is_zero() const
return false;
}
-value_t value_t::value(const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of) const
+value_t value_t::value(const datetime_t& moment,
+ const commodity_t * in_terms_of) const
{
switch (type()) {
case INTEGER:
return NULL_VALUE;
case AMOUNT:
- if (optional<amount_t> val =
- as_amount().value(moment, in_terms_of))
+ if (optional<amount_t> val = as_amount().value(moment, in_terms_of))
return *val;
return NULL_VALUE;
case BALANCE:
- if (optional<balance_t> bal =
- as_balance().value(moment, in_terms_of))
+ if (optional<balance_t> bal = as_balance().value(moment, in_terms_of))
return *bal;
return NULL_VALUE;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.value(moment, in_terms_of));
+ return temp;
+ }
+
default:
break;
}
@@ -1427,37 +1432,100 @@ value_t value_t::value(const optional<datetime_t>& moment,
return NULL_VALUE;
}
-value_t value_t::price() const
+value_t value_t::exchange_commodities(const std::string& commodities,
+ const bool add_prices,
+ const datetime_t& moment)
{
- switch (type()) {
- case AMOUNT:
- return as_amount().price();
- case BALANCE:
- return as_balance().price();
- default:
- return *this;
+ if (type() == SEQUENCE) {
+ value_t temp;
+ foreach (value_t& value, as_sequence_lval())
+ temp.push_back(value.exchange_commodities(commodities, add_prices, moment));
+ return temp;
}
-}
-value_t value_t::exchange_commodities(const std::string& commodities,
- const bool add_prices,
- const optional<datetime_t>& moment)
-{
- scoped_array<char> buf(new char[commodities.length() + 1]);
-
- std::strcpy(buf.get(), commodities.c_str());
-
- for (char * p = std::strtok(buf.get(), ",");
- p;
- p = std::strtok(NULL, ",")) {
- if (commodity_t * commodity =
- commodity_pool_t::current_pool->parse_price_expression(p, add_prices,
- moment)) {
- value_t result = value(moment, *commodity);
- if (! result.is_null())
- return result;
+ // If we are repricing to just a single commodity, with no price
+ // expression, skip the expensive logic below.
+ if (commodities.find(',') == string::npos &&
+ commodities.find('=') == string::npos)
+ return value(moment, commodity_pool_t::current_pool->find_or_create(commodities));
+
+ std::vector<commodity_t *> comms;
+ std::vector<bool> force;
+
+ typedef tokenizer<char_separator<char> > tokenizer;
+ tokenizer tokens(commodities, char_separator<char>(","));
+
+ foreach (const string& name, tokens) {
+ string::size_type name_len = name.length();
+
+ if (commodity_t * commodity = commodity_pool_t::current_pool
+ ->parse_price_expression(name[name_len - 1] == '!' ?
+ string(name, 0, name_len - 1) :
+ name, add_prices, moment)) {
+ DEBUG("commodity.exchange", "Pricing for commodity: " << commodity->symbol());
+ comms.push_back(&commodity->referent());
+ force.push_back(name[name_len - 1] == '!');
}
}
+
+ std::size_t index = 0;
+ foreach (commodity_t * comm, comms) {
+ switch (type()) {
+ case AMOUNT:
+ DEBUG("commodity.exchange", "We have an amount: " << as_amount_lval());
+ if (! force[index] &&
+ std::find(comms.begin(), comms.end(),
+ &as_amount_lval().commodity().referent()) != comms.end())
+ break;
+
+ DEBUG("commodity.exchange", "Referent doesn't match, pricing...");
+ if (optional<amount_t> val = as_amount_lval().value(moment, comm)) {
+ DEBUG("commodity.exchange", "Re-priced amount is: " << *val);
+ return *val;
+ }
+ DEBUG("commodity.exchange", "Was unable to find a price");
+ break;
+
+ case BALANCE: {
+ balance_t temp;
+ bool repriced = false;
+
+ DEBUG("commodity.exchange", "We have a balance: " << as_balance_lval());
+ foreach (const balance_t::amounts_map::value_type& pair,
+ as_balance_lval().amounts) {
+ DEBUG("commodity.exchange", "We have a balance amount of commodity: "
+ << pair.first->symbol() << " == "
+ << pair.second.commodity().symbol());
+ if (! force[index] &&
+ std::find(comms.begin(), comms.end(),
+ &pair.first->referent()) != comms.end()) {
+ temp += pair.second;
+ } else {
+ DEBUG("commodity.exchange", "Referent doesn't match, pricing...");
+ if (optional<amount_t> val = pair.second.value(moment, comm)) {
+ DEBUG("commodity.exchange", "Re-priced member amount is: " << *val);
+ temp += *val;
+ repriced = true;
+ } else {
+ DEBUG("commodity.exchange", "Was unable to find price");
+ temp += pair.second;
+ }
+ }
+ }
+
+ if (repriced) {
+ DEBUG("commodity.exchange", "Re-priced balance is: " << temp);
+ return temp;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ ++index;
+ }
+
return *this;
}
diff --git a/src/value.h b/src/value.h
index 1e4d0ce9..a95968c2 100644
--- a/src/value.h
+++ b/src/value.h
@@ -477,14 +477,12 @@ public:
void in_place_unreduce(); // exists for efficiency's sake
// Return the "market value" of a given value at a specific time.
- value_t value(const optional<datetime_t>& moment = none,
- const optional<commodity_t&>& in_terms_of = none) const;
+ value_t value(const datetime_t& moment = datetime_t(),
+ const commodity_t * in_terms_of = NULL) const;
- value_t price() const;
-
- value_t exchange_commodities(const std::string& commodities,
- const bool add_prices = false,
- const optional<datetime_t>& moment = none);
+ value_t exchange_commodities(const std::string& commodities,
+ const bool add_prices = false,
+ const datetime_t& moment = datetime_t());
/**
* Truth tests.
diff --git a/src/xact.cc b/src/xact.cc
index f05c6069..4e8e56fa 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -269,11 +269,13 @@ bool xact_base_t::finalize()
cost_breakdown_t breakdown =
commodity_pool_t::current_pool->exchange
- (post->amount, *post->cost, false,
+ (post->amount, *post->cost, false, ! post->has_flags(POST_COST_VIRTUAL),
datetime_t(date(), time_duration(0, 0, 0, 0)));
if (post->amount.has_annotation() && post->amount.annotation().price) {
if (breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) {
+ DEBUG("xact.finalize", "breakdown.basis_cost = " << breakdown.basis_cost);
+ DEBUG("xact.finalize", "breakdown.final_cost = " << breakdown.final_cost);
if (amount_t gain_loss = breakdown.basis_cost - breakdown.final_cost) {
DEBUG("xact.finalize", "gain_loss = " << gain_loss);
gain_loss.in_place_round();
@@ -329,13 +331,24 @@ bool xact_base_t::finalize()
if (balance.is_balance()) {
const balance_t& bal(balance.as_balance());
- typedef std::map<string, amount_t> sorted_amounts_map;
+#if 1
+ typedef std::map<std::pair<string, annotation_t>,
+ amount_t> sorted_amounts_map;
sorted_amounts_map samp;
foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) {
+#if defined(DEBUG_ON)
std::pair<sorted_amounts_map::iterator, bool> result =
- samp.insert(sorted_amounts_map::value_type(pair.first->mapping_key(),
- pair.second));
+#endif
+ samp.insert(sorted_amounts_map::value_type
+ (sorted_amounts_map::key_type
+ (pair.first->symbol(),
+ pair.first->has_annotation() ?
+ as_annotated_commodity(*pair.first).details :
+ annotation_t()),
+ pair.second));
+#if defined(DEBUG_ON)
assert(result.second);
+#endif
}
bool first = true;
@@ -351,6 +364,21 @@ bool xact_base_t::finalize()
add_post(p);
}
}
+#else
+ bool first = true;
+ foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) {
+ if (first) {
+ null_post->amount = pair.second.negated();
+ null_post->add_flags(POST_CALCULATED);
+ first = false;
+ } else {
+ post_t * p = new post_t(null_post->account, pair.second.negated(),
+ ITEM_GENERATED | POST_CALCULATED);
+ p->set_state(null_post->state());
+ add_post(p);
+ }
+ }
+#endif
}
else if (balance.is_amount()) {
null_post->amount = balance.as_amount().negated();
@@ -749,22 +777,25 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context)
post_t * new_post = new post_t(account, amt);
new_post->copy_details(*post);
new_post->add_flags(ITEM_GENERATED);
-
- // jww (2012-02-27): Do account directive assertions get applied
- // to postings generated from automated transactions?
- xact.add_post(new_post);
- new_post->account->add_post(new_post);
-
- if (new_post->must_balance())
- needs_further_verification = true;
+ new_post->account =
+ journal->register_account(account->fullname(), new_post,
+ journal->master);
if (deferred_notes) {
foreach (deferred_tag_data_t& data, *deferred_notes) {
- if (data.apply_to_post == post)
+ if (! data.apply_to_post || data.apply_to_post == post)
new_post->parse_tags(data.tag_data.c_str(), bound_scope,
data.overwrite_existing);
}
}
+
+ extend_post(*new_post, *journal);
+
+ xact.add_post(new_post);
+ new_post->account->add_post(new_post);
+
+ if (new_post->must_balance())
+ needs_further_verification = true;
}
}
}
diff --git a/src/xact.h b/src/xact.h
index 7d7fb826..df82258d 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -173,17 +173,19 @@ public:
typedef std::list<deferred_tag_data_t> deferred_notes_list;
optional<deferred_notes_list> deferred_notes;
+ post_t * active_post;
- auto_xact_t() : try_quick_match(true) {
+ auto_xact_t() : try_quick_match(true), active_post(NULL) {
TRACE_CTOR(auto_xact_t, "");
}
auto_xact_t(const auto_xact_t& other)
: xact_base_t(), predicate(other.predicate),
- try_quick_match(other.try_quick_match) {
+ try_quick_match(other.try_quick_match),
+ active_post(other.active_post) {
TRACE_CTOR(auto_xact_t, "copy");
}
auto_xact_t(const predicate_t& _predicate)
- : predicate(_predicate), try_quick_match(true)
+ : predicate(_predicate), try_quick_match(true), active_post(NULL)
{
TRACE_CTOR(auto_xact_t, "const predicate_t&");
}
@@ -202,12 +204,12 @@ public:
}
}
- virtual void parse_tags(const char * p,
- scope_t&,
- bool overwrite_existing = true) {
+ virtual void parse_tags(const char * p, scope_t&,
+ bool overwrite_existing = true) {
if (! deferred_notes)
deferred_notes = deferred_notes_list();
deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing));
+ deferred_notes->back().apply_to_post = active_post;
}
virtual void extend_xact(xact_base_t& xact, parse_context_t& context);